ng-repeat is not updating when array is changed - angularjs

I have an ng-repeat that isn't updating upon changing the data in the array that it is using. I've researched for quite a while but nothing seems to be working. Initially, when the page loads, the ng-repeat displays the first page of a dataset, upon getting new data (the next page) and setting that array with this data, the ng-repeat isn't noticing the change and never populates with the updated array. It would be greatly appreciated if someone could steer me in the right direction on this.
gatekeeper.controller('businessController', ['$scope', 'siteService', function($scope, siteService) {
$scope.page = 1;
$scope.resultsPerPage = 50;
$scope.maxPaginationSite = 10;
$scope.pageCount = 0;
$scope.resultCount = 0;
$scope.getBusinessSites = [];
function getBusinessSites()
{
siteService.getBusinessSites($scope.page, $scope.resultsPerPage).then(function(response) {
$scope.getBusinessSites = response.data;
console.log($scope.getBusinessSites);
$scope.resultCount = response.data[0].QueryCount;
$scope.page = response.data[0].Page;
$scope.pageCount = Math.ceil($scope.resultCount / 50);
});
}
getBusinessSites();
$scope.pageChanged = function () {
$scope.page = this.page;
getBusinessSites($scope.page, $scope.resultsPerPage);
};
}]);
<tbody ng-controller="businessController">
<tr ng-repeat="site in getBusinessSites">
<td>{{ site.SiteName }}</td>
<td class="tableButton">
<button ng-controller="ModalCtrl" type="button" class="btn btn-default" ng-click="open('lg')">
{{ site.ClientName }}
</button>
<br />
<b>Facilities:</b>
No Data Yet
</td>
<td>{{ site.Subdomain }}</td>
<td>
<a href={{ site.URL}}> {{ site.URL}} </a>
<br />
<b>Go-live Date: </b> No Data Yet
</td>
<td>No Data Yet</td>
<td>{{site.ChannelPartner}}</td>
<td>No Data Yet</td>
<td>No Data Yet</td>
<td>No Data Yet</td>
<td>No Data Yet</td>
</tr>
<div >
<uib-pagination class="pull-right" boundary-link-numbers="true" max-size="maxPaginationSite" boundary-links="true" total-items="resultCount" ng-model="page" ng-change="pageChanged()"></uib-pagination>
</div>
</tbody>

The problem is that ng-repeat has referenced on this initial empty array [], when you change $scope.getBusinessSites, you change this variable's reference, but ng-repeat still reference on that empty array in memory.
So, solution is write data directly to array your ng-repeat reference. You can do it with angular.extend function:
Change this line:
$scope.getBusinessSites = response.data;
On this one:
angular.extend($scope.getBusinessSites, response.data);
UPD:
Also, if you use loading data not once, you'll need to clear previously loaded data in that array:
// empties an array
$scope.getBusinessSites.length = 0;

Try wrapping the tbody inside of a div and but the controller in the div:
<div ng-controller="BusinessController">
<tbody>
<tr ng-repeat="site in getBusinessSites">
.
.
.
</tbody>
</div>
and I suggest naming the $scope.getBusinessSites to $scope.businessSites for avoiding confusion :)

The ng-repeat create its own scope. Try adding $parent to reference your variable on your current controller scope

Related

data from component to view (Angularjs)

I am sure this is something simple, but I cannot seem to pass information from a component to a view. I search for this on stackoverflow and google but could not find a solution.
Here is the javascript component call to the API, which prints the information to the console,
component.js
$ctrl.getInformation = function() {
// call php controller.
// return audits for html file.
$http.get(`path/to/getInformation/${$ctrl.path.idTo}`).then(function(responseState) {
console.log('sucessfull callback from server', responseState.data);
return responseState.data;
});
}
View template.html file:
<tr ng-repeat="i in vm.getInformation">
<td> {{ vm.getInformation().data[i].Date }}</td>
<td> {{ vm.getInformation(responseState.data[i].Date) }} </td>
</tr>
Can anyone show me why is it not rendering the information from the component to the template file?
You need to assign your fetched data to a $scope variable or a variable in the controller then you can loop it. In your controller you need to change your code to
$ctrl.information = [];
$ctrl.getInformation = function() {
$http.get(`path/to/getInformation/${$ctrl.path.idTo}`).then(function(responseState) {
console.log('sucessfull callback from server', responseState.data);
$ctrl.information = responseState.data;
});
}
Then in your html
<tr ng-repeat="data in vm.information">
<td> {{ data.Date }}</td>
<td> {{ data.whatever) }} </td>
</tr>

