Polymer update datalist - javascript

I'm making a polymer element with some requirements. I've a list (ul) and two input fields. I need that one of the inputs update my list (there's no problem here). The other input should be able to define some default values for the first input, here I'm using a datalist.
I wrapped all in a polymer element. That's when I started having problems. I'm trying to update the datalist inside the polymer element, the datalist is updated but the input doesn't show the new hints.
Also, I've noticed that if I have more than one of these elements all the input binds with the datalist of the first element. I don't understand this behaviour. Each element must bind to the list inside her scope.
One more thing that I need to figure out is how can I delete some of default values added.
Here is my code:
<dom-module id="test-input" attributes="edit list type">
<template>
<!-- Here I have the first input that creates a list and I need to be binded with a datalist-->
<div id="container">
<div id="display" on-click="_openEdit">
<div id="textDescription" class="textDescription">{{description}}</div>
<ul id="list" class="list"></ul>
<form id="addContainer" class="addContainer" action="#" method="post">
<label for="newitem">{{label}}</label>
<!--<input type="text" name="newitem" id="newitem" placeholder="{{placeholder}}" required>-->
<input list="defaultValues" name="newitem" id="newitem" placeholder="{{placeholder}}" required>
<!-- I've added this options so we can see that it's working but when the datalist is updated it doesn't show-->
<datalist id="defaultValues">
<option value="option added on html"></option>
<option value="option added on html 2"></option>
</datalist>
<input type="submit" id="addToList" value={{buttonText}}>
</form>
</div>
<!-- Here I have the second input that updates the datalist default values-->
<div id="edit">
<div id="extendedList">
<div>Enter the default values</div>
<form id="extendedAddContainer" class="addContainer" action="#" method="post">
<label for="extendedNewitem">{{label}}</label>
<input type="text" name="extendedNewitem" id="extendedNewitem"
placeholder="enter default values" required>
<input type="submit" id="extendedAddToList" value={{buttonText}}>
</form>
</div>
</div>
</div>
</template>
<script>
defaultValueCounter = 0;
function isEmpty(str) {
return !str.replace(/^\s+/g, '').length; // boolean (`true` if field is empty)
}
Polymer({
is: "test-input",
properties: {
buttonText: {
type: String,
value: "Add"
}
},
ready: function () {
/* Start: add input to list */
var list = this.$.list,
field = this.$.newitem,
form = this.$.addContainer;
form.addEventListener('submit', function (ev) {
if (field.validity.valid && !isEmpty(field.value)) {
list.innerHTML += '<li class="style-scope list-input">' + field.value + '</li>';
field.value = '';
field.focus();
ev.preventDefault();
}
}, false);
list.addEventListener('click', function (ev) {
var t = ev.target;
if (t.tagName === 'LI') {
t.parentNode.removeChild(t);
}
ev.preventDefault();
}, false);
/* End: add input to list */
/* Start: Add default values */
var extendedList = this.$.defaultValues,
extendedField = this.$.extendedNewitem,
extendedForm = this.$.extendedAddContainer,
container = this.$.extendedList,
defaultList = this.$.defaultList;
extendedForm.addEventListener('submit', function (ev) {
ev.preventDefault();
/* Here I add things to the datalist */
if (extendedField.validity.valid && !isEmpty(extendedField.value)) {
aux = document.createElement("option");
aux.value = extendedField.value;
extendedList.appendChild(aux);
extendedField.value = '';
extendedField.focus();
}
}, false);
}
/* End: Add default values */
});
</script>
</dom-module>
Thanks in advance!

Related

Reusable function to display input text in div

I have a function that displays text in a div as its typed into an input. Right now it simply checks for each ID go get the value and display the text.
I want to make this function reusable so that I can match different inputs with different divs without writing a unique function for each case.
Here is an example that works using a single input and div:
<body>
<input type='text' name='name' id='inputBox'>
<div id='displayBox'></div>
<script type="text/javascript">
var displayText = document.getElementById('inputBox');
displayText.onkeyup = function() {
document.getElementById('displayBox').innerHTML = inputBox.value;
}
</script>
</body>
And I want to be able to repeat this for different sets of unique inputs & divs with a reusable function.
<body>
<!-- First set -->
<input type='text' name='name' id='inputBox'>
<div id='displayBox'></div>
<!-- Second set -->
<input type='text' name='name' id='inputBox'>
<div id='displayBox'></div>
<!-- etc... -->
<script type="text/javascript">
var displayText = document.getElementById('inputBox');
displayText.onkeyup = function() {
document.getElementById('displayBox').innerHTML = inputBox.value;
}
</script>
</body>
If you wrap each "set" in a container, and swap your ids for classes, you can can add listeners to each input to watch for changes, find the parent container, find the display box and update its text content.
// Get all of the inputs
const displayText = document.querySelectorAll('.inputBox');
// Attach listeners to all of them
displayText.forEach(input => {
input.addEventListener('keyup', handleChange, false);
});
function handleChange() {
// Find the closest div ancestor element (the container)
const parent = this.closest('div');
// Then locate the display box and update the text content
parent.querySelector('.displaybox').textContent = this.value;
}
.container { margin-bottom: 1em; }
.displaybox { margin-top: 0.2em; height: 1.3em; width: 300px; border: 1px solid black; }
<div class="container">
<input type="text" name="name" class="inputBox" placeholder="Type here">
<div class="displaybox"></div>
</div>
<div class="container">
<input type="text" name="age" class="inputBox" placeholder="Type here">
<div class="displaybox"></div>
</div>
<div class="container">
<input type="text" name="location" class="inputBox" placeholder="Type here">
<div class="displaybox"></div>
</div>
It seems you would need to get the ID of each input box and each output box?
function showTypedInput(inputID, outputID) {
var inputBox = document.getElementById(inputID);
var outputBox = document.getElementById(outputID);
inputBox.onkeyup = function(){
outputBox.innerHTML = inputBox.value;
};
}
Then you just reuse this?
showTypedInput("myInputBox", "myOutputBox");
You can create this functionality using following:
function listener(target){
return function(e){target.innerHTML = e.target.value};
}
function init(){
var elems = document.querySelectorAll("input[data-keyuptarget]");
for(var elem of elems){
var target = document.getElementById(elem.getAttribute('data-keyuptarget'));
if (target) elem.onkeyup = listener(target);
}
}
init();
In html just use
<input type='text' name='name' data-keyuptarget="displayBox1">
<div id='displayBox1'></div>
<input type='text' name='name' data-keyuptarget="displayBox2">
<div id='displayBox2'></div>
JS Bin : https://jsbin.com/piwiyapohe/edit?html,output

Creating efficient function instead of repetitive functions

I have a function which onclick displays the form.
Was wondering if there is any efficient way to code instead of creating 4 different functions for 4 different forms? Below example is for 4 forms but I am working with multiple forms.
<div class="navbar">
<div class="dropdown">
<button class="dropbtn" onclick="myFunction1()">Category 1
<i class="fa fa-caret-down"></i>
</button>
</div>
//Same for other 3 categories
<div id="form1" style = "display:none">
<form action="#" method="post" id="demoForm1" class="demoForm1" >
<fieldset>
<legend>Use CTRL to select multiple options</legend>
<p>
<select name="demoSel[]" id="demoSel" size="4" multiple>
<option value="ABC">ABC</option>
</select>
<input type="submit" value="Submit" />
<textarea name="display" id="display" placeholder="view select list value(s) onchange" cols="20" rows="4" readonly></textarea>
</p>
</fieldset>
</form>
</div>
//Same for other 3 forms
<script>
function myFunction1() {
document.getElementById("form1").style.display = '';
}
function myFunction2() {
document.getElementById("form2").style.display = '';
}
function myFunction3() {
document.getElementById("form3").style.display = '';
}
function myFunction4() {
document.getElementById("form4").style.display = '';
}
</script>
It's generally not a good idea to use inline event handlers.
Next, add a data-* attribute to each button and remove the onclick attribute like:
<button class="dropbtn" data-target="form1">...</button>
<button class="dropbtn" data-target="form2">...</button>
<button class="dropbtn" data-target="form3">...</button>
<button class="dropbtn" data-target="form4">...</button>
Then, you can use .addEventListener() on these buttons with class dropbtn and update respective form element display property like:
const btns = document.querySelectorAll(".dropbtn");
btns.forEach(function(btn) {
btn.addEventListener("click", function(cbox) {
document.getElementById(this.dataset.target).style.display = '';
});
});
Demo:
const btns = document.querySelectorAll(".dropbtn");
btns.forEach(function(btn) {
btn.addEventListener("click", function(cbox) {
document.getElementById(this.dataset.target).style.display = '';
});
});
<button class="dropbtn" data-target="form1">Form 1</button>
<button class="dropbtn" data-target="form2">Form 2</button>
<br><br>
<form id="form1" style="display:none">Form 1 Content Here</form>
<form id="form2" style="display:none">Form 2 Content Here</form>
Don't use on-event attributes:
<button onclick='eventHandler()'></button>
Use event listeners or on-event properties:
const btn = document.querySelector('button');
btn.addEventListener('click', eventHandler);
// OR
btn.onclick = eventHandler;
If you have multiple targets to click -- register the click event to a parent tag that all target tags share.
document.querySelector('main').onclick = toggleForm;
Instead of using .style on each <form> toggle classes
// CSS
.off { display: none }
// JavaScript
forms[idx].classList.toggle('off');
Demo
Note: Details are commented in demo
/*
- Reference the parent tag (<main>)
- Register <main> to the click event
- Event handler function toggleForm() is called on click
*/
document.querySelector('main').onclick = toggleForm;
// Event handler always passes Event Object (event)
function toggleForm(event) {
// Collect all <form>s into a HTML Collection
const forms = document.forms;
// Collect all <button> into a NodeList
const buttons = document.querySelectorAll('button');
// Reference the tag the user clicked (<button>)
const clicked = event.target;
// if a <button> was clicked...
if (clicked.matches('button')) {
// ...toggle the <button>'s .on and .off classes
clicked.classList.toggle('off');
clicked.classList.toggle('on');
/*
- Convert buttons NodeList into a rel Array
- Iterate through the buttons array and return
the index of the clicked <button>
*/
let idx = [...buttons].flatMap((button, index) => clicked === button ? [index] : []);
/*
- Toggle the .off class on the <form> located at the
index that was obtained from the previous statement
*/
forms[idx].classList.toggle('off');
}
}
button {
display: inline-block;
width: 11ch
}
button.off::before {
content: 'Show '
}
button.on::before {
content: 'Hide '
}
form.off {
display: none
}
<main>
<button class='off' type='button'>A</button>
<button class='off' type='button'>B</button>
<button class='off' type='button'>C</button>
<button class='off' type='button'>D</button>
<hr>
<form id='A' class='off'>
<fieldset>
<legend>Form A</legend>
</fieldset>
</form>
<form id='B' class='off'>
<fieldset>
<legend>Form B</legend>
</fieldset>
</form>
<form id='C' class='off'>
<fieldset>
<legend>Form C</legend>
</fieldset>
</form>
<form id='D' class='off'>
<fieldset>
<legend>Form D</legend>
</fieldset>
</form>
</main>

how to display the array of values for the single field using jquery?

Here I need to get all the values entered in the input field. But it echoes only the first value.
ie. When I press the + and give some values, I need to get that value too.
$(document).ready(function() {
$(document).on("click", ".add", function() {
var clone = '<div class="add1"><input type="text" name="selprice" /><input type = "submit" value = "+" class = "add" ><input type = "submit" value = "-" class = "remove" ></div>';
$(this).closest('.add1').after(clone);
});
$(document).on("click", ".remove", function() {
$(this).parent(".add1").remove();
});
});
$('#package').change(function() {
var arr = [];
$("input.packageclass").each(function() {
arr.push($(this).val());
alert(arr);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<div class="add1">
<h6>Sales Package </h6>
<div>
<input type="text" name="package" placeholder="Ex:34" id="package" class="packageclass">
<input type="submit" value="+" class="add"></div>
</div>
$('.package').change(function() {
You are using an ID in your input type="text". IDs are only used once. If you want to add the listener to all of your textfields use classes.
In addition to that the .change(function() is only once called, when the dom is ready. That will be a problem too. So the change listener is not added to the generated textfields. Maybe you use something like...
$('.package').on('change', 'input', function() {
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<div class="add1">
<h6>Sales Package </h6>
<div>
<input type="text" name="package" placeholder="Ex:34" id="package" class="packageclass">
<input type="submit" value="+" class="add">
</div>
</div>
<script type="text/javascript">
var addInput = function(e) {
var arr = [];
$("input.packageclass").each(function() {
arr.push($(this).val());
});
alert(arr);
};
$(document).ready(function() {
$(document).on("click", ".add", function() {
var clone = '<div class="add1"><input class="packageclass" type="text" name="selprice" /><input type = "submit" value = "+" class = "add" ><input type = "submit" value = "-" class = "remove" ></div>';
$(this).closest('.add1').after(clone);
$('.packageclass').unbind().bind('change', addInput);
});
$(document).on("click", ".remove", function() {
$(this).parent(".add1").remove();
});
});
$('.packageclass').unbind().bind('change', addInput);
</script>
Just using loop you can get the particular value from loop.
for (var i = arr.length - 1; i >= 0; i--) {
arr[i];
//work with arr[]
}
I have used event delegation to capture the events and take appropriate action.
In this, you can add a event listener to your parent element i.e., click to the .body in my case. When I click on the .add button, the event propagates and .body click handler gets invoked. By checking for event.target we can find out the origin of event and add or remove the divs.
Similary we can listen for the change event of the input boxes and take appropriate actions.
$('#body').click(function(e) {
if(e.target.className === 'add') {
$('#body').append(`
<div class="add1">
<input type="text" name="package" placeholder="Ex:34" id="package" class="packageclass">
<button class="add">+</button>
<button class="remove">-</button>
</div>
`);
}
if(e.target.className === 'remove') {
$(e.target).parent().empty();
}
});
$('#body').change(function(e) {
console.log(e.target.value);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<div id="body">
<h6>Sales Package </h6>
<div class="add1">
<input type="text" name="package" placeholder="Ex:34" id="package" class="packageclass">
<button class="add">+</button>
</div>
</div>
Just add class="packageclass" to the input when creating your clone variable.
https://jsfiddle.net/289xvmu7/
var clone = '<div class="add1"><input type="text" name="selprice" class="packageclass"/> <input type = "submit" value = "+" class = "add" ><input type = "submit" value = "-" class = "remove" ></div>';

How to add new field when button is clicked multiple times by using angularjs

I'm trying to add fields multiple times up on clicking the buttons , they r nested each other..what i am trying to do as shown in the picture
html code :
<body ng-controller="myController">
<form name="{{form.name}}" ng-repeat="form in forms">
<div ng-repeat="cont in form.contacts">
<select>
<option>--select machine--</option>
<option>data1</option>
<option>data2</option>
</select>
<div class="data">
<button class="add_s" ng-click = "add()">Add more</button>
</div>
</div>
<button ng-click="addFields(form)">Add</button>
</body>
angularjs code:
var app = angular.module('myApp', []);
app.controller('myController', function($scope) {
$scope.forms = [{ }];
$scope.addFields = function (form) {
if(typeof form.contacts === 'undefined') {
form.contacts = [];
}
form.contacts.push('');
}
$scope.add = function(){
var max_fields = 10; //maximum input boxes allowed
var wrapper = $(".data"); //Fields wrapper
var add_button = $(".add_s"); //Add button ID
var y=1;
var x = 1;//initlal text box count
$(add_button).click(function(e){ //on add input button click
e.preventDefault();
if(x < max_fields){ //max input box allowed
x++; //text box increment
$(wrapper).prepend('<div><select><option>--select--</option><option>data1.1</option><option>data1.2</option></select><input type="text"><input type="text"><input type="text"><input type="text"><i class="glyphicon glyphicon-trash"></i></div>');
}
});
$(wrapper).on("click",".remove_field", function(e){ //user click on remove text
e.preventDefault(); $(this).parent('div').remove(); x--;
})
}
});
error:
if i click on add button i m getting only one time the above div tag is repeating, but i want that should repeat for many times,
and when i am clicking add more button data with in the 2nd div tag should repeat. but its happening..:( ..
please help.. thank u in advance.
Use this code. Here is the plunker.
$scope.forms = [];
$scope.addForms = function () {
$scope.forms.push({contacts : []});
};
$scope.addContact = function (form) {
form.contacts.push({});
};
View
<form name="{{form.name}}" ng-repeat="form in forms" style="border:1px solid red">
New Form
<div ng-repeat="cont in form.contacts">
<select>
<option>--select machine--</option>
<option>data1</option>
<option>data2</option>
</select>
</div>
<div class="data"> <!-- Move this out of contacts -->
<button class="add_s" ng-click = "addContact(form)">Add more</button>
</div>
</form><!--You have unclosed form tag -->
<button ng-click="addForms()">Add</button>
Here's my take on your problem and how I'd do it :
<div ng-controller="MyCtrl">
<button ng-click="addBloc()">
Add bloc
</button>
<div ng-repeat="bloc in blocs">
<div>
<button ng-click="addNewField($index)">
Add field
</button>
</div>
<div ng-repeat="field in bloc.fields">
<select ng-model="field.gender">
<option value="Male">Male</option>
<option value="Female">Female</option>
</select>
<input type="text" ng-model="field.FirstName">
<input type="text" ng-model="field.LastName"> {{field.gender}} {{field.FirstName}} {{field.LastName}}
</div>
</div>
</div>
And controller :
$scope.blocs = [];
$scope.fields = [];
$scope.addNewField = function(index) {
alert(index);
$scope.blocs[index].fields.push({
gender: "",
FirstName: "",
LastName: ""
});
};
$scope.addBloc = function() {
$scope.blocs.push({
fields: []
});
Working JSFiddle : JSFIDDLE
Basic idea is to put your fields inside a ng-repeat and bind it to an empty array. When you click the button it adds an empty element inside the array which will be binded to a new element of the ng-repeat automatically.
Happy coding!
EDIT : I added another level of repeated fields as previously asked since I forgot to read the entire request.

Calculating TOTAL in added input fields

I have this small problem. I have been checking out the net to find an answer, but it is mostly for input fields which aren't generated / added.
<script>
$(document).ready(function(){
/* --- ADD FIELD --- */
$('.TotalMultiField').each(function() {
var $wrapper = $('.multiFields', this);
$(".addField", $(this)).click(function(e) {
$('.multiField:first-child', $wrapper).clone(true).appendTo($wrapper).find('input').val('').focus();
});
/* --- REMOVE FIELD --- */
$('.multiField .removeField', $wrapper).click(function() {
if ($('.multiField', $wrapper).length > 1)
$(this).parent('.multiField').remove();
});
});
</script>
Above is the Jquery script to add and remove fields. And below is the HTML code. As you see, in the "insert a number" field, the total should appear in the span id="added".
<form role="form" action=""" method="">
<div class="TotalMultiField">
<div class="multiFields">
<div class="multiField">
<input type="date">
<input type="number" class="number" placeholder="Insert a number">
<button type="button" class="removeField">Remove</button>
</div>
</div>
<button type="button" class="addField">Add field</button>
</div>
Total:<span id="added"></span>
</form>
Just noticed you updated that you do indeed want a sum, here is a fiddle with the total calculated on blur and when you remove a row, bind additional events as required (some tidy up is required, but this should get you started):
http://jsfiddle.net/1ggaco1d/4/
The below code does the totaling:
function total() {
var total = 0;
$(".number").each(function (idx, el) {
var value = $(el).val();
if (value !== "") {
total = total + parseFloat($(el).val());
}
});
$("#added").text(total);
}
Please check the below fiddle
https://jsfiddle.net/srsu4rne/
You should be able to just count the classes in the div
var multiField = $('.multiField').length
$('#added').html(multiField);

Categories