KnockoutJS proper way to update observableArray AJAX - ajax

In KnockoutJS, what's the proper way to update an observableArray of JSON data each time an AJAX command is run?
Right now, I'm blanking the array using something like viewmodel.items([]), then repopulating it with the JSON data from the server. Short of using the KnockoutJS mapping plugin (which might be the only way to do this) what is the correct path?
My server logic is going to send some of the same data each time, so I can't just iterate and push the items into the array unless I want duplicates.
//// Adding how I'm doing it today ////
I'm not sure why I'm doing it this way, but this is just how I initially figured out how to update. So basically, like I said before, I get JSON data, then I pass it to something like this:
_model.addIncident = function (json) {
var checked = json.UserTouches > 0 ? true : false;
_model.incidents.push({
id: ko.observable(json.IncidentIDString),
lastTouchId: ko.observable(json.UserLastTouchIDString),
weight: ko.observable(json.Weight),
title: ko.observable(json.Title),
checked: ko.observable(checked),
createdOn: ko.observable(json.IncidentCreatedOn),
servicename: ko.observable(json.Servicename),
inEdit: ko.observable(false),
incidentHistory: ko.observableArray(),
matchScore: ko.observable()
});
};
for each node in the JSON array. As you can see, I've got some custom observables in there that get build with every passing piece of data. Maybe this is the wrong way to go, but it's worked great up until now.

An observableArray is really just a normal observable with some extra methods for array operations.
So, if you want to set the value of an observableArray to a new array, you can just do:
viewModel.items(myNewArray)
The mapping plugin can help you update the existing items in an array with any updates. In this case, your UI will only be updated from any differences.

I know I'm way too late on this one as I found myself stuck in this situation just recently. We can use a simple Javascript util function as a work-around.
If you have already marked _model.incidents as observableArray, you can do something like this when binding the returned JSON data:
eval("_model.incidents("+JSON.stringify(json)+");");
It worked for me. Hope you have created your observable like this:
_model.incidents = ko.observableArray([]);

Related

How can I get an immutable version of an object from store.getState()?

I'm using react-redux for a project I'm working on. I noticed that when I grab an object from my store and edit it, the object in state changes without me dispatching the change (but doesn't trigger a re-render on the components attached to that reducer's object). How can I stop state from changing without a dispatch?
For example if I do:
export function changeNeonGreenColourValue(colour) {
return (dispatch) => {
var neonColours = store.getState().colours.neon;
neonColours.green = colour;
dispatch(push('./home'));
};
}
And then in the layoutComponent I log:
console.log(this.props.state.colours.neon.green)
The output is still whatever I passed into changeNeonGreenColourValue() as "colour" but the page doesn't re-render to show that change. I know to get the page to re-render, all I have to do is dispatch the appropriate reducer case but I don't want the state object being altered at all unless I have an appropriate dispatch.
Apparently the 'standard' deep copying technique for solving this is to do a JSON stringifying and parse as so: const copiedObj = JSON.parse(JSON.stringify(sourceObj)); Unfortunately if you use this on large objects that will need parsing frequently you're going to have performance issues in your app as I do, if anyone has any suggestions for this I welcome them.
edit: so both jQuery and Loadash have their own implementations of deep cloning that are supposed to be better performance-wise:
https://lodash.com/docs/#cloneDeep
I personally implemented loadash to resolve my issue and it worked fine with little to no performance issues. I highly recommend it over JSON.stringify.

Apply a sort to a dataset in a PowerApps component (PCF)

I’m trying to create a new dataset type Powerapps Component (PCF). For the moment I am using it to display a view of the records that are available in an entity in Microsoft Dynamics CRM.
I wish to make the view sort itself when I click on the grid column headers (in a similar way that the default CRM grid view does). I'm trying to figure out how to apply a sort to the dataset so that I can refresh it as indicated by the documentation for the dataset.refresh() function:
Refreshes the dataset based on filters, sorting, linking, new column.
New data will be pushed to control in another 'updateView' cycle.
The dataset object does have a “sorting” property, but changing its value and then refreshing the dataset doesn’t seem to have any effect. After the refresh, the sorting property reverts to the value it had before I changed it.
In short, the click handler for the grid header does something like the following bit of code. The refresh gets done and my updateView() function gets called as expected but the sorting was not applied.
dataset.sorting = [{name: 'createdon', sortDirection: 1}];
dataset.refresh();
Any help on getting the dataset sorting to work would be appreciated.
I've been experimenting with PowerApps Component Framework a little bit recently and I can confirm that the following code won't be working:
dataSet.sorting = [ { name: "columnName", sortDirection: 0 } ];
However, I managed to get this one working for me:
dataSet.sorting.pop(); // you may want to clean up the whole collection
dataSet.sorting.push({ name: "columnName", sortDirection: 0 });
I haven't really figured out the reason of this behavior. The sorting array may be implemented as some form of observable collection in the background.
I hope this will guide you to a functioning solution.
The documentation is pretty abysmal here, but here is my best guess from putting a few different pieces of information together.
TLDR: I think there is some kind of extra method that needs to be called on the .sorting property, but I can't find out what it is called. Maybe something like:
dataset.sorting.setSorting({name: 'createdon', sortDirection: 1});
I think you're going to have to try a bunch of likely method names and see what works.
Background and links:
The only reference I could find to dataset.sorting was from here:
In this preview for canvas apps, only a limited set of filtering and sortStatus methods are supported. Filter and sort can be applied to dataset on primary type columns except for the GUID. Filter and sorting can be applied in the same way as in model-driven apps.To retrieve the dataset with filtering and sorting information, call
the methods in context.parameters.[dataset_property_name].filtering
and context.parameters.[dataset_property_name].sorting, then invoke
the context.parameters.[dataset_property_name].refresh().
So it seems that the .filtering and .sorting properties are handled similarly, and that there are some methods attached to them, and only some are supported. That is about as vague as they could make it...
I did find an example of how .filtering is used:
_context.parameters.sampleDataset.filtering.setFilter({
conditions: conditionArray,
filterOperator: 1, // Or
});
There is a brief reference to .setFilter() in the docs, as well as FilterExpression
There is a SortStatus reference, but it doesn't have any corresponding methods explicitly called out. It is possible that this is not yet a supported feature in the public preview, or the documentation is lacking and the name and syntax of the method you need to call on .sorting is not yet documented.