Count clicks and add to input

First of all I am very new in Angular JS.
I have a list of items and by clicking on each one, it should be added to the table. The items are stored in a json file.
If the click event repeated several times the counter input which is located on the table must increases.
<ul class="list-inline" >
<li ng-repeat="food in foods" class="food_list">
<img class="img-box" src="images/{{ food.food_photo }}" ng-click = 'addRow(food)'><br/><span>{{food.food_name}}</span>
</li>
</ul>
<table class="table" id="table-right">
<tr>
<th>Item Name</th>
<th>Quantity</th>
<th>Price</th>
<th class="hidden-print">Delete</th>
</tr>
<tr ng-repeat="row in rows">
<td>{{row.food_name}}</td>
<td><input type="number" class="form-control" ng-model="row.food_count"></td>
<td>{{row.food_cost}}</td>
<td class="hidden-print"><button class="btn btn-info hidden-print" data-ng-click="removeRow($index)">Remove</button></td>
</tr>
app = angular.module('app',[]);
app.controller('MyCtrl', ['$scope','$http', function($scope, $http){
$scope.rows = [];
$scope.addRow = function(obj) {
$scope.foodname = obj.id;
$scope.foodprice = obj.price;
$scope.rows.push(obj);
$scope.counter++;
}
}]);
Could you please help me? Thank You.
First you have to understand that food_count property of a row object is the variable that should be updated on repetitive clicks. Updating any other $scope variables won't change row specific data because your view is bound to $scope.rows object.
Your addRow function should look like this.
$scope.addRow = function(obj) {
if($scope.rows.indexOf(obj) >= 0){ // if this obj already exist
$scope.rows[$scope.rows.indexOf(obj)].food_count++;
}
else
$scope.rows.push(obj);
}
Then the objects of $scope.foods should have a property called food_count to display.
$scope.foods = [
{food_name:'Jani',food_cost:'10', food_count:0},
{food_name:'Hege',food_cost:'8',food_count:0},
{food_name:'Kai',food_cost:'5',food_count:0}]
solution

how can I keep some values in a ngshow and ng hide variable so that $digest does not initialize them again and again in angularjs?

