Show/Hide Form elements based on json response - javascript

I make an api call and get a json Response that looks like this:
{"Field":"Amount","FieldName":"Amount","FieldType":"Numeric","MaximumLength":128,"MinimumLength":0,"Options":"Mandatory"}
The api call Returns between 5-10 of These objects.
In my html i have the fields inside a form, and they look as follow
<div class="form-group">
<label class="col-md-4 control-label" for="Amount">Amount</label>
<div class="col-md-4">
<input name="amount" class="form-control input-md" id="amount" type="text" placeholder="placeholder">
</div>
</div>
Now, depending on the json repsonse, i Need to show/hide different fields. Additionally, i Need to pass the min and max length, aswell as whether it's required or not ("Options":"Mandatory", in this example) Attributes to html, so the front end is already validate (obviously, further validation will be done server-side).
What's the best way to do this?
Edit:
I hide the Form Elements by using class="hidden". Also, every form element has a class that is identical to the id of the form element.
What i've tried so far is the following:
var ids = [];
for(var item in data) {
ids.push(data[item]['Field']);
$("." + ids[ids.length]).removeClass("hidden");
}
So in the above example, what i was hoping would happen in the first Iteration is that
$("." + ids[ids.length]).removeClass("hidden");
changes to
$(.Amount).removeClass("hidden");
and therefor show the form element.
Thanks

The problem is in this line
$("." + ids[ids.length]).removeClass("hidden");
When you push one element to ids array the length becomes 1 and your element is in 0th position but you are accessing element in first position.
To correct it change the line to
$("." + ids[ids.length - 1]).removeClass("hidden");

