Knockout.js updating array within an array - javascript

I have an array in Knockout view model which looks like so:
this.Activities = ko.observableArray([
{ "date": "28/11/2012 00:00:00",
"activities": [
{ "company": "BOW",
"description": "Backup Checks",
"length": "60"
},
{ "company": "AMS",
"description": "Data Request",
"length": "135"
},
]},
{ "date": "30/11/2012 00:00:00",
"activities": [
{ "company": "BOW",
"description": "Backup Checks",
"length": "60"
},
{ "company": "SLGT",
"description": "Software Development",
"length": "240"
},
{ "company": "BOW",
"description": "Data Request",
"length": "30"
},
]},
]);
I use this code to add a new element to it:
this.Activities.push(new NewActivity(company, description, length, fullDate));
Which uses NewActivity function:
function NewActivity(company, description, length, date) {
this.date = date;
this.activities = [{ "company": company, "description": description, "length": length }];
}
And it works fine. However, it creates an entirely new object every time it is getting released. I need to implement a condition when the code would check for the date of the objects already created. If the newly created object had the same date, activity details should be added to activities array within the Activities array for that date.
How can I do it?
All of the data for the Activities array comes from the model in the strongly typed view in MVC application:
this.Activities = ko.observableArray([
#foreach (ActivitySet aSet in Model)
{
#:{ "date": "#aSet.Date",
#:"activities": [
foreach(Activity a in aSet.Activities)
{
#:{ "company": "#a.Companies.Select(c => c.Title).Single()",
#:"description": "#a.Descriptions.Select(c => c.Title).Single()",
#:"length": "#a.LengthInMinutes"
#:},
}
#:]},
}
]);

I suggest that you create a few entities describing your activities:
// Details
function ActivityDetails(company, description, length) {
this.company = ko.observable(company);
this.description = ko.observable(description);
this.length = ko.observable(length);
}
// Activity
function Activity(date, activityDetails) {
this.date = ko.observable(date);
this.details = ko.observableArray(activityDetails);
}
The you can control activities in the following manner:
function ViewModel () {
var self = this;
this.Activities = ko.observableArray([
// Activities here
]);
this.addActivity = function (activity) {
var flag = false;
ko.utils.arrayMap(self.Activities(), function (item) {
// Flag is here so it doesn't keep checking further in iterations
if (!flag) {
if (item.date() === activity.date()) {
item.details.push(activity.details);
flag = true;
}
}
});
// Case where activity date was not found in existing records
if (!flag) {
self.Activities.push(activity);
}
}
}
This requires your view model to have a custom add method which I have provided an example of. Note that everywhere I am resolving observable values, so if you are using non-observable ones remove the function calls.

Related

Print the same child nodes from an object but two levels deep

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

unable to push and append data to the state using React js