I am displaying some data in a web app that I am getting from a SQL database using a http service. I want to be able to modify the information of that data on the same table where the data is shown. I am using angularjs and I am using the directives ng-show and ng-hide on the table. Here I show the part of the code I am talking about.
<table>
<tr>
<td><font size = 4>Servicio: </font> </td>
<td> </td>
<td>
<select class="col-sm-12" name="repeatSelect" id="repeatSelect" ng-model="currentItem.ServicioId">
<option ng-repeat="option in items.Servicio" value="{{option.Id}}"> {{option.Nombre}}</option>
</select>
</td>
</tr>
</table>
<h4>Cliente: {{getNameC(currentItem.ServicioId)}}</h4>
<br />
<button class="btn btn-primary" ng-click="editOrCreate()">Nuevo</button>
<button class="btn btn-primary" ng-click="list()">Actualizar</button>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Cantidad</th>
<th>Tipo Concepto</th>
<th>Descripción</th>
<th>Precio Unitario</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in itemsSC">
<td>
<div ng-hide="editingData[item.Id]">{{item.Cantidad}}</div>
<div ng-show="editingData[item.Id]"><input ng-model="currentItem.Cantidad" /></div>
</td>
<td>
<div ng-hide="editingData[item.Id]">{{getName(item.ServicioConceptoTipoId)}}</div>
<div ng-show="editingData[item.Id]">
<select name="repeatSelect" id="repeatSelect" ng-model="currentItem.ServicioConceptoTipoId">
<option ng-repeat="option in items.ServicioConceptoTipo" value="{{option.Id}}"> {{option.Nombre}}</option>
</select>
</div>
</td>
<td>
<div ng-hide="editingData[item.Id]">{{item.Descripcion}}</div>
<div ng-show="editingData[item.Id]"><input type="text" ng-model="currentItem.Descripcion" /></div>
</td>
<td>
<div ng-hide="editingData[item.Id]">{{item.PrecioUnitario}}</div>
<div ng-show="editingData[item.Id]"><input ng-model="currentItem.PrecioUnitario" /></div>
</td>
<td>
<button class="btn btn-xs btn-primary" ng-hide="editingData[item.Id]" ng-click="editOrCreate(item)">Modificar</button>
<button class="btn btn-xs btn-primary" ng-show="editingData[item.Id]" ng-click="saveEdit(currentItem,0)">Actualizar</button>
<button class="btn btn-xs btn-primary" ng-hide="viewField" ng-click="delete(item)">Eliminar</button>
</td>
</tr>
</tbody>
<tfoot id="total">
<tr>
<td colspan="3" class="text-right">Total:</td>
<td class="text-right">
{{total(currentItem.ServicioId) | currency}}
</td>
</tr>
</tfoot>
</table>
On my controller I am using object arrays to get the information from the databases and I am using a mirror array to keep the boolean values of the data shown on the table, so depending on the boolean value the tables shows the data or get an input for the user to modify the data.
I get the mirror array once I know the service and client related to the data I want to show and which is possible to be modified.
However, when I click on the button to modify the status of the ng-hide or ng-show object named editingData, the controllers modifies it, but the $digest run all the other functions related to the bindings of the view and returns the bool object array to its inital values.
I haven´t been able to find away to go around this normal working way of the $digest and $watches so my $editingData object is not modified to its initial values. Here I show the code of the controller related to this.
angular.module("App")
.controller("ConceptoCtrl", function ($scope, $http, $resource, serviciosConceptoFactory, serviciosConceptoTipoFactory, serviciosFactory, clientesFactory) {
$scope.list = function () {
$scope.items = serviciosConceptoFactory.query();
$scope.itemsT = serviciosConceptoTipoFactory.query();
$scope.items.ServicioConceptoTipo = $scope.itemsT;
$scope.itemsS = serviciosFactory.query();
$scope.items.Servicio = $scope.itemsS;
$scope.itemsC = clientesFactory.query();
$scope.itemsS.Cliente = $scope.itemsC;
}
//some other functions
$scope.editingData = {};
$scope.getitemsSC = function (item) {
$scope.itemsSC = [];
for (var j = 0; j < $scope.items.length; j++) {
if ($scope.items[j].ServicioId == item) {
newitem = $scope.items[j];
$scope.itemsSC.push(newitem);
}
}
for (var i = 0; i < $scope.itemsSC.length; i++) {
$scope.editingData[$scope.itemsSC[i].Id] = false;
}
}
$scope.editOrCreate = function (item) {
$scope.currentItem = item;
$scope.editingData[item.Id] = true;
}
$scope.list();
});
The {{getNameC(currentItem.ServicioId}} function shown on the html file is the one that calls the $scope.getItemsSC(item) function once it knows the services and client related to the data that will be shown and it is this function the one that initializes the boolean values for the $scope.editingData mirror array depending on the Id of the item.
The $scope.editOrCreate(item) function is the one that changes the boolean object of the specified item so that the view shows the input element for the user instead of the data. However, $digest re-runs the $scope.getItemsSC(item) function because the $watches were modified, and that´s what I want to avoid, because in this case the input elements are never shown.
I thank in advance any help provided
Instead of using the HTML to call functions when data is a available, use the $promise property attached to the resources.
$scope.list = function () {
$scope.items = serviciosConceptoFactory.query();
$scope.itemsT = serviciosConceptoTipoFactory.query();
$scope.items.ServicioConceptoTipo = $scope.itemsT;
$scope.itemsS = serviciosFactory.query();
$scope.items.Servicio = $scope.itemsS;
$scope.itemsC = clientesFactory.query();
$scope.itemsS.Cliente = $scope.itemsC;
var promiseArray = [$scope.items.$promise,
$scope.itemsT.$promise,
$scope.itemsS.$promise,
$scope.itemsC.$promise
];
$q.all(promiseArray).then(function(resultsArray) {
var items = resultsArray[0];
var itemsT = resultsArray[1];
var itemsS = resultsArray[2];
var itemsC = resultsAttay[3];
//create mirror array here
//create editingData array here
});
};
By using the .then method of the promises, the processing of the data can be delayed until it arrives from the server. This way there is no need for the HTML to call functions.

How do i access the data in json object in Angularjs

I am trying to access a the data in a json object using angular but i finding it difficult to this at the moment and hopefully you can help me here.
so i have a function in my controller like this:
var vm = this;
vm.getData = getData;
vm.data = [];
function getData() {
var promise = appService.getAll(vm.query);
promise.then(function(response) {
vm.data = response.data,
console.log(vm.data);
},
function(error) {
$log.error("an error here", error);
});
}
and my view goes something like this:
<div>
<table class="table">
<tr >
<th> Department</th>
</tr >
<tr ng-repeat="n in vm.data">
<td>{{n.sectionName}} </td>
</tr>
</table>
</div>
Everything works .. i retrieve my json object as required .. but its just accessing them is where i am having in issue as the above context in my view is not dispplayed ..
so in my example above i am trying to acess a json with the heaser of "sectionName" and displaying it in my view.
here is a visual of of the json object in my console ...
Thank you for your time
If results is the data you need to display, then you should adjust your markup to be this:
<tr ng-repeat="n in vm.data.results">
<td>{{n.sectionName}} </td>
</tr>
You are looping over the object itself. Instead, you have to loop over the results array inside the object.
<div>
<table class="table">
<tr >
<th> Department</th>
</tr >
<tr ng-repeat="n in vm.data.results">
<td>{{n.sectionName}} </td>
</tr>
</table>
</div>

Search in angularjs not working properly with angular bootstrap ui

i am using angular-ui bootstrap for pagination of a table and i have separate search input tag, the search input only searches the first set data from the paginated list, it does not search the subsequent paginated pages of the table, how do i search from the input for all the data.
HTML
<div ng-controller="IndexCtrl" >
<input class="form-control formcustom" id="exampleInputEmail2" ng-model="custquery" placeholder="Search for Tripsheets" autofocus>
<table class="table table-striped table-bordered table-condensed table-hover">
<tr ng-repeat="customer in customers | orderBy:'id':true | filter: paginate | filter: custquery">
<td>{{customer.id}}</td>
<td>
<span>{{customer.name}}</span>
</td>
<td>
<span>{{customer.address}}</span>
</td>
<td>
<span>{{customer.phone1}}</span>
</td>
<td>
<span >{{customer.phone2}}</span>
</td>
<td>
<span >{{customer.phone3}}</span>
</td>
<td>
<span>{{customer.phone4}}</span>
</td>
<td>
<span >{{customer.email}}</span>
</td>
</tr>
</table>
<pagination total-items="totalItems" ng-model="currentPage"
max-size="5" boundary-links="true"
items-per-page="numPerPage" class="pagination-sm">
</pagination>
</div>
JS
app.controller('IndexCtrl', function ($scope, customerFactory, tripsheetFactory, driverFactory, notificationFactory) {
$scope.customers = [];
$scope.addMode = false;
customerFactory.getCustomers().then(function(data){
$scope.customers = data.data;
$scope.totalItems = $scope.customers.length;
$scope.currentPage = 1;
$scope.numPerPage = 20;
$scope.paginate = function(value)
{
var begin, end, index;
begin = ($scope.currentPage - 1) * $scope.numPerPage;
end = begin + $scope.numPerPage;
index = $scope.customers.indexOf(value);
return (begin <= index && index < end);
};
});
});
customerFactory is the factory i created to fetch json data
The position of the customer in $scope.customers doesn't change so the paginate function will always filter out all but the first page of the full customers array. What you need is an intermediate result (another array) that holds the customers that pass the $scope.custquery filter. The paginate function needs to operate on that second, filtered array.
I couldn't see a way to do that declaratively so I injected filterFilter into the controller and added a watch on $scope.custquery to execute it.
I put together a plunk that shows the result.

Resources