Angular 2 - binding input form data to SVG text element - svg

I'm looking to bind a form input string to an svg text element in an Angular 2 template. I'm not sure how to do this and would appreciate any help.
<svg class="box">
<rect class="largeFrame" x="40" y="10" rx="10" ry="10"/>
<text class="name" x ="400" y="40">Name</text>
<text class="personName" x="400" y="55" [textContent]="personName.value"></text>
</svg>
<form #f="ngForm">
<div class="input">
<label for="name">Enter Your Name</label>
<input type="text" [(ngModel)]="personName.value" id="name">
</div>
[textContent] is not working... can anyone clue me in on the best syntax? Or suggest a method for binding SVG and inputs in Angular 2.
Thanks!

Looks like I needed to use [()] to bind the data.
I also needed to give personName.value an initial value.
<text class="personName" x="400" y="55" [(textContent)]="personName.value"></text>

use interpolation binding, which will bind to the textContent property for you
use the safe navigation operator to guard against null and undefined, then you won't need to specify an initial value
<text class="personName" x="400" y="55">{{personName?.value}}</text>

Related

How to turn a <circle> (that's inside an svg) into a checkbox?

PART 1 -
So I have a bunch of <circle>'s inside a SVG, and I want those circles to be checkboxes. And after that I want to:
PART 2 -
When circle 1 (which is now a checkbox) is clicked, then it is checked. But all the other circles now get unchecked.
This is what I've already tried:
PART 1 - Turning the SVG into a checkbox:
<circle opacity="0.5" cx="842" cy="451.814" r="25.582" class="svg_spot" id="1" fill="#FFB60C" >
<animate attributeName="r" values="25.582; 33; 25.582" keyTimes="0; 0.5; 1" begin="0s" dur="1s" repeatCount="indefinite" calcMode="linear" />
<input type="checkbox" id="spot1" name="spot" class="common_selector spot_id" value="spot1">
</circle>
PART 2 -
$('input[name=spot]').click (function (){
$(this).attr('checked', true);
$('input[name=spot]').not(this).attr('checked', false);
});
Thanks for your time guys. Would appreciate any help!
It is possible to get this working without any Javascript.
How this works:
Put the SVG in the label of the input. Clicking on the SVG (ie the label) will thus activate the field.
Make the actual <input> field invisible, so that all you see is the label.
Style the SVG based on whether the field is selected, by using the :checked pseudo-selector.
// just here to prove that the form value is changing
// prints value of spot field when inputs change
document.querySelectorAll("#spot1, #spot2, #spot3")
.forEach((elem) => elem.addEventListener("change", (evt) => console.log(document.myform.spot.value)));
.common_selector {
position: absolute;
opacity: 0;
}
.common_selector + label svg {
width: 50px;
height: 50px;
fill: red;
}
.common_selector:checked + label svg {
fill: green;
}
<form name="myform">
<input type="radio" id="spot1" name="spot" class="common_selector spot_id" value="spot1">
<label for="spot1">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50"/>
</svg>
</label>
<input type="radio" id="spot2" name="spot" class="common_selector spot_id" value="spot2">
<label for="spot2">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50"/>
</svg>
</label>
<input type="radio" id="spot3" name="spot" class="common_selector spot_id" value="spot3">
<label for="spot3">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50"/>
</svg>
</label>
</form>
<input> is not a valid SVG element - it is a HTML element, so this won't work. You can either:
wrap an input element inside a <foreignObject> element and do it that way, or
you could use positioning to place the input element over the circle. But fair warning - form elements haven't always played well when they're positioned over other types of elements. Or
You can manually draw SVG that looks like an input element and use JavaScript to make it behave like one. or
Since you just need a circle, why not wrap the input element in a Div with an appropriate border radius and make a circle that way.
PART 1:
Use <foreignObect> to display any HTML element inside an SVG:
<foreignObject x="20" y="20" width="100" height="100">
<input type="checkbox" id="spot1" name="spot" class="common_selector spot_id"
value="spot1">
</foreignObject>
Then you can use css to hide default styling of this input field and position your circle over it. You can read about it here: https://www.w3schools.com/howto/howto_css_custom_checkbox.asp
PART 2:
Use Radio Buttons instead of Checkboxes. Checkboxes allow more than one selection. Radio buttons are what you need here. Read about it here: https://www.w3schools.com/tags/att_input_type_radio.asp
I hope I understand your question. You can not change an svg element into an input however you can try to mimic one.
// selects all the circles with a class of radio
let inputs = document.querySelectorAll(".radio")
// for every circle
inputs.forEach(i =>{
//when the circle is clicked
i.addEventListener("click", ()=>{
// remove the class checked from all the circles but the clicked one
inputs.forEach(j =>{if(i!==j)j.classList.remove("checked") })
// toggle the class checked on the clicked one
i.classList.toggle("checked")
})
})
svg{border:1px solid}
.checked{fill:red}
<svg id="theSVG" viewBox="800 410 300 85" width="300">
<circle class="radio" opacity="0.5" cx="842" cy="451.814" r="25.582" fill="#FFB60C" stroke="#FFB60C" stroke-width="10" />
<circle class="radio" opacity="0.5" cx="950" cy="451.814" r="25.582" fill="#FFB60C" stroke="#FFB60C" stroke-width="10" />
<circle class="radio" opacity="0.5" cx="1050" cy="451.814" r="25.582" fill="#FFB60C" stroke="#FFB60C" stroke-width="10" />
</svg>
It sounds like you want to use an SVG as a type of "selector" like a color selector or, in my case a planet selector. Here I have a SVG representing the solar system where the user clicks on a planet to select it. Since it sounds like you only want to select a single item then a radio button is the better option, however the method below should work with either.
As others have suggested the foreignObject element can be used. I had issues getting it work without specifically declaring a prefixed namespaces for the svg and saving the file with a .xhtml extension.
Of course there are plenty of ways to do something similar with javaScript, however a big advantage of using an underlying radio button is that it takes care of all the logic and makes validation easier.
I used an empty div over the circle that is acting as a label for the radio button. Since clicking on the label activates the radio button the circle now functions as a radio button!
The SVG does not show up in the snippet, so the div's are shaded for visibility. The actual radio buttons can be hidden if desired.
div{
border: red solid 1px;
background-color: black;
opacity: 20%;
}
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:svg="http://www.w3.org/2000/svg">
<p>Select a planet</p>
<form name="planetData">
<input id="venus" name="planet" type="radio" value="venus" />
<label for="venus">Venus</label>
<input id="earth" name="planet" type="radio" value="earth" />
<label for="venus">Earth</label>
<input id="mars" name="planet" type="radio" value="mars" />
<label for="venus">Mars</label>
</form>
<svg:svg height="100" width="100" viewbox="0 0 100 100">
<svg:circle cx="0" cy="50" r="10" fill="yellow"/>
<svg:circle cx="30" cy="75" r="10" fill="green"/>
<svg:circle cx="60" cy="30" r="10" fill="blue"/>
<svg:circle cx="90" cy="60" r="10" fill="red"/>
<svg:foreignObject x="20px" y="65px" height="20px" width="20px">
<label for="venus">
<div style="height: 30px; width: 30px;">
</div>
</label>
</svg:foreignObject>
<svg:foreignObject x="50" y="20" height="20" width="20">
<label for="earth">
<div style="height:20px; width: 20px;"></div>
</label>
</svg:foreignObject>
<svg:foreignObject x="80" y="50" height="20" width="20">
<label for="mars">
<div style="height:40px; width: 40px;"></div>
</label>
</svg:foreignObject>
</svg:svg>
</html>

Mobile's virtual keyboard destroy design

First, sorry for my bad english. I'll try to explain the best i can.
So, i have problem with mobile's keyboards. When i try to search something on my search-bar, the virtual keyboard appears and destroy my design. Here you can see my problem :
Normal
Bug
On the first screen, you can see " Barre de recherche " means " SearchBar" and " Contenu du site " means " Website content "
On the second screen, as you can see, the content hide my searchbar, only when keyboard is open...
I don't really know how to solve this.
<div class="row bgBlanc clrNoir hidden-lg hidden-md search-container hidden-search-container">
<div class="col-24 flex flexRow flexAlignCenter-sm flexJustifySpace-sm">
<form class="row search-bar searchFormMob fullW" name="quickFind" action='advanced_search_result.php' method="get" autocomplete="off">
<input class="tup col-offset-1 col-18 col-sm-20 keywords fs40" type="search" name="keywords" placeholder="<?php echo TEXT_SEARCH_PEF_KEYWORDS; ?>" style="min-height:10rem; width:100%;border-style:none;opacity: 0.4;">
<!-- <button class="submitBtn-search col-2 ffwi pt15 fs60" type="submit"></button> -->
<button class="submitBtn-search col-2 col-sm-2 ffwi pt15 fs60" type="submit">
<svg width="44px" height="44px" viewBox="0 0 22 22" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Icones-nav-2" transform="translate(-805.000000, -25.000000)" fill-rule="nonzero" fill="#000000">
<g id="ic_search_black_24px-copy" transform="translate(805.000000, 25.000000)">
<path d="XXXXXXXXXXXXX" id="Shape"></path>
</g>
</g>
</g>
</svg>
</button>
</form>
</div>
</div>

editable field with in svg

I am trying to provide edit option for label inside svg element using text box with in foreign object.
If text written in the text box is larger than width of the text box,text given overflows outer the text box field.
<svg>
<foreignObject x="30" y="15" width="200" height="40" class="editable" style="cursor: default;">
<div xmlns="http://www.w3.org/1999/xhtml">
<input type="text" class="editable-div" style="width:120px;">
</div>
</foreignObject>
</svg>
sample

Is there a way to apply clip-path to a div without CSS?

I'm trying to clip a div with svg clip-path, because css rule clip-path: url(#foo) have poor support. So I thought may be i can attach svg clip-path to a div with html attribute clip-path="#foo". Like in the example here - link. But it doesn't work.
This is my svg element and div.
<svg class="svg-graphic">
<defs>
<clippath id="hexagonal-mask">
<polygon points="130,0 0,160 0,485 270,645 560,485 560,160" />
</clippath>
</defs>
<div class="element" clip-path="#hexagonal-mask">
<div class="content">
<h2>How it works</h2>
<p>
Download<span class="icon"></span>
</p>
<p>
Watch video and Play games<span class="icon"></span>
</p>
<p>
Choose your <span class="icon"></span>
</p>
</div>
</div>
</svg>
When i open an element inspector, i see that my div somehow appears outside of the svg element.
http://prntscr.com/ay7kxt
And clip-path doesn't apply.
Am i doing something wrong or is this aproach just doesnt work with divs, only with shapes like , ?

How to create a flow layout in SVG using D3.js?

Say that I have an SVG container with a couple of shapes:
<svg>
<g class="container">
<rect id="first" x="0" y="0" width="100" height="100" fill="red" />
<rect id="second" x="100" y="0" width="100" height="100" fill="green" />
<rect id="third" x="200" y="0" width="100" height="100" fill="blue" />
</g>
</svg>
Using D3 I will manipulate the width of these shapes, for example in transitions. How do I make sure that the rects will always stay in this order, without any space between them? That is, if I modify the width of first, x of second and third will update instantaneously.
Option A: Create a treemap and set the sticky option to true: .sticky(true). The treemap layout provides you with x, y, width, and heigth values that you can use to manipulate your DOM/SVG. The sticky option takes care of smooth transitions.
Option B: Use plain html elements, such as div instead of svg:rect elements. If you really just manipulate the width, that should be the more reasonable option:
<style>
#container div{ float: left; }
</style>
<div id="container">
<div id="first" style="width:100px; height:100px; background-color:red;" ></div>
<div id="second" style="width:100px; height:100px; background-color:green;" ></div>
<div id="third" style="width:100px; height:100px; background-color:blue;" ></div>
</div>
Using plain html you can manipulate the width and the browser's layout/CSS engine handles the floating. D3 is not restricted to SVG, it can also handle normal html elements (the treemap example also uses div elements).
Btw: In d3 you should not manipulate the DOM directly. Always think of the underlying data and make the updates data driven, i.e., when using a treemap you would the set the item.value of a data item in your source data, e.g.:
data = [ {value: 100}, {value:200}, {value:100} ]
//...
updateData() //changes some values in your data
drawChart() //draws the whole chart based on the data, e.g., computes the x, y,
//width, height from the `item.value` (e.g., via d3.layout.treemap)
//and manipulates/animates the DOM via d3
I did a variation of a tree layout to make it into a flow layout. You can see the demo here and view the Gist here.

Resources