I ended up solving it like this:
var ids = [];
var minlength = [];
var maxlength = [];
var required = [];
for(var item in data) {
//push values into array
ids.push(data[item]['Field']);
maxlength.push(data[ids.length-1]['MaximumLength']);
minlength.push(data[ids.length-1]['MinimumLength']);
required.push(data[ids.length-1]['Options']);
//removes the class hidden and therefor shows the Element
$("." + ids[ids.length-1]).removeClass("hidden");
//adds the Attribute minlength to the html element
$("#" + ids[ids.length-1]).attr('minlength', minlength[minlength.length-1]);
//adds the Attribute maxlengthto the html element
$("#" + ids[ids.length-1]).attr('maxlength', maxlength[maxlength.length-1]);
//if the value in the Array is "Mandatory", adds the Attribute "required" to the html element
if (required[ids.length-1] == "Mandatory") {
$("#" + ids[ids.length-1]).attr('required', "required");
}

Related

Sending Checkbox and Text Input Values to URL String with Javascript

I have a list of products, each individual product has a checkbox value with the products id e.g. "321". When the products checkbox is checked (can be more than 1 selected) i require the value to be collected. Each product will also have a input text field for defining the Qty e.g "23" and i also require this Qty value to be collected. The Qty text input should only be collected if the checkbox is checked and the qty text value is greater than 1. The plan is to collect all these objects, put them in to a loop and finally turn them in to a string where i can then display the results.
So far i have managed to collect the checkbox values and put these into a string but i'm not sure how to collect the additional text Qty input values without breaking it. My understanding is that document.getElementsByTagName('input') is capable of collecting both input types as its basically looking for input tags, so i just need to work out how to collect and loop through both the checkboxes and the text inputs.
It was suggested that i use 2 if statements to accomplish this but i'm new to learning javascript so i'm not entirely sure how to go about it. I did try adding the if statement directly below the first (like you would in php) but this just seemed to break it completely so i assume that is wrong.
Here is my working code so far that collects the checkbox values and puts them in a string. If you select the checkbox and press the button the values are returned as a string. Please note nothing is currently appended to qty= because i dont know how to collect and loop the text input (this is what i need help with).
How can i collect the additional qty input value and append this number to qty=
// function will loop through all input tags and create
// url string from checked checkboxes
function checkbox_test() {
var counter = 0, // counter for checked checkboxes
i = 0, // loop variable
url = '/urlcheckout/add?product=', // final url string
// get a collection of objects with the specified 'input' TAGNAME
input_obj = document.getElementsByTagName('input');
// loop through all collected objects
for (i = 0; i < input_obj.length; i++) {
// if input object is checkbox and checkbox is checked then ...
if (input_obj[i].type === 'checkbox' && input_obj[i].checked) {
// ... increase counter and concatenate checkbox value to the url string
counter++;
url = url + input_obj[i].value + '&qty=' + '|';
}
}
// display url string or message if there is no checked checkboxes
if (counter > 0) {
// remove first "&" from the generated url string
url = url.substr(1);
// display final url string
alert(url);
}
else {
alert('There is no checked checkbox');
}
}
<ul>
<li>
<form>
<input type="checkbox" id="checked-product" name="checked-product" value="311">Add To Cart
<div class="quantity">
<input type="text" name="qty" id="qty" maxlength="12" value="1" class="input-text qty"/>
</div>
</form>
</li>
<li>
<form>
<input type="checkbox" id="checked-product" name="checked-product" value="321">Add To Cart
<div class="quantity">
<input type="text" name="qty" id="qty" maxlength="12" value="10" class="input-text qty"/>
</div>
</form>
</li>
<li>
<form>
<input type="checkbox" id="checked-product" name="checked-product" value="98">Add To Cart
<div class="quantity">
<input type="text" name="qty" id="qty" maxlength="12" value="5" class="input-text qty"/>
</div>
</form>
</li>
</ul>
<button type="button" onclick="javascript:checkbox_test()">Add selected to cart</button>
My answer has two parts: Part 1 is a fairly direct answer to your question, and Part 2 is a recommendation for a better way to do this that's maybe more robust and reliable.
Part 1 - Fairly Direct Answer
Instead of a second if to check for the text inputs, you can use a switch, like so:
var boxWasChecked = false;
// loop through all collected objects
for (i = 0; i < input_obj.length; i++) {
// if input object is checkbox and checkbox is checked then ...
switch(input_obj[i].type) {
case 'checkbox':
if (input_obj[i].checked) {
// ... increase counter and concatenate checkbox value to the url string
counter++;
boxWasChecked = true;
url = url + input_obj[i].value + ',qty=';
} else {
boxWasChecked = false;
}
break;
case 'text':
if (boxWasChecked) {
url = url + input_obj[i].value + '|';
boxWasChecked = false;
}
break;
}
}
Here's a fiddle showing it working that way.
Note that I added variable boxWasChecked so you know whether a Qty textbox's corresponding checkbox has been checked.
Also, I wasn't sure exactly how you wanted the final query string formatted, so I set it up as one parameter named product whose value is a pipe- and comma-separated string that you can parse to extract the values. So the url will look like this:
urlcheckout/add?product=321,qty=10|98,qty=5
That seemed better than having a bunch of parameters with the same names, although you can tweak the string building code as you see fit, obviously.
Part 2 - Recommendation for Better Way
All of that isn't a great way to do this, though, as it's highly dependent on the element positions in the DOM, so adding elements or moving them around could break things. A more robust way would be to establish a definitive link between each checkbox and its corresponding Qty textbox--for example, adding an attribute like data-product-id to each Qty textbox and setting its value to the corresponding checkbox's value.
Here's a fiddle showing that more robust way.
You'll see in there that I used getElementsByName() rather than getElementsByTagName(), using the name attributes that you had already included on the inputs:
checkboxes = document.getElementsByName('checked-product'),
qtyBoxes = document.getElementsByName('qty'),
First, I gather the checkboxes and use an object to keep track of which ones have been checked:
var checkedBoxes = {};
// loop through the checkboxes and find the checked ones
for (i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].checked) {
counter++;
checkedBoxes[checkboxes[i].value] = 1; // update later w/ real qty
}
}
Then I gather the Qty textboxes and, using the value of each one's data-product-id attribute (which I had to add to the markup), determine if its checkbox is checked:
// now get the entered Qtys for each checked box
for (i = 0; i < qtyBoxes.length; i++) {
pid = qtyBoxes[i].getAttribute('data-product-id');
if (checkedBoxes.hasOwnProperty(pid)) {
checkedBoxes[pid] = qtyBoxes[i].value;
}
}
Finally, I build the url using the checkedBoxes object:
// now build our url
Object.keys(checkedBoxes).forEach(function(k) {
url += [
k,
',qty=',
checkedBoxes[k],
'|'
].join('');
});
(Note that this way does not preserve the order of the items, though, so if your query string needs to list the items in the order in which they're displayed on the page, you'll need to use an array rather than an object.)
There are lots of ways to achieve what you're trying to do. Your original way will work, but hopefully this alternative way gives you an idea of how you might be able to achieve it more cleanly and reliably.
Check the below simplified version.
document.querySelector("#submitOrder").addEventListener('click', function(){
var checkStatus = document.querySelectorAll('#basket li'),
urls = [];
Array.prototype.forEach.call(checkStatus, function(item){
var details = item.childNodes,
urlTemplate = '/urlcheckout/add?product=',
url = urlTemplate += details[0].value + '&qty=' + details[1].value;
urls.push(url)
});
console.log(urls);
})
ul{ margin:0; padding:0}
<ul id="basket">
<li class="products"><input type="checkbox" value = "311" name="item"><input type="text"></li>
<li><input type="checkbox" value = "312" name="item"><input type="text"></li>
<li><input type="checkbox" value = "313" name="item"><input type="text"></li>
</ul>
<button id="submitOrder">Submit</button>

