Angular view not refreshing after the array updates from $http - angularjs

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.

Related

unable to update controller model value from custom directive with controller as syntax

I am facing problem to update my model values from custom directive using controller as syntax. I can clearly see that through console.log that the values are getting changed in the directive however they are not getting updated in the controller. <h4>{{self.tabs}} </h4> always shows same values.
In addition I want to do watch on controller (self --> tabs) model to add 'active class' but when I try to use watch it is throwing me out error. So I have commented that part. The following is the watch related code, which is not working:
$scope.$watch('tabsCtrlself.tabs.index', function() {
if (tabsCtrlself.tabs.index === index) {
angular.element($element).addClass('active');
}
});
Please find my plunker, Can any one direct me to fix this?
you can use 'ng-class' attribute,eg:
<en-tab data-pane="myPaneName1" ng-class="{active:index==1}">Tab #1</en-tab>
<en-tab data-pane="myPaneName2" ng-class="{active:index==2}" >Tab #2</en-tab>
<en-tab data-pane="myPaneName3" ng-class="{active:index==3}">Tab #3</en-tab>
'index' is a object in angular: $scope.index, initializes its value:
$scope.index=1;
The first item is selected by default.
I think the object of 'tabsCtrl.tabs.index' is not a angular object, so can't auto refresh

ui-bootstrap pagination with filter

after some research and study of examples I implemented a pagniation with a filter function.
Im very new to angular, so I need your help if this application is ok or it has some bugs/logical errors.
The target is to select a collection (in this application load1 or load2) and create new objects, manipulate existing, or delete some of them. On every update of the data, it has to be checked if the pagination is synchronous to the collection size.
If the user enters something into the search field, a watcher in the controller is fired for updating the filtered data:
$scope.$watch('search.name', function (newVal, oldVal) {
$scope.filtered = filterFilter($scope.items, {name: newVal});
}, true);
I would be very happy if some of you angular pros can look into this code and give me some feedback. I want to use this in a productive system, so every answer would be great!
Here is a working plunkr: http://plnkr.co/edit/j9DVahEm7y1j5MfsRk1F?p=preview
Thank you!
Watchers are heavy if you use them explicitly throughout your large application.
Use ng-change instead. Also, by passing true to that watcher means you're deep watching which is really a bad thing to do, since it will check each property of the object in the array which is performance intensive.
Since I can't see that you need old and new value for a reason, you can simply use $scope.search.name. Whenever you type in something, $scope.search.name has the updated value. Just need to call a function on ng-change.
DEMO: http://plnkr.co/edit/TWjEoM3oPdfrHfcru7LH?p=preview
Remove watch and use:
$scope.updateSearch = function () {
$scope.filtered = filterFilter($scope.items, {name: $scope.search.name});
};
In HTML:
<label>Search:</label> <input type="text" ng-model="search.name" placeholder="Search" ng-change="updateSearch()" />
Previous answer is still the correct, but you will have to make sure to replace the "page" inside the pagination tag and change it to ng-model.
From the changelog (https://github.com/angular-ui/bootstrap/blob/master/CHANGELOG.md)
Since 0.11.0:
Both pagination and pager are now integrated with ngModelController.
page is replaced from ng-model.

Sorting in Angular

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

ng-table , getData called more than once, why?

For some reason when getData uses angular resource to bring the data it is being called twice, causing the resource to do it REST request twice too <--- bad...
Any idea why and how to solve it?
Here a working testcase/plunker example that recreates this scenario (look at the browser console - "getData being called...." displayed twice ) b.t.w as you can see I'm not really using the resource to bring real data, just to demonstrate the scenario, In my real app I do use the resource to bring real data and its being called twice just like in this example,
Thanks ahead
After looking into the src of the ng-table I noticed the following
$scope.$watch('params.$params', function(params) {
$scope.params.settings().$scope = $scope;
$scope.params.reload();
}, true);
Which means that the tables calls it 'getData' on count/filter/group/groupBy/page/sorting
which explains the behavior I was seeing.
When you call params.count(...) you ask ng-table to refresh data as you change page size. That's why you have two get-data calls.
If you don't want to have paging, then remove calls params.count and params.total.
If you need paging, then set page size and do not change it in getData.
This happened to me with a weird reason. getData get called twice on init (first load) only. changing page or sorting didn't call getData twice. The reason was that at init the ng-table directive was hidden in the template file.
Thank #Alexander Vasilyev. I understood my problem as you said. I want to explain a litte more here. In fact, the object "params" is the object configuration the table ng-table, then if "params" changed (ex: count or a property of the object), ng-table will invoke function getData() to refresh table.
In my case, I want to get information in the object "params" and change it but I dont want to refresh ng-table. I did it by cloning object "params" et work his object copied. Clone the object in JS with jQuery :
var resultParams = jQuery.extend(true, {}, params.$params);
And then, I will work on the object resultParams instead of "params" original.

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

Resources