emberjs - sorting data retrieved from Firebase

I've spent the last three days trying to figure this out and i hope someone from here could help.
Note regarding setup: I'm working off Ember-Cli therefore my Ember version 1.13.7 and Ember-Data 1.13.8
To learn more about how emberJS works i decided to create a simple blog system that i will use on my own website. The data is loaded from a Firebase database using the Emberfire plugin.
Help: I cannot sort the data i retrieve into a descending order. For example, if i have 5 posts created on Monday, Tues, Wednesday, Thursday and Friday. Each with a unique timestamp in numerical order.
When i retrieve data it shows in this order:
1st ever post (oldest)
2nd ever post
3rd ever post
4th ever post
5th ever post (newest)
i want to reverse the order so my latest blog post appears on top.
I am retrieving data with the following code:
return this.store.find('post');
I can't work out how to use: sortBy which is from Ember itself or the orderBy() which is from firebase/emberfire?
I understand it needs to be inside a controller which will modify the data from the model before presenting it in the view. But can anyone help?
heres some more code:
Attempt at a controller - it doesn't work:
import Ember from 'ember';
export default Ember.ArrayController.extend({
sortedModel : function(){
return this.get('model').sortBy();
}.property('#each')
});
View Template:
{{#each sortedModel as |post|}}
{{#blog-post post=post}}{{/blog-post}}
{{/each}}
{{outlet}}
You need a property to order your posts by. Luckily, Firebase automatically provides such a property. Every item saved to Firebase is automatically given a timestamp at that moment.
In your post model, add a timestamp property like this:
//models/post
export default DS.Model.extend({
//
timestamp: DS.attr('number')
//
});
Now in your controller you can sort by it. You don't need the #each helper because sortBy already works on arrays.
//controllers/posts
import Ember from 'ember';
export default Ember.ArrayController.extend({
sortedModel : function(){
return this.get('model').sortBy('timestamps').reverse();
}
});
I think that should work, but let me know in the comments if you have any trouble!
Since you said your post array is already ordered but the wrong way.
You could use Array.prototype.reverse();
export default Ember.ArrayController.extend // Array and object c are deprecated.
export default Ember.Controller.extend({
sortedModel : function() {
return this.get('model').reverse();
}).property('model')
NB: If this does not work for you (your problem is bigger, but not explained here). I think you should handle the pagination logics in backend.
Instead of
this.store.find('post');
you should be using
this.store.find('post', { page: variable });

ParseReact: Observe queries depending on other queries?

This is more a question of practices than an issue (though I suppose it's an issue in that I don't know if this is possible) with the Parse-React library, but I have a special case where I need to make a query based off of info I get from another query. Ideally I want to do this in the same component. Is this possible?
e.g. the way I have my project setup right now using only Parse and React libraries (not Parse-React) is:
var QueryOne = new ParseQuery("QueryOne");
QueryOne.find({
success: function(results) {
var listOfThingsToGetFromQueryTwo = results.attributes.listOfThingsToGetFromQueryTwo;
var QueryTwo = new ParseQuery("QueryTwo").containedIn("id", listOfThingsToGetFromQueryTwo).find({
success: function(results) { /* do other stuff */ }
});
}
});
This works for a one-time query, but I'd like to use some of the more reactive features of Parse-React. So my question is, using Parse-React, can I make a component observe (in this example) QueryOne AND a QueryTwo depending on QueryOne?
I naively set out to implement this using by observing QueryOne in a parent component and passing that data as a prop to a child component, and observing QueryTwo from the child component, but I realize now that the observe function isn't triggered on prop updates so the query isn't rerun. Is there some way of triggering the query to rerun from componentWillUpdate?
I filed an issue directly on the parse-react github but haven't heard back. (https://github.com/ParsePlatform/ParseReact/issues/124) Would greatly appreciate any help! Thanks.
Got it! Turns out the observe function is passed newProps and newState as parameters, so if I make QueryOne in the parent container and pass the child the props, I can then use in the containedIn filter on the child with newProps and it works as expected.

I need a global event when data binding occurs in Knockout

I am a newbie to knockoutjs. I have searched examples and so far no luck. I have a page that is a data collection form with the values bound using knockout. What I am trying to do is provide the user with a flag letting him know data is modified and that it needs to be saved. In the app a user may pull down the form and display the data from the server and use it only as information. In other cases he may modify that data. I want to display a label that says something like "data has been modified" to the user once any binding has changed plus if he tries to navigate away from the page I want to warn him the changes will be lost. Is there some event I can subscribe to that tells me when any value has been changed in the model?
Thanks,
Terry
Take a look at Ryan Niemeyer's Dirty Flag. It might be what you are looking for. An example of his method can be seen in this jsFiddle.
this.dirtyItems = ko.computed(function() {
return ko.utils.arrayFilter(this.items(), function(item) {
return item.dirtyFlag.isDirty();
});
}, this);
More info can be found in this SO thread: Knockout isDirty example, using dynamic viewmodule from mapping plugin

Resources