Knockout load another AJAX url to combine to model - javascript

I have some complex question.
My first JSON url has this properties, ID, Name and Parameter. Then when I call the JSON, I want to go to retrieve another JSON URL based on the ID to get the child ID.
Then I want to output as Child ID, Parent Name and Parent Parameter.
Here is the jsFiddle http://jsfiddle.net/c3L7gr4w/1/
And here is the model url 1
[
{
"ParemeterValues": "Actual,2011,SYS.LoadCompanies,y,y",
"ID": "1771cdf7-7a73-49e4-8538-0d0cad965226",
"Name": "EXEC.Data.PLandBS.FromFMISMultipleCompanies",
},
{
"ParemeterValues": "Actual,2012",
"ID": "19439ce4-240c-4f2a-98ee-47cb1708b339",
"Name": "Data.BS.BringForwardOpeningBalances",
}
]
and the model url2
{
"ID": "1771cdf7-7a73-49e4-8538-0d0cad965226",
"Name": "EXEC.Data.PLandBS.FromFMISMultipleCompanies",
"TM1.ChoreProcessesProcess":
[
{
"Name": "EXEC.Data.PLandBS.FromFMISMultipleCompanies",
"ID": "dd1acc0b-51ff-4844-b6c4-c67640b326c4",
}
]
}

