Here is my code http://jsfiddle.net/5kkja1ua/
<ul id="results"></ul>
<script type="text/x-handlebars-template" id="item_tmpl">
{{#json}}
<p> {{index}}: {{title}} </p>
{{/json}}
<button>next</button>
</script>
var dataObject = {
json:[
{
"index": 0,
"title": "IMAGEFLOW"
},
{
"index": 1,
"title": "ENERSOL"
},
{
"index": 2,
"title": "BUNGA"
},
{
"index": 3,
"title": "BITENDREX"
},
{
"index": 4,
"title": "MAROPTIC"
},
{
"index": 5,
"title": "AEORA"
},
{
"index": 6,
"title": "PYRAMIA"
},
{
"index": 7,
"title": "ANIXANG"
},
{
"index": 8,
"title": "SNORUS"
}
]
};
var tpl_source = $("#item_tmpl").html();
var h = Handlebars.compile(tpl_source);
var content = h(dataObject);
// output
var results = document.getElementById("results");
results.innerHTML = content;
One method I can do it easily is to get 3 items wrapped (by using slice()) in a div and then hide and show. However, this method needs to load the whole lot first.
I'd like to know if possible to have the output to show every 3 items as a group? if possible to split the json data?
0: IMAGEFLOW
1: ENERSOL
2: BUNGA
then click next to show another 3 items and so on. Thanks
What about to create handlebars helper to use each with suport from/to parameters, like
Handlebars.registerHelper('each_from_to', function(ary, from, max, options) {
from || (from = 0);
var result = [ ];
for(var i = from; i < from + max && i < ary.length; ++i)
result.push(options.fn(ary[i]));
return result.join('');
});
After that your template will looks like
<script type="text/x-handlebars-template" id="item_tmpl">
{{#each_from_to json startFrom rowsOnPage}}
<p> {{index}}: {{title}} </p>
{{/each_from_to}}
</script>
Check this fiddle. Does it solve your problem?
http://jsfiddle.net/5kkja1ua/1/
Related
I'm trying to create a piece of JavaScript that can read through specific parts of a linked object and place them iteratively into another piece of code which then places the code into HTML and into the front-end.
I've managed to get the fetch part working whereby it pulls in the JSON and can be read in the console, when summoned. Once the code runs, I'm able to refer to the data and bring out the whole dataset with something like:
console.log(AllOffers);
and I can drill down into something like the offerName in the JSON by using the following syntax in a variable and calling it in the console:
var OfferName = data.offersBreakdown.allOffers[0].offers[0].offerName;
However this only pulls in the first iteration of offerName because in the variable I've set it to look into the first iteration of its parent, 'offers'. What I'm looking to do is create a variable which prints all of the offerName data so that I can call on it instead of the data_test variable further down in the code, which processes the data into HTML. Sounds confusing? It is.
Ideally what I think I need is to be able to ask it to look into each child item of 'offers' (rather than just the first one) and then have it look for 'offerName'. I can't work out how one would achieve this. The best I can come up with is to remove the [0] from 'offers', but if I do that, it returns undefined as the result.
Here's my JavaScript (and a bit of jQuery):
<script>
// fetch call for the JSON data (see below)
fetch('api_url', {
headers: {
'Authorization': 'auth_token'
}
})
.then(response => response.json())
.then(function (data) {
var AllOffers = data.offersBreakdown.allOffers[0];
var AllOffers_Offers = data.offersBreakdown.allOffers[0].offers;
var OfferName = data.offersBreakdown.allOffers[0].offers[0].offerName;
var OfferImageUrl = data.offersBreakdown.allOffers[0].offers[0].imageUrl;
console.log(AllOffers);
function createCard(cardData) {
var cardTemplate = [
'<div class="card">',
'<p>My name is: ',
cardData.Offer || 'No offer',
'</p>',
'<p>My job is: ',
cardData.Img || 'No image',
'</p></div>'
];
// a jQuery node
return jQuery(cardTemplate.join(''));
}
var data_test = [
{ "Name": OfferName, "Img": OfferImageUrl },
{ "Name": OfferName, "Img": OfferImageUrl },
{ "Name": OfferName, "Img": OfferImageUrl },
];
var cards = jQuery();
// Store all the card nodes
data_test.forEach(function(item, i) {
cards = cards.add(createCard(item));
});
// Add them to the page... for instance the <body>
jQuery(function() {
jQuery('body').append(cards);
});
</script>
Here's the JSON
<script>
// the JSON
{
"offersBreakdown": {
"totalAddedOffers": 0,
"totalOffers": 2,
"totalAddedRewards": 0,
"totalRewards": 0,
"totalAddedStreakOffers": 0,
"totalStreakOffers": 0,
"allOffers": [
{
"offers": [
{
"offerName": "Offer name 1",
"imageUrl": "https://url_path_1.jpg"
},
{
"offerName": "Offer name 2",
"imageUrl": "https://url_path_2.jpg"
},
{
"offerName": "Offer name 3",
"imageUrl": "https://url_path_3.jpg"
},
{
"offerName": "Offer name 4",
"imageUrl": "https://url_path_4.jpg"
}
]
}
</script>
I'm assuming what you're looking for is a way to loop through all of the offerNames, in which case a simple for loop would suffice. Since your data includes nested arrays and objects, we need two loops, one to iterate through your allOffers array and then a nested for loops to iterate through the offers array inside of your allOffers array
var data = {
"offersBreakdown": {
"totalAddedOffers": 0,
"totalOffers": 2,
"totalAddedRewards": 0,
"totalRewards": 0,
"totalAddedStreakOffers": 0,
"totalStreakOffers": 0,
"allOffers": [{
"offers": [{
"offerName": "Offer name 1",
"imageUrl": "https://url_path_1.jpg"
}, {
"offerName": "Offer name 2",
"imageUrl": "https://url_path_2.jpg"
}, {
"offerName": "Offer name 3",
"imageUrl": "https://url_path_3.jpg"
}, {
"offerName": "Offer name 4",
"imageUrl": "https://url_path_4.jpg"
}]
}]
}
};
var allOffers = [];
var jsonObjectAllOffers = data.offersBreakdown.allOffers;
for (var i = 0; i < jsonObjectAllOffers.length; i++) {
var offers = jsonObjectAllOffers[i].offers;
for (var j = 0; j < offers.length; j++) {
var objectToAppend = {
"Name": offers[j]["offerName"],
"Img": offers[j]["imageUrl"]
};
allOffers.push(objectToAppend);
}
}
console.log(allOffers);
And now you can use your allOffers variable to loop through with the "forEach" and make into HTML
I am trying to make this simple loot system to work but I keep getting this TypeError and I have tried everything I could think of.
Everything is explained in the code I added below. It was working yesterday but it seems like today is no longer working...
Here is the code in the JS file:
console.info("Working!");
var items;
//here I get the json
$.getJSON('js/items.json', function(data) {
return items = data;
});
//run the function cuz laziness to put it every time in the console
loot();
function loot() {
//these are the drops from the mob
const ids = [1, 2, 3, 4, 5, 6, 7];
//define a random number to see what loot it drops
let rand = Math.floor((Math.random() * 100) + 1);
var loot;
//if the chance is between 1 and 5(including them) do that
if (rand >= 1 && rand <= 5) {
var legends = [];
//here I loop inside the ids constant to see what items are legendary and if they are push them to the legends array
for (var id in ids) {
//HERE IS THE PROBLEM I get Uncaught TypeError here and I have tried everything
if (items[ids[id]].rarity == "Legendary") {
legends.push(ids[id]);
};
};
console.log(`legends: ${legends}`);
//then I random the items inside legends array to get the loot
loot = legends[Math.floor(Math.random() * legends.length)];
};
console.warn(`You looted a ${items[loot].name} | ${items[loot].rarity}`);
};
and here is the JSON file:
{
"1": {
"id": 1,
"name": "Sword of Heaven",
"rarity": "Legendary"
},
"2": {
"id": 2,
"name": "Wooden Sword",
"rarity": "Common"
},
"3": {
"id": 3,
"name": "Glass of the Gods",
"rarity": "Rare"
},
"4": {
"id": 4,
"name": "Minor HP Potion",
"rarity": "Common"
},
"5": {
"id": 5,
"name": "The Enchiridion!",
"rarity": "Legendary"
},
"6": {
"id": 6,
"name": "Major MP Potion",
"rarity": "Rare"
},
"7": {
"id": 7,
"name": "Helm of the Forsaken",
"rarity": "Rare"
}
}
try to move your loot function into asynchronous getJson's callback:
console.info("Working!");
var items;
//here I get the json
$.getJSON('js/items.json', function(data) {
items = data;
//run the function cuz laziness to put it every time in the console
loot();
});
if you run it outside of this callback, items variable is not populated yet
if you want to use as function, move AJAX call into loot function, store result and make sure to run it only once:
function lootRun(refresh) {
// use self in order to reach it from anonymous callback
var self = this;
if (!self.items || refresh) {
$.getJSON('js/items.json', function(data) {
// kind of inner functional cache
self.items = data;
loot(self.items);
});
}
else {
loot(self.items);
}
}
function loot(items) {
//these are the drops from the mob
const ids = [1, 2, 3, 4, 5, 6, 7];
//define a random number to see what loot it drops
let rand = Math.floor((Math.random() * 100) + 1);
var loot;
//if the chance is between 1 and 5(including them) do that
if (rand >= 1 && rand <= 5) {
var legends = [];
//here I loop inside the ids constant to see what items are legendary and if they are push them to the legends array
ids.forEach(function(id) {
//HERE IS THE PROBLEM I get Uncaught TypeError here and I have tried everything
if (items[id].rarity == "Legendary") {
legends.push(id);
};
});
console.log(`legends: ${legends}`);
//then I random the items inside legends array to get the loot
loot = legends[Math.floor(Math.random() * legends.length)];
console.warn(`You looted a ${items[loot].name} | ${items[loot].rarity}`);
};
};
now you can run your main loot function via lootRun, pass true to lootRun, if you want to refresh data from server
A few things to note - getJson is asynchronous, so items may be undefined. Move it into the getJson callback. Also, you're trying to get the name of an item that's undefined. You need to move the last console.log statement into the if statement so that you aren't using loot (which is undefined) to index the array.
var data = {
"1": {
"id": 1,
"name": "Sword of Heaven",
"rarity": "Legendary"
},
"2": {
"id": 2,
"name": "Wooden Sword",
"rarity": "Common"
},
"3": {
"id": 3,
"name": "Glass of the Gods",
"rarity": "Rare"
},
"4": {
"id": 4,
"name": "Minor HP Potion",
"rarity": "Common"
},
"5": {
"id": 5,
"name": "The Enchiridion!",
"rarity": "Legendary"
},
"6": {
"id": 6,
"name": "Major MP Potion",
"rarity": "Rare"
},
"7": {
"id": 7,
"name": "Helm of the Forsaken",
"rarity": "Rare"
}
};
// here I get the json
$.getJSON('js/items.json', function(data) {
loot(data);
});
function loot(items) {
//these are the drops from the mob
const ids = [1, 2, 3, 4, 5, 6, 7];
//define a random number to see what loot it drops
let rand = Math.floor((Math.random() * 100) + 1);
var loot;
//if the chance is between 1 and 5(including them) do that
if (rand >= 1 && rand <= 5) {
var legends = [];
//here I loop inside the ids constant to see what items are legendary and if they are push them to the legends array
ids.forEach(function(id) {
//HERE IS THE PROBLEM I get Uncaught TypeError here and I have tried everything
if (items[id].rarity == "Legendary") {
legends.push(id);
};
});
console.log(`legends: ${legends}`);
//then I random the items inside legends array to get the loot
loot = legends[Math.floor(Math.random() * legends.length)];
console.warn(`You looted a ${items[loot].name} | ${items[loot].rarity}`);
};
};
I have two arrays
$scope.tags = [{ "id": 1, "name": "python" }, { "id": 2, "name": "NodeJs" }, { "id": 3, "name": "git" }]
Other one is
$scope.skillsInterested = [1,2];
What is want to do ?
How can i map the above arrays and print only names of the id's in$scope.skillsInterested
I want to print names in first array only the id's present in second.
I have tried this after getting several answers
var tag_map = {};
for (var x = 0; x < $scope.tags.length; x++) {
tag_map[$scope.tags[x]['id']] = $scope.tags[x]['name'];
}
$scope.skillsInts = $scope.skillsInterested.map(function(x) {
return tag_map[x]
On running console.log
console.log("Result", tag_map);
It sometimes give result sometimes it gives 'map' of undefined.
TypeError: Cannot read property 'map' of undefined
at controllers.js:141
at angular.js:16383
at m.$eval (angular.js:17682)
at m.$digest (angular.js:17495)
at m.$apply (angular.js:17790)
at l (angular.js:11831)
at J (angular.js:12033)
at XMLHttpRequest.t.onload (angular.js:11966)
Thanks in advance.
Make a map of your data that looks like this:
var tagMap = { 1: "python", 2: "NodeJs" /* etc. */ };
You can do this by looping over your tags and adding a new property to an object. reduce lets you do this without creating any extra variables.
Then, you can select names from your newly created object using the [] notation: tagMap[1] returns "pyhton".
var tags = [{ "id": 1, "name": "python" }, { "id": 2, "name": "NodeJs" }, { "id": 3, "name": "git" }]
var selectedExpTags = [1,2];
// Make a map for `id: name`
var tagMap = tags.reduce(function(map, tag) {
map[tag.id] = tag.name;
return map;
}, {});
// Quickly select names from the map:
var selectedNames = selectedExpTags.map(function(id) {
return tagMap[id];
});
console.log(selectedNames);
Using this approach, you minimise the iterations over your data. The creation of the map loops over the tags once. Creating the array with names, loops over the selected tags once. So, roughly, the "loop count" is tags.length + selectedTags.length. If you would use an indexOf based approach, your loop count would be tags.length * selectedTags.length.
Use the filter function for first, and then check the id's existnent then map the names from the array.
var first = [{ "id": 1, "name": "python" }, { "id": 2, "name": "NodeJs" }, { "id": 3, "name": "git" }];
var selectedExpTags = [1,2];
var names = first.filter(item => selectedExpTags.some(id => item.id === id)).map(item => item.name);
console.log(names);
You can loop over $scope.selectedExpTags and get a list of all names. You can use array.find if you want first value only.
Sample
var first = [
{ "id": 1, "name": "python" },
{ "id": 2, "name": "NodeJs" },
{ "id": 3, "name": "git" }];
var selectedExpTags = [1,2];
var names = selectedExpTags.map(x=> first.find( y=> y.id === x ).name )
console.log(names);
$scope.newArray = []; // If you need a new array to work with
angular.forEach($scope.tags, function(tag){
$scope.selectedExpTags.forEach(function(selectedTag){
if(selectedTag == tag.id){
//tag.hide = false; // - If you want to update the current array
$scope.newArray.push(tag);
}
// else{ // - If you want to update the current array
// tag.hide = true;
// }
})
})
Lodash is more efficient than angular for manipulating data.
render: function () {
news.fetchMyNews();
for (var i = 1; i <= news.length; i++) {
var newsData = news.get(i);
var newsRow = JST["news/row"](newsData.attributes);
$("#news_tbody").append(newsRow);
if (newsData.is_read == 1) {
this.$('tr').attr("class", "news_read");
} else if (newsData.is_read == 0) {
this.$('tr').attr("class", "news_unread");
}
}
}
In this code newsData.attributes is retrieved well and I get the table with 3 rows rendered.
However, the newsData.is_read values are not retrieved and there is no error message at all, thus, the rows don't get styling.
news is a collection.
I wonder, what can be wrong with this?
JSON file that I'm using for testing looks like this:
[{
"id": 1,
"_type": "friends",
"message": "Your friend ...",
"is_read": 1
},
{
"id": 2,
"_type": "friends",
"message": "Your friend ...",
"is_read": 0
},
{
"id": 3,
"_type": "other",
"message": "User ...",
"is_read": 1
}]
Since newsData is a Model for getting it's attributes either .get('is_read') or newsData.attributes.is_read should be used.
Still this problem Angular.js more complex conditional loops but I felt that the answer to the question as it was asked was right so I accepted it.
So let me elaborate more than I did in the original question.
I'm trying to get this
<h3>11.4.2013</h3>
<ul>
<li>oofrab | 4 | 11.4.2013 14:55 <button>remove</button></li>
<li>raboof | 3 | 11.4.2013 13:35 <button>remove</button></li>
</ul>
<h3>10.4.2013</h3>
<ul>
<li>barfoo | 2 | 10.4.2013 18:10 <button>remove</button></li>
<li>foobar | 1 | 10.4.2013 12:55 <button>remove</button></li>
</ul>
from this data structure
[
{
"id": 4,
"name": "oofrab",
"date": "2013-11-04 14:55:00"
},
{
"id": 3,
"name": "raboof",
"date": "2013-11-04 13:55:00"
},
{
"id": 2,
"name": "barfoo",
"date": "2013-10-04 18:10:00"
},
{
"id": 1,
"name": "foobar",
"date": "2013-10-04 12:55:00"
}
]
Basically the only extra thing over the standard ng-repeat I want to add are those headings. And I simply can't believe I'd have to go thru so many problems by adding them.
This is what I ended up with using the answer I got in the first question http://plnkr.co/edit/Zl5EcsiXXV92d3VH9Hqk?p=preview
Note that there can realistically be up to 400 entries. And I need to be able to add/remove/edit entries on the fly
What the example on plunker is doing is this:
iterating thru the original data creating a new data structure looking like this
{
"2013-10-05": [
{
"id": 4,
"name": "oofrab",
"date": "2013-10-05 14:55:00",
"_orig_index": 0
},
{
"id": 3,
"name": "raboof",
"date": "2013-10-05 13:55:00",
"_orig_index": 1
}
],
"2013-10-04": [
{
"id": 2,
"name": "barfoo",
"date": "2013-10-04 18:10:00",
"_orig_index": 2
},
{
"id": 1,
"name": "foobar",
"date": "2013-10-04 12:55:00",
"_orig_index": 3
}
]
}
allowing me to then get the result I wanted by doing this
<div ng-repeat="(date,subItems) in itemDateMap">
<h3>{{date}}</h3>
<ul>
<li ng-repeat="item in subItems">
{{item.name}} | {{item.id}} | {{item.date}}
<button type="button" ng-click="removeItem(item._orig_index)">x</button>
</li>
</ul>
</div>
Great. But it comes with a cost of shizzload of problems. Everytime a new item is added I have to rebuild the itemDateMap, everytime an item is deleted I have to rebuild the itemDateMap, everytime date is changed, I have to rebuild the itemDateMap. When I want to remove an item, I have to first get index of its original reference. And everytime itemDateMap is rebuilt, the whole thing is re-rendered. And it can't be sorted, as it's an object rather than an array.
When there's a couple of hundred of entries, it also becomes really, really slow. I read somewhere that ng-repeat is quite intelligent, watching values, moving nods in dom rather than re-rendering everything and stuff, but it surely doesn't work this way when I rebuild the whole structure.
This can't be right, all this hassle to do a very, very simple thing..
What should I do?
This is my suggestion - just work with one structure, and only expose one structure to the scope (the map). And create a function to add an array of items to the map, and a function that transforms the map into an array (I assume you need this array for server communication or something).
var toKey=function(item){
return moment(item.date).format("YYYY-MM-DD");
}
$scope.itemDateMap = {};
$scope.addItemToDateMap=function(item){
var key = toKey(item);
if(!$scope.itemDateMap[key]){
$scope.itemDateMap[key] = [];
}
$scope.itemDateMap[key].push(item);
}
$scope.removeItemFromDateMap=function(item){
var key = toKey(item), subitems = $scope.itemDateMap[key];
var index = subitems.indexOf(item);
subitems.splice(index,1);
if(subitems.length === 0){
delete $scope.itemDateMap[key];
}
}
var addArrayToMap = function(items){
for(var i=0; i<items.length; i++){
var item = items[i];
$scope.addItemToDateMap(item);
}
};
$scope.mapToArray = function(){
var items = [];
for(var key in $scope.itemDateMap){
var subitems = $scope.itemDateMap[key];
for(var j=0;j<subitems.length;j++){
var item = subitems[j];
items.push(item);
}
}
return items;
}
I've updated your plnkr with my suggestion. I think it performs quite well.
Oh - I just noticed you want it sorted - I don't have time to update my example, but it is not very complicated. Use this structure instead (array with objects with arrays, instead of object with array) - this way you can use the orderBy:'date' on the root array:
[
{
date:"2013-10-05",
items: [
{
"id": 4,
"name": "oofrab",
"date": "2013-10-05 14:55:00"
},
{
"id": 3,
"name": "raboof",
"date": "2013-10-05 13:55:00"
}
]
},
{
date:"2013-10-04",
items: [
{
"id": 2,
"name": "barfoo",
"date": "2013-10-04 18:10:00"
},
{
"id": 1,
"name": "foobar",
"date": "2013-10-04 12:55:00"
}
]
}
]