Sorting in Angular - angularjs

I am building an app using Angular.
I have JSON that comes back from the server sorted by date.
I bind to this data in the view.
A user may change what data is displayed in the view. This kicks off a request and the view gets updated as it is bound to the JSON data changes.
When Angular gets this JSON data it then sorts it alphabetically.
I want to maintain the sort order that comes from the server.
I googled this a bit and found the following solution...
<div ng-repeat="key in notSorted(myData)" ng-init="data = myData[key]">
"notSorted" is a function on the controller...
$scope.notSorted = function(obj){
if (!obj) {
return [];
}
return Object.keys(obj);
}
This works fine initially. The data displays in the correct order.
However it breaks the binding. So when the user tries to change the data being displayed, the view does not get updated as notSorted does not see the binding change.
I can think of a few hacky ways around this such as manually firing the updates but I would like to do this the correct angular way.
Anyone know what that is?
Thanks

I believe you need this:
https://docs.angularjs.org/api/ng/filter/orderBy
It works sort of like this:
<div ng-repeat="item in items | orderBy:'key'">

Because of the way the data was coming back from the server and how I needed to iterate over that data I wasn't able to get any of the "orderBy" or filter solutions to work.
However I discovered that in Angular 1.4 they removed the default ordering (alphabetical/ascending) in favour of preserving the order the data was returned from the server...
http://jaxenter.com/angular-releases-1-3-update-1-4-beta-113906.html
I was using 1.3.x and simply updated to 1.4 and it worked OOTB.
Thanks

Related

Angular view not refreshing after the array updates from $http

I've the following code that displays table of data using ng-repeat, initially the model (array) will have no data, but upon request to an api it receives some data that i'm trying to update my model with, but unfortunately my view is not getting refreshed with model updates though I see that my model contains the new data that was received from api call.
Component code:
app.component('fooList', {
bindings: {
foo: '<'
},
templateUrl: '/app/foo.html',
controllerAs: 'list',
controller: function ($http,$scope) {
let list=this;
list.search=()=>{
$http.get('/ui/foo/'+ list.searchCriteria)
.success(response=>{
list.foo.searchResults=response.searchResults;
})
}
}
});
HTML View:
<tr ng-repeat="foo in ::list.foo.searchResults">
<td>{{::foo.name}}</td>
<td>{{::foo.address}}</td>
</tr>
I've tried the following so far which didn't help
Setting the list.foo.searchResults to undefined initially, which is working fine for the first time, but if I send a different search criteria then again it is not refreshing.
$scope.$apply(), which I hate to do, but tried which is throwing digest in progress error.
Inside the success of $http i've tried the below code
list.foo.searchResults=0;
list.foo.searchResults.push.apply(list.foo.searchResults,response.searchResults);
Created one more child component and replaced my html with the child template and moved the code to populate the array in to that component.
Any help is really appreciated.
You have to remove one time data binding.
<tr ng-repeat="foo in list.foo.searchResults">
<td>{{foo.name}}</td>
<td>{{foo.address}}</td>
</tr>
Try removing :: or can you give a jsfiddle to work on
:: is used to provide one way databinding , please remove it and then try
::if you are using this, even if your model is getting updated it will not allow to update view.
You may try to update in the view section while you are fetching data from service, use $scope.$apply() in your controller section, where your are taking value from service(means storing in array).
or remove :: from your html part because its give to one way binding in angularjs.

How does AngularJS handle Memory

I am wondering how AngularJS 'saves' its data/model. Does it actually save it or.. how does it work?
We are using different methods to retrieve JSON data. In other frameworks like jQuery we had to think about how to store data locally, i.e. when we want to provide a sorting possibility. In Angular this seems different, it seems to do all that for us out of the box.
Is it that Angular displays everything how it is supposed to be and looks at changes, reads in the displayed data in and then displays it differently or does it use a local storage to save the raw json.. and work from there? (This would limit the amount of data we can feed)
Here is a simple code-example:
$http.get("url-to-json")
.success(function(returnedData) {
$scope.search_result = returnedData['search_result'];
})
From there I can just use:
<div ng-repeat='result in search_results | sortResult:"price":sorted' id="res_<% result.id %>" class="result">
Product: <% result.name %>
</div>
I am riddled how Angular still knows the data and doesn't have to load it again from the external source.
Do you know?
There is a lot more that goes into it, but essentially its all stored in local memory. Angular creates an object of all your scope properties. When you do data binding in angular you are registering an event listener and when that event is called angular loops through this object detecting if something has changed, and if so updates the object accordingly. Each time an update occurs it returns to the loop to check if anything else has been updated. This is what is referred to as the $digestLoop.
SOURCE
The ng-book