I think you need to adjust your dataMappingOptions.key so that KO can return a different key when you load your child model in on top of your original.
I have a working fiddle here, I think - http://jsfiddle.net/sifriday/c3L7gr4w/4/
dataMappingOptions now looks like:
var dataMappingOptions = {
key: function(data) {
var ChildID = "";
if (data["TM1.ChoreProcessesProcess"] != undefined)
ChildID = data["TM1.ChoreProcessesProcess"][0].ID
return data.ID + ChildID;
},
create: function(options) {
return new Person(options.data);
}
};
The problem with this is that when you load your updatedData KO will now use the mapping to destroy your original people and create new ones. However, updatedData is missing the ParemeterValue, so you need to merge this in from your original JSON. This will be OK even when you are using Ajax, because you can save the original JSON in a variable somewhere:
loadUpdatedData: function() {
// You need to merge in paremeterValues from your orig JSON
updatedData[0].ParemeterValues = data[0].ParemeterValues;
// Now good to go...
ko.mapping.fromJS(updatedData, dataMappingOptions, viewModel.people);
}
(I've also done it so that ChildID is appended as an extra property, but you should be able to see from this how to make it replace the original ID in both the People view-model and in the dataMappingOptions.key)

Related

With RESTful API, how I don't make error in the client side if specific response json level is undefined

here is a simple response json:
{
"blog": {
"title": "Blog"
"paragraphs": [{
"title": "paragraph1",
"content": "content1"
}, {
"title": "paragraph2",
"content": "content2"
}, {
"title": "paragraph3",
"content": "content3"
}
],
"selectList": ["food", "play", "mood"]
}
}
If something wrong with server, paragraphs property is undefined, how I avoid to make error in the client side? Even I still could get other datas successfully, like blog.title, blog.selectList...etc
if I don't want to use :
if (response.blog && response.blog.paragraphs && response.blog.paragraphs....) {
//do something
}
Because if there are many level in JSON object, it is not efficient way. Is anyone have smart and efficient way?
UPDATE
#MatthewHerbst said:just check the lowest property.
if I just check the lowest property(response.blog.paragraphs), that will make javascript error, crush my site. because the browser found response.blog is undefined. In the real case, if response.blog is undefined, I will get response.error property, then if(response.blog.paragraphs) will encounter error, because response object doesn't defined blog property in javascript. how I just check the lowest property?
You could use lodash for this. E.g.
let paragraphs = _.get(response, 'blog.paragraphs', null);
if (!paragraphs) {
// Couldn't get from response.
} else {
// Do something with response.blog.paragraphs.
}
You can do something like this-
var blogs = response.blogs;
if(blogs){
//do something
var paraData = blogs.paragraphs;
if (paraData){
// do some more fun
}
}
OR
You can do something like this-
var arrKeys = Object.keys(blogs),
newBlogArr = [] // to get the filtered result;
(function(){
for(i=0;i<arrKeys.length;i++){
// to get the filtered defined objects only
if(blogs[arrKeys[i]] !== null || blogs[arrKeys[i]] !== undefined){
//Now simply push that array in newBlogArr;
newBlogArr.push(blogs[arrKeys[i]]);
}
}
return newBlogArr;
})();

POST request to an array in JSON

I want to give a post request using my API. The JSON is like this:
{
"_id": "56b8e96xxxxxxxxxx7cd",
"name": "abc",
"conditions": [
{
"name": "Condition 1"
},
{
"name": "Condition 2"
}
],
"id": 10
}
The following is the angular code:
app.factory('Cohort', function($resource) {
return $resource('http://API URL:id')
});
I am using the following angular function to give the post request:
$scope.createCohort= function (){
var arr=["condition 1","condition 2"];
var cohort=new Cohort();
cohort.name=$scope.CohortName;
cohort.id=$scope.CohortId;
for(var i=0;i<length_of_arr;i++)
{
cohort.conditions[i].name=arr[i] // This line gives me error.
}
Cohort.save(cohort,function(){
});
};
The error is
Cannot read property '0' of undefined
How can I give the post request to it ?
Since you don't precise where you declare you var arr=[..]. I presume you have declare it inside a function and not in your controller which make it invisble to the createCohort function.
Either declare a var arr=[] in your controller (not in a function define in it) and remove the var on the place you set the value (so you don't mask it). Or use $scope.arr = [...];
Note that if you never use arr in your view template it's better to not store it in the $scope because angular watch evrery change of all fields in the $scope.
EDIT :
here is the problem: before the for loop add :
cohort.conditions = [];
and in the for loop do :
cohort.conditions.push({name:arr[i]});

ko.observableArray and JSON data massaging

I'm using web services to load data to client side. For binding purposes I need to expand on data that I get. I.e. I don't want to massage all data on server side.
For example, object Trip { Id: "123", Status: "P" }
In HTML I bind table to observableArray and want to display "Pending" instead of "P".
I'm coming from Silverlight/MVVM and usually you would use converter or just add new R/O property to object.
Not sure how this scenario should be handled in knockout.js
You may find here all you need :
http://net.tutsplus.com/sessions/knockout-succinctly/
Have a good read.
If you are just looking for a converter, computed observables are a good candidate.
var Tip = function(data) {
var self = this;
self.id = data.id;
self.status = ko.observable(data.status);
//You may prefer fullStatus, or statusName
self.statusConverter = ko.computed(function() {
return self.statusMap[self.status()];
});
};
Tip.prototype.statusMap = {
P: "Pending",
O: "Open",
C: "Closed"
};
which you can bind to like this:
<td data-bind="text: statusConverter"></td>
You can see it in this fiddle

Chrome extension and local storage

I'm trying to make a Chrome extension. For that extension, I need some info that is dynamically created, but I want that data to be added even later on (on a different page).
This is some sort of data that i want to be always accessible (when the plugin runs):
var villages = new Array();
villages[0][0] = "village1";
villages[0][1] = "1325848";
villages[1][0] = "village2";
villages[1][1] = "8744351";
villages[2][0] = "village3";
villages[2][1] = "8952187";
As you can see, the array is multi-dimensional. This because I want to store the names [0] and the village id 1 together.
Does anybody knows a good way of handling this problem?
I've looked at this: jQuery Cookie
But don't know if that is a proper way of handling the problem.
Alternatively do I have to create some kind of XML file that will contain all the values?
UPDATE:
This is a skeleton example, if you want to store just village.id and village.name, just change the default data, that still works.
I have changed all code for you, you will see how to iterate array and get villages data below code.
At first I should say that It's really bad practice to save data in a multidimensional array.
You should use object, it makes your data tidy, than you can manipulate it easily.
Here is an example object for your situation,
var village = {
id: "1325848",
name : "village1"
};
console.log(village.id); //1325848
console.log(village.name); //village1
This was a basic get started example.
Here is the solution for your problem with localstorage and javascript object.
var ExtensionDataName = "myfirstextensiondata";
var ExtensionData = {
dataVersion: 3, //if you want to set a new default data, you must update "dataVersion".
villages: []
};
//default data
ExtensionData.villages.push(
{id: "1325848", name: "village1"},
{id: "8744351", name: "village2"},
{id: "8952187", name: "village3"}
);
function DB_setValue(name, value, callback) {
var obj = {};
obj[name] = value;
console.log("Data Saved!");
chrome.storage.local.set(obj, function() {
if(callback) callback();
});
}
function DB_load(callback) {
chrome.storage.local.get(ExtensionDataName, function(r) {
if (isEmpty(r[ExtensionDataName])) {
DB_setValue(ExtensionDataName, ExtensionData, callback);
} else if (r[ExtensionDataName].dataVersion != ExtensionData.dataVersion) {
DB_setValue(ExtensionDataName, ExtensionData, callback);
} else {
ExtensionData = r[ExtensionDataName];
callback();
}
});
}
function DB_save(callback) {
DB_setValue(ExtensionDataName, ExtensionData, function() {
if(callback) callback();
});
}
function DB_clear(callback) {
chrome.storage.local.remove(ExtensionDataName, function() {
if(callback) callback();
});
}
function isEmpty(obj) {
for(var prop in obj) {
if(obj.hasOwnProperty(prop))
return false;
}
return true;
}
DB_load(function() {
//YOUR MAIN CODE WILL BE HERE
console.log(ExtensionData);
console.log(ExtensionData.villages); //array of villages
console.log(ExtensionData.villages[0]); //first village object
console.log(ExtensionData.villages[0].id); //first village id
console.log(ExtensionData.villages[0].name); //first village name
//HOW TO ITERATE VILLAGES
for (var i = 0; i < ExtensionData.villages.length; i++) {
console.log(ExtensionData.villages[i].id); //village id
console.log(ExtensionData.villages[i].name); //village name
}
});
QUESTIONS:
Does the ExtensionDataName to be the same? or can i change that?
ExtensionDataName is used as a name when your data is saved to localstorage, it's just a name of your data collection. Therefore of course you can change it, do what you want, it's up to you.
what is the goal that you achief when you change the number of this line:
dataVersion: 3, //if you want to set a new default data, you must update "dataVersion"?
At the first time when user run this extension there is no data in the localstorage. So default village list is used,
In my example default village list is,
[
{id: "1325848", name: "village1"},
{id: "8744351", name: "village2"},
{id: "8952187", name: "village3"}
]
this default list is saved to localstorage. After than when extension runs again(not first time), the default list is not important anymore, because there is a village list stored in localstorage, so it loads village list from localstorage.
For example if you want to add a new village during the runtime of extension you can do it like this,
ExtensionData.villages.push({id: "1215555", name: "village4"});
DB_save();
So what is the goal of dataVersion?
If you look DB_load() function, it's used there. It checks whether dataVersion is still same, If it's not same, It decides that
"There is a updated default data so I should clear localstorage and reload new data to localstorage"
So If you don't change this lines,
ExtensionData.villages.push(
{id: "1325848", name: "village1"},
{id: "8744351", name: "village2"},
{id: "8952187", name: "village3"}
);
Than you won't change dataVersion

How get data with jQuery.parseJSON

In page 1 I have this array:
$pole = array("countLike" => "countLike", "Message" => "Some message");
echo json_encode($pole);
And I want get this data on page 2, but this code doesn't work.
function Like(id)
{
$.post("page1.php", { action: "Like", "id": id }, function(data) {
var obj = jQuery.parseJSON(data);
$(".countLike#"+id).html(obj.countLike);
$(".Message#"+id).html(obj.Message);
});
}
Can you help me please with this code.
Thanks.
Pass json as expected output from the post request.
function Like(id)
{
$.post("page1.php", { action: "Like", "id": id }, function(data) {
//data is already a json parsed string
$(".countLike#"+id).html(data.countLike);
$(".Message#"+id).html(data.Message);
}, "json"); // <<<<< This is what you missed
}
Code should work making the request but there is an issue with your element selectors.
".countLike#"+id
".Message#"+id
The "#" is not space separated from the class name. Issue now becomes are you looking for elements with ID "#"+id? I suspect you are so you really don't need the class prefixing the ID at all and simply use
$('#' + id)
An Id selector is far faster for jQuery to search than class since there can only be one in the page.

Categories

Resources