Get last tag data on change - javascript

I have this code:
var i = 0;
$('#jobs').change(function() {
var id = $(this).select2('data')[i].id;
var txt = $(this).select2('data')[i].text;
$('#jobSection').append('<fieldset><legend>' + txt + '</legend><input type="text" class="form-control" name="size_' + id + '" placeholder="Size"><br/><select class="form-control" name="type_' + id + '"><option>Type</option><option value="1">Type 1</option><option value="2">Type 2</option></select></fieldset>');
i++
});
With that I am appending extra input fields for each tag.
But it works only if I select tags by order otherwise it takes previous tag data.
I understand that problem is in i because this is not actual loop but change event.
So I want to know how to get just last tag data on change?
Full example is here

var selectOption = [{
"id": 1,
"text": "one"
}, {
"id": 2,
"text": "two"
}, {
"id": 3,
"text": "three"
}, {
"id": 4,
"text": "four"
}, {
"id": 5,
"text": "five"
}];
$('#jobs').select2({
tags: true,
multiple: true,
placeholder: 'Choose job...',
data: selectOption
});
var selectOptionObj = {};
$.each(selectOption, function(i, v) {
selectOptionObj[v.id] = v;
});
$('#jobs').change(function() {
var thisVal = $(this)[0].value.split(',');
var i = thisVal[thisVal.length - 1];
var id = selectOptionObj[i].id;
var txt = selectOptionObj[i].text;
$('#jobSection').append('<fieldset><legend>' + txt + '</legend><input type="text" class="form-control" name="size_' + id + '" placeholder="Size"><br/><select class="form-control" name="type_' + id + '"><option>Type</option><option value="1">Type 1</option><option value="2">Type 2</option></select></fieldset>');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.full.min.js"></script>
<div id="jobSection">
<input type="text" id="jobs">
</div>
Refer : JSFiddle

I think you should redesign your application to rebuild whole #jobSelection node every time selection changed according to current selected elements. This way your #jobSelection structure always be actualized.
I looked through event object and didn't find info about last selected option. So you need to work with current state and not with changes.
If this is not the option, you can always keep previous selection state in some variable and diff it with current state on every onChange event. But I think it is not the best solution for your problem.

Related

attaching json into data attribute in an array of DOM elements

I'm making a website with recipes in it and I am loading them from a json file via a Mustache.js template.
My json looks something like this:
{
"recipes":[
{"name": "A", preparationTime: "40min", "servings": "3", "image": "path/to/imageA"},
{"name": "B", preparationTime: "30min", "servings": "2", "image": "path/to/imageB"},
{"name": "C", preparationTime: "20min", "servings": "3", "image": "path/to/imageC"},
{"name": "D", preparationTime: "30min", "servings": "4", "image": "path/to/imageD"}
]
}
my template looks like this:
var recipeTemplate = "" +
"<div class='col-6 recipeUnit'>" +
"<div class='recipeItem row'>" +
"<div class='recipeItem__image col-5'><img src='{{image}}' alt='recipe image'></div>" +
"<div class='recipeItem__description col-7'>" +
"<h3 class='recipeTitle'>{{name}}</h3>" +
"<div class='details'>" +
"<span>{{preparationTime}}</span>" +
"<span>{{servings}}</span>" +
"</div>" +
"<a class='buttonDetails' href='#'>see more</a>" +
"</div>" +
"</div>" +
"</div>";
And my ajax load function looks like this:
$(document).ready(function(){
loadRecipes()
function loadRecipes(){
$.ajax({
type: "GET",
url: "recipes.json",
dataType: "JSON",
cache: false,
success: function(data){
$section.empty();
for(var i = 0; i < data.recipes.length; i++){
var recipe = data.recipes[i];
var html = Mustache.to_html(recipeTemplate, recipe);
$section.append(html);
$button = $(".buttonDetails");
$button.data.recipe = recipe;
};
$button.on("click", function(){
console.log($(this).data.recipe)
return false;
});
});
}
})
I want to be able to store the json per specific recipe into the $button in each recipe displayed on the page. Everything works fine but when I want to console.log the data.recipe property when I click the button I always get the last array item from the json. I have been struggling with this for quite some time now and I don't understand why it's displaying the last item.
Originally I took the idea from telez here:
Best practices for Storing JSON in DOM.
I would appreciate if anyone could explain to me why is this problem happening and how could I fix it.
Because $button = $(".buttonDetails"); matches all the buttons appended to the document up to that point. So basically you iterate over all recipes and set the last receipts data to the all buttons for each recipe. This leaves you with all buttons data set to the last recipe.
The problem is in the line:
$button = $(".buttonDetails");
You get all the buttons and assign a recipe to all of them at once.
To avoid this, you should change your selector so it will search in the current template only.
There's a couple of issues here.
Your reference to $button is being reassign every iteration, so when you click the $button, it points to the last button that was bound.
Because your $button is being reassigned, the data associated with it will also be reassigned.
Please see this fiddle
$(document).ready(function(){
var $section = $('section');
loadRecipes();
function loadRecipes(){
var solution = [];
for(var i = 0; i < data.recipes.length; i++){
var $button;
var recipe = data.recipes[i];
var html = Mustache.to_html(recipeTemplate, recipe);
$section.append(html);
$button = $(".recipeUnit:last .buttonDetails"); // get the last $button every time...
$button.data.recipe = recipe;
// capture each $button and store it in array. By doing so we ensure we don't reasign the button.
solution[i] = $button;
solution[i].data('recipe',recipe);
};
// $button here is going to be the D button, because $button is assigned 4 times.
$button.on("click",function(){
console.log('This is wrong, because the $button variable is repointed on every iteration',$(this).data.recipe);
return false;
});
// here we have really do have 4 different buttons...
solution.map(function($button){
$button.on('click',function(e){
console.log($(this).data().recipe);
});
});
}
})

Javascript prevent "undefined" value with dynamic array

I have dynamic form elements and trying to add values to input's value attribute which is coming from as a array. However sometimes there are no any data ,then javascript prints undefined to value area of the input element.
How can i check it and dont print undefined in dynamic element?
$("#test").append("<input type='text' name='first-test' class='form-control' value='"+testarray['first-test']+"' >
When we check the textbox , it shows "undefined" as a value.
I think this will work
$("#test").append("<input type='text' name='first-test' class='form-control' value='"+(testarray['first-test']===undefined ? "Insert here what ever you want when it is empty.":testarray['first-test'])+"'>");
https://jsfiddle.net/b51gq0mq/2/
Here is working example, we pass an array with objects into our function. For loop simply skips if value is not found.
var yourArray = [
{ name: 'Janet' },
{ name: 'Jane', value: 1 },
{ name: 'Tom', value: 2 },
{ name: 'Kate', value: 3},
{ name: 'Mark' },
{ name: 'Tom' }
];
function addInputs(array) {
for (i = 0; i < array.length; i++) {
var element = array[i];
// Skips if no value found
if (element.value === undefined) {
continue;
}
var html = '<input type="text" name="' + element.name + '" class="form-control" value="' + element.value + '" />';
$('#test').append(html);
}
};
addInputs(yourArray);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="test"></div>

Dynamically adding text box using jQuery

I'm working on a project for school that involve converting a form into HTML. At one point there is a drop down that asks if you are applying to be a TA or a PLA. When selected, I want a new text box to appear below that drop down. Here is what I have:
This is the dropdown data in my controller:
$data['selection'] = array("TA (Graduate Student)", "PLA (Undergraduate)");
Here is what is in the view:
<label for="studentSelection">What position are you applying for?
<?php echo Form::select(array( 'name'=> 'position', 'id' => 'position', 'class' => 'form-control', 'data' => $data['selection']));?>
</label>
This is what I'm attempting:
$("#position").change(function() {
//one selection is 1, the other is 0.
if ($("#position").val() == 1){
var row = $("<div class='row' id='row_position></div>");
var year = $('<div class = "col-md-6"></div>');
var position = $('<input type = "text", id="position1", name="position1" class="form-control"></input>');
$("#position").append(row);
row.append(year);
year.append(position);
//alert works when PLA is selected.
//alert("hello");
}
else {
//something else
}
I can't quite figure it out
I tried to fix the errors in your code: https://jsfiddle.net/840xyrL7/1/
<select id='position'>
<option value="1">Item1</option>
<option value="2">Item2</option>
</select>
$("#position").change(function() {
//one selection is 1, the other is 0.
if(this.selectedIndex === 1){
var row = $("<div class='row' id='row" + this.selectedIndex + "'></div>");
var year = $('<div class = "col-md-6"></div>');
var position = $('<input placeholder="Your text" type="text" id="position' + this.selectedIndex + '" name="position' + this.selectedIndex + '" class="form-control"></input>');
year.append(position);
row.append(year);
$("#position").after(row);
}
});
Here are the lessons learned for you:
Do not put comma between the attributes: type = "text", id=
Be careful about closing quotes: id='row_position></div>
Do not use append if you want to add a new control next to another control: $("#position").append(row);, instead use .after()
<div class='row' id='row_position></div>
You're missing a closing single quote for id, which is probably breaking everything. Try replacing that with
<div class='row' id='row_position'></div>

AngularUI Boostrap typeahead doesn't select when item is clicked

I am new to Angular and having issues with the AngularUI bootstrap toolkit where clicking on an item in the dropdown, doesn't fill the textbox with the full value. The dropdown box disappears when clicked and only the characters that were typed are left in.
Here is the code (start typing "spy" into the textbox)
http://plnkr.co/edit/WYLn0c6HvuOLl1pJBCxa?p=preview
<body>
<div data-ng-controller="AssetCtrl">
<br />
<input type="text" ng-model="selected" typeahead="asset.ticker as typeaheadLabel(asset) for asset in assets | filter:{ticker:$viewValue}" class="form-control">
</div>
<script>
var ConsoleApp = angular.module('ConsoleApp', ['ui.bootstrap']);
function AssetCtrl($scope) {
$scope.assets = [{
"assetClass": "stock",
"ticker": "spy",
"description": "S&P"
}];
$scope.typeaheadLabel = function(item) {
return item.ticker.toUpperCase() + ' (' + item.assetClass + ') - ' + item.description;
};
}
ConsoleApp.controller('AssetCtrl', AssetCtrl);
</script>
</body>
It seems like the issue lies in the typeaheadLabel function which gets evaluated while calculating the model value and often the item comes as null. You could add some defensive null checks on the label function so that value evaluation doesn't get broken, because this is what typeahead actually matches the value of the model as :-
Snippet From TypeAhead
return {
itemName:match[3],
source:$parse(match[4]),
viewMapper:$parse(match[2] || match[1]), //<-- here if the function evaluation is undefined which is in your case it will take the value of ticker propery as modelValue
modelMapper:$parse(match[1])
};
WorkAround1:-
$scope.typeaheadLabel = function(item) {
if(!item || !item.ticker) return;
return item.ticker.toUpperCase() + ' (' + item.assetClass + ') - ' + item.description;
};
Plnkr
Another way i would suggest is to add a displayName property in the viewModel itself. Ex:-
WorkAround 2:
$scope.assets = [{
"assetClass": "stock",
"ticker": "spy",
"description": "S&P"
},{
"assetClass": "stock",
"ticker": "asd",
"description": "A&D"
}].map(typeaheadLabel);
function typeaheadLabel(item) {
item.displayName = item.ticker.toUpperCase() + ' (' + item.assetClass + ') - ' + item.description;
return item;
};
and specify displayName as alias asset.ticker as asset.displayName for asset in assets
Plnkr2

jQuery is not pulling data from a JSON and adding the data - What's wrong with my code?

I have a JSON file set up with data that, when a user on my site selects a music genre from a dropdown menu, songs of that music genre (the songs are stored in the JSON file, along with the artist) will be displayed in a combo box on my site. Sadly, this is not going according to plan.
I know that this error is based around some mistake that I am making, but I don't know where exactly! My browser's console is saying, "Uncaught TypeError: Cannot read property 'undefined' of undefined" on the following statement:
var selectedValue = dropDown.options[dropDown.selectedIndex].value;
However, I don't know what that means or if it's actually a legitimate runtime error. Anyways, here is my code. I would really appreciate it if you awesome people could look over my code and see what you think may be causing my code to not act in the way that I want to it to act (detailed in the first paragraph) :)
JSON
{
"library":
[
// ROCK
{
"title": "Your Love",
"artist": "The Outfield"
},
{
"title": "Voodoo Child",
"artist": "Jimi Hendrix"
},
{
"title": "When I'm Gone",
"artist": "Three Doors Down"
},
// ALTERNATIVE
{
"title": "Jumper",
"artist": "Third Eye Blind"
},
{
"title": "One Week",
"artist": "Barenaked Ladies"
},
{
"title": "The Middle",
"artist": "Jimmy Eat World"
}
]
}
JavaScript
$(document).ready(function()
{
// Declare our needed variables
var dropDown = $("#music-genre"); // <select id="music-genre">...</select>
var selectedValue = dropDown.options[dropDown.selectedIndex].value;
var target = $('#song'); // <select multiple id="song">...</select>
// If "Alternative" is chosen, choose the alternative songs
// I didn't add a conditional statement for choosing "Rock" because it's the same
if (selectedValue == "Alternative")
{
// "data/music.json" is the location of my JSON file
$.getJSON("data/music.json", function(data)
{
$.each(data, function(key, val)
{
target.innerHTML += '<option value="' + val.title + '">' + val.title + 'by ' + val.artist + '</option>';
}); // END $.each
}); // END $.getJSON
} // END if
}); // END ready()
HTML
<!-- Drop Down -->
<select id="music-genre">
<option id="rock" value="rock">Rock</option>
<option id="alternative" value="alternative">Alternative</option>
</select>
<!-- Combo Box -->
<select multiple id="song">
<!-- "Select one or more" is disabled because I don't want users selecting it -->
<option value="select" disabled>Select one or more</option>
</select>
Your the data you want from your returned JSON is in the library array, so you need to use data.library.
Also note that target will be a jQuery object which does not have a innerHTML property, so you need to use append instead:
$.each(data.library, function(key, val) {
target.append('<option value="' + val.title + '">' + val.title + 'by ' + val.artist + '</option>');
})
Finally as others mentioned you can use the val() property on a select element to get the selected value:
var selectedValue = $("#music-genre").val();
Try to use it like this:
JSON:
{
"library": {
"rock": [
{
"title": "Your Love",
"artist": "The Outfield"
},
{
"title": "Voodoo Child",
"artist": "Jimi Hendrix"
},
{
"title": "When I'm Gone",
"artist": "Three Doors Down"
}
],
"alternative": [
{
"title": "Jumper",
"artist": "Third Eye Blind"
},
{
"title": "One Week",
"artist": "Barenaked Ladies"
},
{
"title": "The Middle",
"artist": "Jimmy Eat World"
}
]
}
JAVASCRIPT:
$(function(){
// Declare our needed variables
var dropDown = $("#music-genre"); // <select id="music-genre">...</select>
// var selectedValue = dropDown.options[dropDown.selectedIndex].value;
var target = $('#song'); // <select multiple id="song">...</select>
dropDown.on("change", function(){
var genre = $(this).val();
var html = '';
$.getJSON("test.json", function(data){
$.each(data.library[genre], function(key, val){
html += '<option value="' + val.title + '">' + val.title + 'by ' + val.artist + '</option>'; // Save whole html in a var
}); // END $.each*/
target.html(html); // append it in target div
}); // END $.getJSON
});
}); // END ready()
Try to use:
var selectedValue = dropDown.find(':selected').val();
or:
var selectedValue = dropDown.val();
instead of:
var selectedValue = dropDown.options[dropDown.selectedIndex].value;
since dropDown is a jQuery object which required you to use jQuery method here.
Also note that data contains library array, so you need to access this array to get the value inside:
$.each(data.library, function(key, val) {
target.innerHTML += '<option value="' + val.title + '">' + val.title + 'by ' + val.artist + '</option>';
});

Categories

Resources