JQuery replace elements within string

I have a string that contains HTML data, for example:
<div data-type="date" class="form-group">
<label class="control-label col-sm-5" for="dateInput">Date Input:</label>
<div class="col-sm-3">
<input type="text" name="dateInput[]" class="form-control date_picker" id="dateInput">
</div>
</div>
<div data-type="date" class="form-group">
<label class="control-label col-sm-5" for="dateInput">Date Input:</label>
<div class="col-sm-3">
<input type="text" name="dateInput[]" class="form-control date_picker" id="dateInput">
</div>
</div>
I asked this question before presuming I was working with the DOM. However, this is just a string. The first thing I do is to remove the data attribute from the string:
$("#content").find(".form-group").each(function() {
var html = $(this).attr('class', 'form-group')[0].outerHTML.replace(/ data-(.+)="(.+)"/g, "");
});
Next, you can see that both input elements have an id of dateInput. What I need to do now is to change this value so that it is unique, using something like an incrementing number. So, the first input should have dateInput1 and the second dateInput2.
How can I achieve this? If possible, it would also be good to change the for value in the label to match the id.
Thanks
Update
If I do:
$("#content").find(".form-group").each(function() {
var html = $(this).attr('class', 'form-group')[0].outerHTML.replace(/ data-(.+)="(.+)"/g, "");
$(this).find('input[type="text"]').attr('id', this.id+$(this).index()+1);
dataArray.push(html);
});
It does not seem to update. I have an example fiddle here https://jsfiddle.net/mLgrfzaL/4/
You can use jQuery for evaluating the string in order to navigate it as DOM tree:
var string = "<div><span>Hello</span></div>";
var $parsedDom = $(string);
$parsedDom.children(); // => <span>Hello</span>
In your case, it is not clear to me where you want to apply it.
Answering your other questions:
$("#content").find(".form-group").each(function(index) {
//$(this).attr('class', 'form-group') has no effect:
// you have already found the nodes with this class
// removing the "data-" (why?)
$(this).removeAttr("data-type");
//modifing the id
var $thisInput = $(this).find('input');
var oldId = $thisInput.attr('id');
$thisInput.attr('id', oldId + index);
});
Eventually you can loop on element attribute for removing every attribute starting with "data-".
You should cycle each input having id equal to dateInput, then append to its old value index + 1 (since it is zero-based).
$("input[id='dateInput']").each(function(index, value) {
var old_id = $(value).attr("id");
var new_id = old_id + (index + 1);
$(value).attr("id", new_id);
});
Use .index() to get a unique number in the each loop:
$("#content").find(".form-group").each(function() {
// other code
$(this).find('input[type="text"])
.attr('id', this.id+$(this).index()+1);
});

How can I retrieve input values from elements added after page load?

I'm working with Zend Framework 1.12 and I've need to be able to dynamically add and delete fields from a sub-form, in this case we're associating hyperlinks to a parent "promotion".
I haven't found a way to accomplish dynamically adding and removing elements via Zend, and the rare tutorial I've found that claimed to do this are half a decade old and aren't working when I attempt them.
So what I am doing is storing the links I need to work with in a Zend Hidden input field and then dealing with the JSON data after I submit. Not very efficient, but it's the only thing I've gotten to work so far.
Below is the section of the code I'm working with:
Assume a form like:
<form action="/promos/edit/promo_id/15" method="POST" id="form_edit">
<!-- input is Zend_Form_Element_Hidden -->
<input type="hidden" id="link_array" value="{ contains the JSON string }"/>
<button id="add_link">Add Link</button>
</form>
The purpose is that every time the Add Link button is pressed, the form adds fields to allow the user to input new hyperlinks that will be associated with the specific items.
Here's the function:
// add links
$('#add_link').click(
function(e) {
e.preventDefault();
link = '<div class="p_link new_link">' +
'<div class="element_wrap">' +
'<label for="link_name" class="form_label optional">Text: </label>' +
'<input type="text" id="new_link_name" name="link_name"/>' +
'</div>' +
'<div class="element_wrap">' +
'<label for="link_http" class="form_label optional">http://</label>' +
'<input type="text" id="new_link_http" name="link_http"/>' +
'</div>' +
'<div class="element_wrap">' +
'<button class="submit delete_link">Delete</button>' +
'</div>' +
'</div>';
$('#add_link').prev().after(link);
}
);
Now, what I need to do is on submit, for every new_link class element, to take the links name and http reference and place it in a json object. Here's the code as I have it so far (I know I don't have both input fields represented at this point):
$('#submit').click(
function(e) {
e.preventDefault();
var link_array = [];
var new_links = document.getElementsByClassName('new_link');
$.each(new_links, function() {
console.log(this);
var n = $(this).children('#new_link_name').text();
console.log(n);
link_array.push({'link_name':n}); //'link_http':h
});
console.log(JSON.stringify(link_array));
}
);
My problem is that: var new_links = document.getElementsByClassName('new_link'); will collect all the newly added new_link elements, but it does not pull in any value that has been input into the text fields.
I need to know how I can apparently bind any input I make to the input field's value attribute, because right now anything I type into these new elements are tossed out and the field appears empty when it's anything but.
$('#submit').click(
function(e) {
e.preventDefault();
var link_array = [];
var new_links = $('.new_link');
$.each(new_links, function() {
console.log(this);
var n = $(this).find('input').val(); // you need input values! This line //is changed...
console.log(n);
link_array.push({'link_name':n}); //'link_http':h
});
console.log(JSON.stringify(link_array));
}
);
JSFIDDLE: http://jsfiddle.net/DZuLJ/
EDit: You can't have multiple IDS (make class for each input, and target class, if you want link names and http's)