Angular UI Sortable get index after updated

I really dislike how angular-ui is documented. Sometimes they really don't explain a lot. This is the documentation to sortable-ui: https://github.com/angular-ui/ui-sortable
First, I cannot pass in options.
$scope.sortableOptions = {
cursor:"move"
};
I also changed "move" to "pointer" or "crosshair". Nothing happens.
Second, I need to update the backend by the new order of which the user has sorted. I am not a great javascripter at all (more of a back end developer). The only order-related js function I can find is indexof(). Then it gets very complicated because I need to iterate through all elements and find the new order since the user has rearranged all the elements.
Is there an easier way to get the current order of the list whenever the sortable directive is updated?
I created a demo on plunker (since it allows me to add extra libraries)
http://plnkr.co/edit/uNErHgKL3ohNyFhgFpag?p=preview
Again the cursor part is not working, and I have no idea how to get the order of these items.
I see there are methods on the Sortable UI page...I'm new to angularJS. I just couldn't figure out how to call these methods within AngularJS code.
Seralize method/toArray might not be a good idea..The actual data I'm dealing with does not look like ["one", "two", "three"]. It's more like:
[{"id":"5","article_name":"New Article
Title","article_order":"1","article_author":"Author","article_body":"Start typing your
article here!","is_visible":"1","created_date":"2013-10-27
05:37:38","edit_date":null,"magazineID":"7"},
{"id":"13","article_name":"New Article
Title","article_order":"2","article_author":"Author","article_body":"Start typing your
article here!","is_visible":"1","created_date":"2013-10-27
05:45:10","edit_date":null,"magazineID":"7"}]
If you guys look into this data stream..there is one attribute called article_order. This is the attribute (database column) I am trying to modify...
Read the jQuery UI Sortable docs. There are lots of events you can bind to and methods for serializing the sorted elements. Within the event callbacks you want to use you can make ajax calls to server with updated data
This angular module is simply a wrapper for jQuery UI Sortable.
Create a demo in jsfiddle or plunker that shows the problems you are having
If you use my new sortable Angular.js directive, you would do it like this:
$scope.items = [{"id":"5","article_name":"New Article
Title","article_order":"1","article_author":"Author","article_body":"Start typing
your article here!","is_visible":"1","created_date":"2013-10-27
05:37:38","edit_date":null,"magazineID":"7"},
{"id":"13","article_name":"New Article
Title","article_order":"2","article_author":"Author","article_body":"Start typing
your article here!","is_visible":"1","created_date":"2013-10-27
05:45:10","edit_date":null,"magazineID":"7"}];
$scope.onChange(fromIdx, toIdx) {
$scope.items[fromIdx].article_order = toIdx;
$scope.items[toIdx].article_order = fromIdx;
// OR
// var temp = $scope.items[fromIdx].article_order;
// $scope.items[fromIdx].article_order = $scope.items[toIdx].article_order;
// $scope.items[toIdx].article_order = temp;
}
HTML:
<ul ng-sortable="items"
ng-sortable-on-change="onChange">
<li ng-repeat="item in items" class="sortable-element" ng-style="{backgroundColor: item.color}">
{{item.name}}, {{item.profession}}
</li>
</ul>
See demo + documentation here:
https://github.com/schartier/angular-sortable
https://github.com/schartier/angular-sortable-demo
I guess I got into an issue similar to you. If we subscribe the update callback we don't get the latest order of items. But using the stop event handler helped me. While using angular ui sortable, we get the model updated with the latest order in the stop event handler. You can post this data to backend or wherever you want to store..Hope this helps...:)
You can refer the jquery ui sortable documentation for stop here
http://api.jqueryui.com/sortable/#event-stop

Having a set of checkboxes map to a nested array

I am working on a SPA that pulls in customer data from one $resource call, and gets some generic preference data from another $resource call.
The preference data is sent as an array, which I want to use to populate a series of checkboxes, like so:
<div ng-repeat="pref in fieldMappings.mealPrefs">
<input type="checkbox"
id="pref_{{$index}}"
ng-model="customer.mealPrefs"
ng-true-value="{{pref.name}}" />
<label class="checkbox-label">{{pref.name}}</label>
</div>
When a user clicks one or more checkboxes, I want the values represented in that array of checkboxes to be mapped to an array nested inside a customer object, like so:
.controller( 'AppCtrl', function ( $scope, titleService, AccountDataService ) {
// this is actually loaded via $resource call in real app
$scope.customer = {
"name": "Bob",
"mealPrefs":["1", "3"]
};
// this is actually loaded via $resource call in real app
$scope.fieldMappings.mealPrefs = [
{'id':"1", 'name':"Meat"},
{'id':"2", 'name':"Veggies"},
{'id':"3", 'name':"Fruit"},
{'id':"4", 'name':"None"}
];
});
I have tried setting up ng-click events to kick off functions in the controller to manually handle the logic of filling the correct part of the customer object model, and $watches to do the same. While I have had some success there, I have around 2 dozen different checkbox groups that need to be handled somehow (the actual SPA is huge), and I would love to implement this functionality in a way that is very clean and repeatable, without duplicating lots of click handlers and setting up lots of $watches on temporary arrays of values. Anyone in the community already solved this in a way that they feel is pretty 'best practice'?
I apologize if this is a repeat - I've looked at about a dozen or more SO answers around angular checkboxes, and have not found one that is pulling values from one object model, and stuffing them in another. Any help would be appreciated.
On a side-note, I'm very new to plunkr (http://plnkr.co/edit/xDjkY3i0pI010Em0Fi1L?p=preview) - I tried setting up an example to make it easier for folks answer my question, but can't get that working. If anyone wants to weigh in on that, I'll set up a second question and I'll accept that answer as well! :)
Here is a JSFiddle I put together that shows what you want to do. http://jsfiddle.net/zargyle/t7kr8/
It uses a directive, and a copy of the object to display if changes were made.
I would use a directive for the checkbox. You can set the customer.mealPrefs from the directive. In the checkbox directive's link function, bind to the "change" event and call a function that iterates over the customer's mealPrefs array and either adds or removes the id of the checkbox that is being changed.
I took your code and wrote this example: http://plnkr.co/edit/nV4fQq?p=preview

AngularJS pattern for persisting record selection through multiple asynchronous calls?

We're working on CRUD patterns for a fairly large application. One common UI pattern we're using to define a one-to-many relationship where a record is associated by checkbox. The challenge is to persist selections (checked/unchecked) through asynchronous calls (search/sort) that refresh the record list (and the associated ng-model). I'd like to hear from more advanced AngularJS users (I'm a noob) what's considered a best practice for this? Any feedback is appreciated!
EDIT
Here's a working plunk showing how I'd most likely tackle this with my current understanding of Angular. Please let me know if you have a better approach!
I think u could maintain a separate collection of selected names. So the next time you filter the list, you just need to lookup in the collection in order to keep the item selected. That you can do by binding some variable (arrSelected) in the controller or you can create a separate service also.
Have you considered using a filter instead of refetching. Of course this really depends on how many items you plan on having, but I've had success filtering a nested JSON object with around 5000 items.
Here is the full plunker : http://plnkr.co/edit/l4jYgt0LjRoP2H0YuTIT?p=preview
Highlights:
In index.html for your repeat you add | filter
<tr ng-repeat="user in users | filter:userFilter">
In script.js we add the filter function and a $scope variable to hold the filtered letter:
$scope.filteredLetter
$scope.userFilter = function (elem) {
if (elem.name.lastIndexOf($scope.filteredLetter, 0) === 0 || $scope.filteredLetter == '') {
return true;
} else {
return false
}
}
As a bonus I added in ng-class to show which letter was highlighted.
Pretty simple code but this gives you full persistence now even as people change things. You can even experiment now with adding a <input> tag with an ng-model binding to say $scope.filteredName. Then all you need to do is add the JS to the filter to do a deeper filter for part of the name.

Resources