here i state with data
state = {
Response: [
{
"id": "15071",
"name": "John",
"salary": "53",
"age": "23",
"department": "admin"
},
{
"id": "15072",
"name": "maxr",
"salary": "53",
"age": "23",
"department": "admin"
},
{
"id": "15073",
"name": "Josef",
"salary": "53",
"age": "23",
"department": "admin"
},
{
"id": "15074",
"name": "Ye",
"salary": "53",
"age": "23",
"department": "admin"
}
]
i am displaying these records in the table. In table u will see 10 records and there will be a button on top of table so if append button is pressed then 10 records has to be added on every button press and the data has to be same but it has to be appended using the below logic i am trying to set the state by pushing 10 records and trying to append it for ex if i have 1,2,3,4,5,6,7,8,9,10 if i press append 1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10 has to be apeended
appendEmployees() {
var reLoadCount = 1;
for (let i = 0; i < 10; i++) {
const myObj = {
id: 0,
name: '',
salary: 0,
department: ''
};
myObj.id = +this.setState.employee[i].id + (reLoadCount * 10);
myObj.name = this.setState.employee[i].name;
myObj.salary = this.setState.employee[i].salary;
myObj.department = this.setState.employee[i].department;
this.setState.employee.push(myObj);
}
reLoadCount++;
}
am i doing some thing wrong here
If I get this right you're trying to add 10 duplicates of the objects in the this.state.employee array, the only difference between these new objects and the existing ones is their id.
If that is the case, here is how you can do that:
appendEmployees() {
this.setState(prevState => {
// Get the biggest ID number.
const maxId = Math.max(...prevState.employee.map(e => parseInt(e.id)));
// create 10 new employees copies of the first 10.
const newEmployees = prevState.employee.slice(0, 10).map((e, i) => ({
...e,
id: (maxId + i + 1)
}));
// return/update the state with a new array for "employee" that is a concatenation of the old array and the array of the 10 new ones.
return {
employee: [...prevState.employee, ...newEmployees]
}
});
}
I've added some comments to the example to explain what it does.
The important thing is this.setState which is the function used to update the state, here, I've used it with a function as the first parameter (it works with objects as well), I've did that because it is the preferred way of generating a new state that is derived from the old state.

dustjs iterate through array and get count

Is there any way in dustjs to iterate through array and get the number of occurrence?
I am trying to get the count of type='MOBILE' from the JSON data below:
[
{
"type": "MOBILE",
"formattedPhoneNumber": "5123 4566"
},
{
"type": "MOBILE",
"formattedPhoneNumber": "5123 4568"
},
{
"type": "MOBILE",
"formattedPhoneNumber": "5123 4568"
},
{
"type": "LANDLINE",
"formattedPhoneNumber": "5123 4568"
}
]
here I am expecting a count of 3 from above example where type is 'MOBILE'.
You can write a simple helper to do this for you. A helper transforms data from your context in a specific way. For more information, you can read the documentation on context helpers
{
"numbers": [{ "type": "MOBILE", ... }, { ... }],
"countByKey": function(chunk, context, bodies, params) {
var target = context.resolve(params.target);
var key = context.resolve(params.key);
var value = context.resolve(params.value);
return target.filter(function(item) {
return item[key] === value;
}).length;
}
}
Then you can use your helper in a template like this:
{#countByKey target=numbers key="type" value="MOBILE"}You have {.} mobile numbers{/countByKey}

Push Json filtered key values to nested ul with Javascript

I need help pushing the values from a filtered json, I need this generate a nested ul list, I can not modify the json format at this point, I you check the console.log you will see the values to create the list, at this point I can't figure how to complete the 'for loop' to render the html markup needed, any help will be appreciated, this is the jsfiddle http://jsfiddle.net/43jh9hzz/, and if you check the console log you will see the values.
This is the Js:
var json='';
var property_set = new Set();
function iterate(obj, stack) {
json="<ul>";
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
if (typeof obj[property] == "object") {
iterate(obj[property], stack + '.' + property);
}
else {
// console.log(property);
property_set.add(property);
json+="<li>";
if(typeof obj[property] !== "number") {
json+="<li>"+obj[property]+"</li>";
console.log(obj[property]);
}
}
} json += "</li>";
}
}
var listEl = document.getElementById('output');
iterate(jsonObj)
And this is the json format:
var jsonObj =
{
"level_1": [
{
"level_1_name": "CiscoSingaporeEBC",
"level_2": [
{
"level_2_name": "Khoo Tech Puat",
"level_2_id": 2222,
"level_3": [
{
"name": "Boon Leong Ong",
"id": 6919
},
{
"name": "Kiat Ho",
"id": 6917
},
{
"name": "Overall Experience",
"id": 6918
}
]
}
]
},
{
"level_1_name": "CiscoLondonEBC",
"level_2": [
{
"level_2_name": "Bernard Mathews Ltd.",
"level_2_id": 2367,
"level_3": [
{
"name": "Barry Pascolutti",
"id": 7193
},
{
"name": "Kathrine Eilersten",
"id": 7194
},
{
"name": "Martin Rowley",
"id": 7189
}
]
},
{
"level_2_name": "FNHW Day 1",
"level_2_id": 5678,
"level_3": [
{
"name": "Jurgen Gosch",
"id": 7834
},
{
"name": "Overall Experience",
"id": 7835
}
]
},
{
"level_2_name": "Groupe Steria Day 1",
"level_2_id": 2789,
"level_3": [
{
"name": "Adam Philpott",
"id": 7919
},
{
"name": "Pranav Kumar",
"id": 7921
},
{
"name": "Steve Simlo",
"id": 7928
}
]
}
]
}
]
};
enter code here
I'm not sure if I am interpretting your request correctly, but I think this is what you want: http://jsfiddle.net/mooreinteractive/43jh9hzz/1/
Basically, you are calling the iterate function to run, but then that's it. The function actually needs to also return the value it generates.
I've added to the end of the function, after the for loop completes:
return json;
Do now the function returns the value it generated, but there are some other issues too. When you recursively call the iterate function again inside the iterate function, you actually want to add what it returns to the current json string housing all of your returned value.
So on that line I changed it from:
iterate(obj[property], stack + '.' + property);
to
json += iterate(obj[property], stack + '.' + property);
Now that other value will come back as well inside the main list you were creating in the first run of the function. Ok so that's pretty close, but one more small thing. I think when you added additional surrounding LI, you actually wanted to do an UL. I changed those to ULs and now I think the result is like a UL/LI list representing the text parts of the JSON object.
Again, that may not be exactly what you were after, but I think the main take away is using the function to return the value, not just generate it, then do nothing with it.