How can I create dynamic controls and put their data into an object?

I created a div and a button. when the button clicked, there will be a group of element(included 1 select box and 2 text inputs) inserted into the div. User can add as many group as they can, when they finished type in data of all the group they added, he can hit save button, which will take the value from each group one by one into the JSON object array. But I am stuck in the part how to get the value from each group, so please help, thank you.
The code for the div and the add group button function -- AddExtra() are listed below:
<div id="roomextra">
</div>
function AddExtra() {
$('#roomextra').append('<div class=extra>' +
'<select id="isInset">' +
'<option value="Inset">Inset</option>' +
'<option value="Offset">OffSet</option>' +
'</select>' +
'Length(m): <input type="text" id="insetLength">' +
'Width(m): <input type="text" id="insetWidth">' +
'Height(m): <input type="text" id="insetHeight">' +
'</div>');
}
function GetInsetOffSetArray (callBack) {
var roomIFSDetail = [{
"IsInset": '' ,
"Length": '' ,
"Width": '' ,
"Height": ''
}];
//should get all the value from each group element and write into the array.
callBack(roomIFSDetail);
}
This should just about do it. However, if you're dynamically creating these groups, you'll need to use something other than id. You may want to add a class to them or a data-* attribute. I used a class, in this case. Add those classes to your controls so we know which is which.
var roomIFSDetail = [];
var obj;
// grab all of the divs (groups) and look for my controls in them
$(.extra).each(function(){
// create object out of select and inputs values
// the 'this' in the selector is the context. It basically says to use the object
// from the .each loop to search in.
obj = {
IsInset: $('.isInset', this).find(':selected').val() ,
Length: $('.insetLength', this).val() ,
Width: $('.insetWidth', this).val() ,
Height: $('.insetHeight', this).val()
};
// add object to array of objects
roomIFSDetail.push(obj);
});
you'd better not to use id attribute to identity the select and input, name attribute instead. for example
$('#roomextra').append('<div class=extra>' +
'<select name="isInset">' +
'<option value="Inset">Inset</option>' +
'<option value="Offset">OffSet</option>' +
'</select>' +
'Length(m): <input type="text" name="insetLength">' +
'Width(m): <input type="text" name="insetWidth">' +
'Height(m): <input type="text" name="insetHeight">' +
'</div>');
}
and then, usr foreach to iterate
$(".extra").each(function() {
var $this = $(this);
var isInset = $this.find("select[name='isInset']").val();
var insetLength = $this.find("input[name='insetLength']").val();
// ... and go on
});
A common problem. A couple things:
You can't use IDs in the section you're going to be repeating, because IDs in the DOM are supposed to be unique.
I prefer to use markup where I'm writing a lot of it, and modify it in code rather than generate it there.
http://jsfiddle.net/b9chris/PZ8sf/
HTML:
<div id=form>
... non-repeating elements go here...
<div id=roomextra>
<div class=extra>
<select name=isInset>
<option>Inset</option>
<option>OffSet</option>
</select>
Length(m): <input id=insetLength>
Width(m): <input id=insetWidth>
Height(m): <input id=insetHeight>
</div>
</div>
</div>
JS:
(function() {
// Get the template
var container = $('#roomextra');
var T = $('div.extra', container);
$('#addGroup').click(function() {
container.append(T.clone());
});
$('#submit').click(function() {
var d = {};
// Fill d with data from the rest of the form
d.groups = $.map($('div.extra', container), function(tag) {
var g = {};
$.each(['isInset', 'insetLength', 'insetWidth', 'insetHeight'], function(i, name) {
g[name] = $('[name=' + name + ']', tag).val();
});
return g;
});
// Inspect the data to ensure it's what you wanted
debugger;
});
})();
So the template that keeps repeating is written in plain old HTML rather than a bunch of JS strings appended to each other. Using name attributes instead of ids keeps with the way these elements typically work without violating any DOM constraints.
You might notice I didn't quote my attributes, took the value attributes out of the options, and took the type attributes out of the inputs, to keep the code a bit DRYer. HTML5 specs don't require quoting your attributes, the option tag's value is whatever the text is if you don't specify a value attribute explicitly, and input tags default to type=text if none is specified, all of which adds up to a quicker read and slimmer HTML.
Use $(".extra").each(function() {
//Pull info out of ctrls here
});
That will iterate through all of your extra divs and allow you to add all values to an array.

