Using updateFromJS is replacing values when it should be adding them - javascript

I have this code:
var attachmentsModel = {
convAttachments: ko.mapping.fromJS([])
};
$(function() {
ko.applyBindings(attachmentsModel)
refreshConvAttachments();
});
function refreshConvAttachments() {
$.ajax({
url: '/xxxxxxx/',
success: function (dataJS) {
// Send KO the data
ko.mapping.updateFromJS(attachmentsModel.convAttachments, dataJS);
}
});
}
The AJAX call above returns:
[{
"title": "BillGates",
"added_by": "xxx",
"thumb": "urlhere",
"id": 410,
"link": "/link/410",
"added_on": "2011-02-22T12:57:09-08:00"
}, {
"title": "biz-stone",
"added_by": "xxx",
"urlhere",
"id": 411,
"link": "/link/411",
"added_on": "2011-02-22T12:57:53-08:00"
}]
This works fine. Later though the user is able to add an attachment, and that's where it's breaking. While it adds the new attachment to the mode, and displays on the page, it removes all the previously loaded items in the attachmentsModel.convAttachments.
Later on, this happens:
ko.mapping.updateFromJS(attachmentsModel.convAttachments, file);
Ajax Returns:
[{
"title": "eric_schmidt",
"added_by": "xxx",
"thumb": "xxxxxx",
"id": 417,
"link": "/link/417",
"added_on": "2011-02-22T13:16:45-08:00"
}]
I hope that gives a clear walk through, if not please let me know. Any ideas why knockoutjs is kill everything when I use updateFromJS?

ko.mapping.updateFromJS() expects that you are receiving the complete list of items that was originally prepared with ko.mapping.fromJS(). Any items that are missing from the original are considered to be deleted and any new items in the updates are considered additions. So, currently the mapping plugin will not allow you to do incremental updates in this way.
If you are doing incremental updates, your best bet would be to locate the items that you need to update and either replace them completely or replace individual observables on each item.

you could always try using
$.extend(attachmentsModel.convAttachments,ko.mapping.fromJS(dataJS));
Although i am not quite sure if that will update all the bindings properly, you might have to reply the binding by calling
ko.applyBindings(attachmentsModel)

Related

Two Different JSON sources, Update at Different Times; Use Result in ng-Repeat List

I am trying to create a status list from two separate JSON sources. The list will display general info from the first source, and show a status color based on the number of people in the second source.
The first source contains general data that will not be changing much (i.e. feed name, version, description) and likely called only two times a day. See code example below:
/metadata
{
data: [
{
"feedName": "Feed 1",
"version": "000001",
"location": "1234 Main Street, New York, New York"
"description": "This feed gives information on the number of people in Building A at a given time."
},
{
"feedName": "Feed 2",
"version": "000001",
"location": "1000 Broad Street, New York, New York"
"description": "This feed gives information on the number of people in Building B at a given time."
},
{
"feedName": "Feed 3",
"version": "000001",
"location": "1111 Governor Street, New York, New York"
"description": "This feed gives information on the number of people in Building C at a given time."
}
]
}
The second source contains data on each feed that will change very often. This source will be called more frequently; about every hour.
/customers
{
data: [
{
"metricName": "Feed 1",
"customerNumber": "10",
"time": "2012-10-03 15:30:00"
},
{
"metricName": "Feed 2",
"customerNumber": "5",
"time": "2012-10-03 15:30:00"
},
{
"metricName": "Feed 3",
"customerNumber": "15",
"time": "2012-10-03 15:30:00"
}
]
}
Where metricName and feedName are actually the same values.
I've only dealt with one JSON source per list before, where my Javascript would look like this:
$scope.metadataList = function(){
$http.get('/metadata')
.then(function(result) {
$scope.metadata = result.data.data;
});
}
and corresponding HTML would look like this:
<ul ng-repeat = "data in metadata | orderBy: ['feedName']">
<li> {{data.feedName}}, {{data.version}} </li>
</ul>
So my question is, how to I make async calls to each data source? How do I match them up to populate my list with both the metadata information and the customer information?
Update
Before anyone asks, I did try ng-repeat = "data in metadata.concat(customers)" where "customers" defines the second data source, (as shown in Ng-repeat datas from 2 json Angular) but that only appends to the end of the list ... not quite what I was going for.
Thank you in advance.
First, you can call the all() method from the $q service to resolve multiple promises, in that case the two requests.
// both promises calling your api to get the jsons
var promises = {
metadataPromise: $http.get('/echo/json/', {}),
customersPromise: $http.get('/echo/json/', {})
};
$q.all(promises).then(function(response) {
metadata = response.metadataPromise;
customers = response.customersPromise;
joinMetadataAndCustomers();
}, function(response) {
// in case any promise is rejected
});
After that, you check for every feedName the metricName which matches it using filter. In the end, you can merge both with angular.merge.
var joinMetadataAndCustomers = function() {
metadata.data.forEach(function(mtd) {
customers.data.filter(function(customer) {
return customer.metricName === mtd.feedName;
}).forEach(function(customer) {
$ctrl.metadataAndCustomers.push(angular.merge({}, mtd, customer));
});
});
}
Not sure if it's the best approach, but it works. Of course can be improved according to your needs. For example, if you only have one single match, the last forEach can be avoided.
Fiddle with an example: https://jsfiddle.net/virgilioafonsojr/tv1ujet0/
I hope it helps.