Querying JSON array in JSON object value

Using jsonSelect Plugin I am trying to do a query select as below. I am presenting this example.
How can I retrieve the All Project and Contractors who has the Projects in "North" ProjectArea? I already tried this to get the contractor's name but it didn't work
var contractors = [{
"contractor1": [{
"project": "Project 2",
"projectArea": "South"
},
{
"project": "Project 101",
"projectArea": "East"
}],
"contractor2": [{
"project": "Project 500",
"projectArea": "North"
},
{
"project": "Projetc 108",
"projectArea": "West"
}]
}]
var stringSelect = 'has(:root > .projectArea:val("North"))';
JSONSelect.forEach(stringSelect, contractors, function (resultObj) {
$('body').append('<p>' + resultObj + '</p>');
});
I think the only way you are going to be able to achieve what you want is by changing your JSON data structure. Specifically, you have your contractors' names as keys ('contractor1', contractor2'), but they really should be values. The library you are using returns a set of objects, but I don't see any way to step back a level and obtain each object's corresponding key. I suggest structuring your data like this:
var contractors = [
{
"name": "contractor1",
"projects": [
{ "project": "Project 2", "projectArea": "South" },
{ "project": "Project 101", "projectArea": "North" }
]
},
{
"name": "contractor2",
"projects": [
{ "project": "Project 500", "projectArea": "North" },
{ "project": "Project 108", "projectArea": "West" }
]
}
];
Also, I think you will have to apply two levels of filters, first to get the contractors, and secondly to get each contractor's projects:
JSONSelect.forEach('object:has(.projects:has(.projectArea:val("North")))', contractors, function (contractor) {
$('body').append('<p>' + contractor.name + '</p>');
JSONSelect.forEach('object:has(.projectArea:val("North"))', contractor.projects, function (project) {
$('body').append('<p>' + project.project + '</p>');
});
});
I do not know of any other plugins that do this. If I were to come across this problem, my first inclination would be to solve it using jQuery's $.map and $.grep methods:
var filtered = $.map(contractors, function (contractor) {
var filteredProjects = $.grep(contractor.projects, function (project) {
return project['projectArea'] === 'North';
});
if (filteredProjects.length) {
return {
'name': contractor.name,
'projects': filteredProjects
};
}
});
I am using $.grep to filter each contractor's projects to only those with ['projectArea'] === 'North'. If the contractor has any projects that match the filter, a new object with the contractor's name and the filtered projects gets added to my filtered var.

Categories