JQuery: Selecting elements with unique class AND id

I have this html code:
<div class="category" id="154"> Category </div>
<div class="category2" id="156"> Category2 </div>
<div class="category3" id="157"> Category3 </div>
<div class="category4" id="158"> Category4 </div>
<input type="text" />
So in example if I write a id in text box, how to select div .category with this ID and get inner HTML text. With jQuery
so you only need to use the ID as this is a unique value (or should be)
var html = $("#154").html();
NOTE: If you do have duplicate ID values in use then it is important to note that JQuery will only select the first one.
if you want to do this when a textbox value is entered you could do this on the textbox change event...
$("input").change(function(){
var id = $(this).val();
var element = $("#" + id);
if(element){
var html = element.html();
//do something with html here
}
});
NOTE: you may want to put an ID value on your textbox to ensure you get the correct control
Although I strongly suggest you find a way around using duplicate ID values, you could have a function like this to get the DIV you want...
function GetContent(className, id) {
var result = null;
var matchingDivs = $("." + className);
matchingDivs.each(function(index) {
var div = $(matchingDivs[index]);
if (div.attr("id") == id) {
result = div.html();
}
});
return result;
}
Click here for working example
I recommend you give the textbox an ID, in case you add other textboxes to the page.
But if you only have the 1 text input, the following would work:
var id = $('input:text:first').val();
var innerHtml = $('#' + id).html();
Here is a jsFiddle that will alert the html using this technique whenever the text in the textbox changes.
$("#id.class")
will select the necessary element by both class and ID (replacing id and class with their respective names, of course).
Adding .html() to the end will get you the content.
i.e:
$("#id.class").html()

Categories

Resources