Return JSON results as JS array with AJAX?

Apologies in advance - I am new to this and so other answers have not been able to help me.
I have used AJAX to send data to a PHP script (part of a 3rd party API). The PHP script returns the results as JSON, but I have no idea how to format these on my HTML page.
Ultimately, I would like to save the JSON results as an array and then use JS/Jquery to format them on the page.
I am not sure how to modify the PHP and AJAX scripts to achieve this. Can anyone point me in the right direction?
My AJAX:
$.ajax({
type: 'POST',
url: 'calculator.php',
data: {toPostcode: toPostcodeValue, parcelLengthInCMs: parcelLengthInCMsValue, parcelHeighthInCMs: parcelHeighthInCMsValue, parcelWidthInCMs: parcelWidthInCMsValue, parcelWeightInKGs: parcelWeightInKGsValue},
success: function(data) {
<!--NOT SURE WHAT TO PUT HERE-->
}
})
PHP (after the calculator does its thing - not sure if it needs to be changed):
$serviceTypesJSON = json_decode($rawBody);
echo json_encode($serviceTypesJSON);
The expected JSON results should look like:
{
"services": {
"service" : [
{
"code": "AUS_PARCEL_REGULAR",
"name": "Parcel Post (your own packaging)",
"speed": 2,
"price": 6.95,
"max_extra_cover": 5000,
"extra_cover_rule": "100,1.5,1.5",
"icon_url": "http://test-static.npe.auspost.com.au/api/images/pac/regular_post_box.png",
"description": "Based on the size and weight you've entered",
"details": "Check our ",
"delivery_time": "Delivered in up to 3 business days",
"ui_display_order": 1,
"options": {
"option": [
{
"code": "AUS_SERVICE_OPTION_STANDARD",
"name": "Standard Service",
"price": "0.00",
"price_type": "static",
"option_type": "optional",
"ui_display_order": 1
},
{
"code": "AUS_SERVICE_OPTION_SIGNATURE_ON_DELIVERY",
"name": "Signature on Delivery",
"price": 2.95,
"price_type": "static",
"option_type": "optional",
"tooltip": "Signature on Delivery provides you with the peace of mind that your item has been delivered and signed for.",
"ui_display_order": 2,
"suboptions": {
"option": {
"code": "AUS_SERVICE_OPTION_EXTRA_COVER",
"name": "Extra Cover",
"option_type": "optional",
"price_type": "dynamic",
"tooltip": "Extra Cover provides cover for loss, damage or theft of your item and will fully compensate you to the value specified for your item.",
"ui_display_order": 1
}
}
}
]
}
},
You can do two things, if the return data is JSON use dataType: "json" in the AJAX call.
Edit 1
If you are using dataType: "json". Which is more preferred if you are sure the data return is JSON string. data variable in the success will directly give you the JSON object. I think you can access it like data['services'].
success: function (data) {
jsonObj = $.parseJSON(data);
//this gives you the inner onject of the return data
servicesObj = jsonObj.services;
}
Or you can just get the data then use jQuery.parseJSON() to parse the data string into JSON object.
$.ajax({
type: 'POST',
url: 'calculator.php',
data: {
toPostcode: toPostcodeValue,
parcelLengthInCMs: parcelLengthInCMsValue,
parcelHeighthInCMs: parcelHeighthInCMsValue,
parcelWidthInCMs: parcelWidthInCMsValue,
parcelWeightInKGs: parcelWeightInKGsValue
},
success: function (data) {
jsonObj = $.parseJSON(data);
//this gives you the inner onject of the return data
servicesObj = jsonObj.services; //or jsonObj["services"]
}
})
Your success function will never be called if you are using
echo json_encode(); in your php script.
You should add dataType:'json' after type:'POST' and then your success function will get called and will get the result returned by server in data

AngularJS - parsing JSON feed with namespace

New to angular, and it is awesome.
One thing I am having a brain fart on is parsing a JSON feed that contains namespaces:
Example from JSON feed:
"title": {
"label": "Fuse"
},
"im:name": {
"label": "John Doe"
},
"im:image": [ {
"label": "70x70",
"attributes": {
"height": "55"
}
}, {
"label": "80x80",
"attributes": {
"height": "60",
"im:link": "www.google.com"
}
}, {
"label": "90x90",
"attributes": {
"height": "170"m
"im:link": "www.yahoo.com"
}
}],
I can successfully parse items without namespaces fine like so:
<p ng-repeat="item in results.feed['entry']">
title: {{item.title['label']}}
</p>
But cannot get the items with namespaces to display using:
name: {{item.['im:name']['label']}}
OR
name: {{item.['im-name']['label']}}
OR
name: {{item.['im->name']['label']}}
Since being a newbie, I thought something like this would work:
<div xmlns:im="http://www.aol.com" id="im-app" im-app="im">
<p ng-repeat="item in results.feed['entry']">
…namespace code in here…
</p>
</div>
But that did not help.
Extra bonus question: What if a namespace contains attributes, that also contain namespaces?
Any help would greatly be appreciated.
Thanks!
Roc.
Although Craig answered the question,
This is also for reference for others:
If you want to target a specific key inside of an object set:
"im:image":[
{
"label":google",
"attributes":{
"height":"55"
}
},
{
"label":"yahoo",
"attributes":{
"height":"60"
}
},
{
"label":"aol",
"attributes":{
"height":"170"
}
}
{{item['im:image'][2]['label']}}
Will get the 3rd key in that set.
Thanks.
Get rid of the dot after item
Working example: http://jsfiddle.net/bonza_labs/Kc2uk/
You access the properties exactly the same way as straight javascript (because angular is basically eval()-ing the expression as javascript**). item.['foo'] is not valid javascript. You are correct in using square-bracket notation as my:name is not valid for dot-notation.
valid:
item.foo
item['foo']
with non-standard property names:
item['foo:bar']
item['foo-bar']
and in your case:
{{item['im:name']['label']}}
** or close enough for understanding this solution

Backbone? Can.js? Ghetto DIY? How should I work with this data?

I'm working on an application that lets our security dispatchers update a page that contains current road and campus conditions. The backend is a nodejs/express stack with and the data is a simple JSON structure that looks something like this:
{
"campus": {"condition": "open", "status": "normal"},
"roads": {"condition": "wet", "status": "alert"},
"adjacentroads": {"condition": "not applicable", "status": "warning"},
"transit": {"condition": "on schedule", "status": "normal"},
"classes": {"condition": "on schedule", "status": "normal"},
"exams": {"condition": "on schedule", "status": "normal"},
"announcements" : "The campus is currently under attack by a herd of wild velociraptors. It is recommended that you do not come to campus at this time. Busses are delayed.",
"sidebar": [
"<p>Constant traffic updates can be heard on radio station AM1234. Traffic updates also run every 10 minutes on AM5678 and AM901.</p>",
"<p>This report is also available at <strong>555-555-1234</strong> and will be updated whenever conditions change.</p>"
],
"links": [
{
"category": "Transportation Links",
"links": [
{
"url": "http://www.localtransit.whatever",
"text" : "Local Transit Agency"
},
{
"url": "http://m.localtransit.whatever",
"text" : "Local Transit Agency Mobile Site"
}
]
},
{
"category": "Weather Forecasts",
"links": [
{
"url": "http://weatheroffice.ec.gc.ca/canada_e.",
"text" : "Environment Canada"
},
{
"url": "http://www.theweathernetwork.com",
"text" : "The Weather Network"
}
]
},
{
"category": "Campus Notices & Conditions",
"links": [
{
"url": "http://www.foo.bar/security",
"text" : "Security Alerts & Traffic Notices"
},
{
"url": "http://foo.bar/athletics/whatever",
"text" : "Recreation & Athletics Conditions"
}
]
},
{
"category": "Wildlife Links",
"links": [
{
"url": "http://velociraptors.info",
"text" : "Velociraptor Encounters"
}
]
}
],
"lastupdated": 1333151930179
}
I'm wondering what the best way of working with this data on the client side would be (e.g. on the page that the dispatchers use to update the data). The page is a mix of selects (the campus, roads, etc conditions), TinyMCE textareas (announcements and sidebar) and text inputs (links). I'm open to changing this data structure if necessary but it seems to me to work well. I've been looking at Backbone, and also Can.JS but I'm not sure if either of those are suitable for this.
Some additional information:
there's no need to update an individual item in the data structure separatly; I plan on POSTing the entire structure when it's saved. That said...
there's actually two different views, one for the dispatchers and another for their supervisors. The dispatchers only have the ability to change the campus, roads, etc conditions through drop-downs and furthermore can only change the "condition" key; each possible condition has a default status assigned to it. Supervisors can override the default status, and have access to the announcements, sidebar and links keys. Maybe I do need to rethink the previous point about POSTing the whole thing at once?
the supervisors need to be able to add and remove links, as well as add and remove entire link categories. This means that DOM elements need to be added and removed, which is why I'm thinking of using something like Backbone or Can.js instead of just writing some ghetto solution that looks at all the form elements and builds the appropriate JSON to POST to the server.
Suggestions welcomed!
CanJS works great with nested data. can.Model is inheriting can.Observe which allows you to listen to any changes in the object structure.
If you include can.Observe.Delegate you have even more powerful event mechanism (example from the docs):
// create an observable
var observe = new can.Observe({
name : {
first : "Justin Meyer"
}
})
var handler;
//listen to changes on a property
observe.delegate("name.first","set",
handler = function(ev, newVal, oldVal, prop){
this //-> "Justin"
ev.currentTarget //-> observe
newVal //-> "Justin Meyer"
oldVal //-> "Justin"
prop //-> "name.first"
});
// change the property
observe.attr('name.first',"Justin")

display json data using xhrget (DOJO)

I am unable to figure out what is the problem with displaying json data..below is the code
var xhrGet1 = dojo.xhrGet({
url: "Page/",
handleAs: "json",
handle: function(response)
{
dojo.byId('json-data').innerHTML = response.questions[0];
}
});
Html
<div id='json-data'></div>
And my json file looks like this
{
"Info": {
"PURPOSE": ".... ",
},
"questions": [
{
"ID": 1,
"Question": "User ID",
"Information": "",
}, {
"ID": 2,
"Question": "Name",
"Information": "",
}
],
so on...any ideas??
The property handleAs : "json" in your xhr call makes the incoming json automatically eval'ed to javascript objects. So, you have to convert your javascript object back to string using JSON.stringify.
e.g. :
dojo.byId('json-data').innerHTML = JSON.stringify(response.questions[0]);
You can also use dojo.toJson for the same purpose. It uses json.stringify but has the benefit of having a second argument ("prettyprint"), allowing you to pretty-print out of the box, like this :
dojo.byId('json-data').innerHTML = dojo.toJson(response.questions[0], true);
wrap your JSON with PRE and CODE tags.
So:
dojo.byId('json-data').innerHTML = "<pre>code>" + response.questions[0] + "</code></pre>";
Also see: Display JSON as HTML for some libraries that can help you pretty-format your JSON when rendering in the browser

Categories