I'm working on this project for learning purposes. The tasks for now are very simple:
Populate data from DB using $.getJSON.
Check every 'n' seconds for new data and append it to the list.
Notify user about new data changes.
Here is the example of where I got so far: ( JSBin /Don't forget to run js)
All the issues will be visible when running the example.
Here is the JS code that i have:
$(document).bind('pageinit', function(){
var $myList = $( "#myList" );
var newItems = [];
function loadList(){
$.getJSON("http://jsbin.com/vayeni/2.js",function(data){
$.each(data, function( index, value ) {
newItems.push( "<li><a>" + value.airline + "</a></li>" );
if(data>newItems){
alert('New Entry');
data=newItems;
}
});
$myList.append( newItems.join( "" ) );
$myList.listview( "refresh" );
setTimeout(loadList,1000);
});
}
loadList();
});
Thanks for your help !
Your data comparison is not correct.
You are comapring this:
<li><a>JetBlue</a></li>
<li><a>Continental</a></li>
...
to this:
{
"id": "1",
"airline": "JetBlue",
"number": "222",
"people": "3",
"time": "12:20"
},
{
"id": "2",
"airline": "Continental",
"number": "222",
"people": "5",
"time": "23:21"
},
There will be always inequality.
You should use another approach. For example, if the id field from your JSON array is an unique one you can attach it to each item from the unordered list as an id attribute. For example:
newItems.push( "<li id=\"" + value.id + "\"><a>" + value.airline + "</a></li>" );
This way, at each iteration you can check if the incomming JSON item already exists into your list and add it when there is no match. Eg:
if (!$myList.find('#' + value.id).length) {
newItems.push( "<li id=\" + value.id + \"><a>" + value.airline + "</a></li>" );
}
Finally, you can append the newItems contents directly if there are items inside:
if (newItems.length > 0) {
$myList.append( newItems.join( "" ) );
}
Here is the edited snippet: JSBin
Related
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);
});
});
}
})
I've been trying to populate div tables with dummy JSON data but I cannot seem to do it. What I want to do is display certain data depending of the selection in a dropdownbox. Also I need to create new row with a new dropdownbox when an item is selected. Could you give me some advice of what's the best way to do it. I'm able to create something close to what I need in Angular but I need it in pure JavaScript. Thanks in advance!
structure of my div tables
Suppose in data you have json object
var data = [
{
"line": "Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.",
"author": "Brian W. Kernighan",
"num" : ["1","2","3"]
},
{
"line": "Walking on water and developing software from a specification are easy if both are frozen.",
"author": "Edward V Berard",
"num" : ["5","0","15"]
},
{
"line": "It always takes longer than you expect, even when you take into account Hofstadter's Law.",
"author": "Hofstadter's Law",
"num" : ["15","222","301"]
}];
and you want to populate all authors in above json object to table and num into respective dropdown element of table-row. Then following populateHTML() function populate object to table and num into respective dropdown element of table-row as shown in below image .
function populateHTML(data) {
if (typeof(data) == 'object') {
document.write('<table>');
for (var i in data) {
document.write('<tr><td>'+data[i].author+'</td><td><select>');
for(var j in data[i].num){
document.write('<option>' +data[i].num[j]+ '</option>');
}
document.write('</select></td></tr>');
}
document.write('</tr></table>');
} else {
document.write(' => ' + data);
}
}
This can be achieved with the following code: You can also check an example here: http://skillcram.com/JS_DivTable.htm
<script type="text/javascript" >
function populateTable() {
var tableData = {
products : [
{"id": 100,"name":"Laptop", "qty":1,"status": ["Delivered","Damaged","Missing"]},
{"id": 200,"name":"Monitor", "qty":2,"status":["Refused","Partial"]}
]
}
var tableBody = document.getElementsByClassName("divTableBody");
for (i in tableData.products){
tableBody[0].innerHTML += "<div class=\"divTableRow\"> " +
"<div class=\"divTableRowCell\">"+ tableData.products[i].id +" </div> " +
"<div class=\"divTableRowCell\">"+ tableData.products[i].name +" </div> " +
"<div class=\"divTableRowCell\">"+ tableData.products[i].qty +" </div> "+
"<div class=\"divTableRowCell\">"+ getSelectHTMl(tableData.products[i].status) +
" </div> "+
"</div>";
}
}
function getSelectHTMl(status) {
selectHTMlStr = "<select> ";
for (j in status){
selectHTMlStr +=
"<option value=\""+ status[j]+ "\" id=\"itemStatus\" >"+status[j]+ " </option>" ;
}
selectHTMlStr += "</select>" ;
return selectHTMlStr;
}
</script>
Help please, guys.
After an Ajax call, I have a JSON object with two rows (users). I have dynamic id's as I intend to load some content (a form to edit the user details) on the page. My problem is each user row that the FOR loop generates has the same ID. So all of the Ajax generated rows have the same ID, 48 in this case.
Here is the code..
// Get the admin information
var loadAdmin = function() {
$.ajax({
type: 'GET',
id: id,
cache: false,
url: 'scripts/administratorsList.php?id=' + id
}).done(function(data) {
var adminData = JSON.parse(data);
for (var i in adminData) {
var userId = adminData[i].id;
$('#adminList').append('<li class="media"><div class="media-left"><img src="assets/images/placeholder.jpg" class="img-circle" alt=""></div><div class="media-body"><div class="media-heading text-semibold">' + adminData[i].userName + '</div><span class="text-muted">Administrator</span></div><div class="media-right media-middle text-nowrap"><span class="text-muted"><i class="icon-pin-alt text-size-base"></i> ' + adminData[i].userCompany + '</span></div></li>')
// Add the edit form view here
$('#edit' + userId).on('click', function(userId) {
var userId = adminData[i].id;
$('#userConfig').append('Here I will generate the form to edit user ' + userId); // This is where the ID stays the same. I have used .append over .html for debugging purposes. Each row returns an ID of 48
});
}
});
};
Below is the JSON file
[{
"id": "17",
"userName": "Mark Bell",
"userCompany": "Pro Movers",
"userTelephone": "12345678911",
"userEmail": "info#info.uk",
"userPassword": "md5hash",
"userUAC": "6",
"originalUAC": "6",
"userRegistered": "20150826",
"activationKey": "0",
"userLastLoggedIn": "20160302",
"userBranch": "0",
"userAdmin": "0"
}, {
"id": "48",
"userName": "demo",
"userCompany": "Monstermove",
"userTelephone": "12345678912",
"userEmail": "info#info.uk",
"userPassword": "demo",
"userUAC": "6",
"originalUAC": "6",
"userRegistered": "20160305",
"activationKey": "0",
"userLastLoggedIn": "20160305",
"userBranch": "3",
"userAdmin": "3"
}]
Thanks in advance
With Jacub's implementation
for(var i in adminData)
{
var userId = adminData[i].id;
storeValueToRemainSame(userId);
}
function storeValueToRemainSame(userId) {
$('#adminList').append('<li class="media"><div class="media-left"><img src="assets/images/placeholder.jpg" class="img-circle" alt=""></div><div class="media-body"><div class="media-heading text-semibold">' + adminData[i].userName + '</div><span class="text-muted">Administrator</span></div><div class="media-right media-middle text-nowrap"><span class="text-muted"><i class="icon-pin-alt text-size-base"></i> ' + adminData[i].userCompany + '</span></div></li>')
// Add the edit form view here
$('#edit' + userId).on('click', function(userId) {
var userId = adminData[i].id;
$('#userConfig').append('Here I will generate the form to edit user ' + userId); // This is where the ID stays the same. I have used .append over .html for debugging purposes. Each row returns an ID of 48
});
}
The issue here is that you are referencing value outside of the handler which changes, therefore the last value is retained.
for(var i in data) {
var value = i;
$('#someId').on('click', function(){
console.log(value);
});
}
The only written value will be the last one as the reference to it will remain.
Possible solution is for example:
function storeValueAndHandleClickEvent(value){
$('#someId').on('click', function(){
console.log(value);
});
}
for(var i in data) {
storeValueAndHandleClickEvent(i);
}
EDIT: If I use the same code as in the question
for (var i in adminData) {
var userId = adminData[i].id;
storeValueToRemainSame(userId);
}
function storeValueToRemainSame(userId) {
$('#adminList').append('<li class="media"><div class="media-left"><img src="assets/images/placeholder.jpg" class="img-circle" alt=""></div><div class="media-body"><div class="media-heading text-semibold">' + adminData[i].userName + '</div><span class="text-muted">Administrator</span></div><div class="media-right media-middle text-nowrap"><span class="text-muted"><i class="icon-pin-alt text-size-base"></i> ' + adminData[i].userCompany + '</span></div></li>')
// Add the edit form view here
$('#edit' + userId).on('click', function(userId) {
var userId = adminData[i].id;
$('#userConfig').append('Here I will generate the form to edit user ' + userId); // This is where the ID stays the same. I have used .append over .html for debugging purposes. Each row returns an ID of 48
});
}
Try this simple code outside ajax
You will use event delegation an will select the id from the id of the dynamically added dom element
$('ul').on('click','div[id^="edit"]',function() {
var userId = $(this).attr('id').substring(4);
$('#userConfig').append('Here I will generate the form to edit user ' + userId); // This is where the ID stays the same. I have used .append over .html for debugging purposes. Each row returns an ID of 48
});
How to retrieve the element class and display its name?
I read that I can use sth like:
var elementResult = element[0].getElementsByClassName('menu-item-active');
console.log('element[0].getElementsByClassName: ', elementResult);
JSON :
{
"title": "Fruits",
"mainmenu": [
{
"id": "apples",
"title": "Apples",
"href": "#/apples",
"act": "menu-item-active"
},
{
"id": "bananas",
"title": "Bananas",
"submenu": [
{
"id": "banana-box",
"title": "Banana Box",
"href": "#/banana-box",
"act": "menu-item-active"
}
]
}
]
}
I have a directive
.directive('dir', function () {
return {
restrict: 'E',
template: "<nav class=\"navbar navbar-inverse navbar-fixed-top\" role=\"navigation\" id=\"nav-bar\">"
+ "<div class=\"container-fluid\">"
+ "<div class=\"navbar-header\">"
+ "<span class=\"navbar-brand\" >{{title}} </span>"
+ "</div>"
+ "<ul class=\"nav navbar-nav\" ng-repeat=\"item in mainmenu\">"
+ "<li>"
+ "{{item.title}}"
+ "</li>"
+ "</ul>"
+ "</div>"
+ "</nav>"
}
});
And the controller:
.module('menu').controller('menuCtrl', ['$scope', '$http', function ($scope, $http) {
$http.get('MenuItems.json').success(function (data) {
$scope.mainmenu = data.mainmenu;
$scope.title = data.title;
});
}]);
What I want to achieve:
when I will be on the each site/subsite and I want to display its name as {{title}} in my site
I tried to use the elementResult but I don't know how to check if the element is menu-item-active. I thought about using the ng-if statement, but I don't know how to handle it.
For.ex.
ng-if="elementResult has class 'menu-item-active' then display its 'title' as {{title}} in menu bar
In dom, getElementByClassName returns NodeList.
var nodeList = element[0].getElementsByClassName('menu-item-active');
So then you have to iterate over nodes. You can use it's length property.
for ( var i = 0 ; i < nodeList.length ; i++ )
if ( nodeList[i].className == 'menu-item-active' )
var elem = $scope.findByClassName('menu-item-active');
and in your controller write scope method:
$scope.findByClassName = function(id){
for ( var i = 0 ; i < jsonArray.length ; i++ )
if ( jsonArray[i].mainmenu.act == id )
return jsonArray[i].title;
}
Also prepare proper JSON file - key value pairs and arrays of objects ( these starting with { and ending with } ).
Also, in Angular you can pass to param to a page/view, so you can use routes and $routeParam service must be dependency injected in your controller. Then for page "new/3" you ask for "$routeParams.id" and you will get 3.
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>';
});