AngularJS data binding with scope functions - javascript

I'm new in Angular (i use 1.6)
I'm facing with data binding for the first time and I can load and refresh my data on a page simply by calling the web service every 3 seconds.
But I can't figure out how to tell to angular to call also the scope functions.
When the page load for the first time I load fake data and for every row I get a call to my scope functions job.rowColor and job.rowIcon that return CSS classes based on row status.
But when the interval expires and I load fresh data from server i see my new data load on page but without CSS classes computed from my scope function.
Why?
Am I miss something?
This is my html:
<div class="container" ng-controller="JobController as job">
<div class="d-flex flex-wrap">
<table class="table table-striped">
<thead>
<tr>
<th scope="col">Status</th>
<th scope="col">User</th>
<th scope="col">Report</th>
<th scope="col">Type</th>
<th scope="col">Files</th>
<th scope="col">Output</th>
<th scope="col">Start</th>
<th scope="col">End</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in job.data track by $index" class="table-{{job.rowColor($index)}}">
<th scope="row"><span class="oi oi-{{job.rowIcon($index)}}"></span> </th>
<th>{{row.Email}}</th>
<td>{{row.Type}}</td>
<td>{{row.Mode}}</td>
<td>{{row.TotalItems}}</td>
<td>{{row.Output}}</td>
<td>{{row.StartTime}}</td>
<td>{{row.EndTime}}</td>
</tr>
</tbody>
</table>
</div>
</div>
this is my controller:
.controller('JobController',function($scope,$http,$interval){
var queue = this;
queue.loadJobs=function(){
$http.get('http://localhost:55006/jobs-list/20/null/null').
then(function(response) {
queue.data = response.data;
});
};
$interval(function(){ queue.loadJobs(); }, 3000);
queue.data=[
{Status:'running',Email:'mmassari#mail.sm',Type:'Report1',Mode:'Multipage PDF',TotalItems:23,Output:'E-Mail',StartTime:'12:05',EndTime:'--:--'},
{Status:'ok',Email:'mbaratti#mail.it',Type:'Report3',Mode:'Zipped PDF',TotalItems:39,Output:'E-Mail',StartTime:'12:05',EndTime:'10:30:15'},
{Status:'error',Email:'acanducci#mail.it',Type:'Report3',Mode:'Multipage PDF',TotalItems:120,Output:'E-Mail',StartTime:'12:05',EndTime:'10:15:55'},
{Status:'okerror',Email:'mmassari#mail.sm',Type:'Report5',Mode:'Zipped PDF',TotalItems:23,Output:'E-Mail',StartTime:'12:05',EndTime:'09:26:00'},
{Status:'wait',Email:'eemo#mail.it',Type:'Report1',Mode:'Single PDF',TotalItems:1,Output:'Download',StartTime:'--:--',EndTime:'--:--'},
];
queue.rowColor = function(index){
switch(queue.data[index].status){
case 'running': return 'primary';
case 'wait': return 'secondary';
case 'error': return 'danger';
case 'okerror': return 'warning';
case 'ok': return 'success';
}
}
queue.rowIcon = function(index){
switch(queue.data[index].status){
case 'running': return 'play-circle';
case 'wait': return 'media-pause';
case 'error': return 'x';
case 'okerror': return 'warning';
case 'ok': return 'check';
}
}
})

I have spent long time to find out what is this code issue because I thought probably it is a bug in ng-repeat. But finally I found out your issue is misspelling the status (it is Capitalized in the data).
switch(queue.data[index].status){
must change to
switch(queue.data[index].Status){
Here is the working jsfiddle:
http://jsfiddle.net/arashsoft/2k3pojpv/2/

Instead of passing in the index I suggest you always pass in the actual object.
View
class="table-{{job.rowColor(row)}}"
Controller
queue.rowColor = function(row){
switch(row.status){
.....

Related

How do i refresh or redraw table rows

Below is the classical issue which I am facing during my app development.
I have an array of JSONObjects in my spring controller that I have to iterate in the jsp;
Also another status attribute called JSONArrayStatus is set that suggests if JSON array is empty or not.
Using jquery if JSONArray is empty I will show noDataImageDiv otherwise will show tableDIV (Binding the data from JSONArray using JSTL)
The problem I am facing is as below.
1. Edit a row in the table and click on Update. At this time I make an Ajax Call say, "UpdatedUser", which will return all the records along with the updated records. I could use refresh however thats not a recommended user experience and hence a no no.
To reflect the updated users in the table, I use jquery as below
clearing table rows table.clear().draw()
Loop the result set as follows.
redraw code
function reDrawExternalContactUsers(externalUsers) {
table.clear().draw();
var row = "";
$.each(externalUsers, function (i, field) {
row = '<tr><td></td><td></td><td class="edit">edit</td></tr>';
$("#tableDIV").append(row);
});
}
afetr this redraw or refresh process
This function is NOT working
$(".edit").click(function(){
});
This function is working
$("#tableDIV .edit").click(function(){
});
Suggest a better way of refreshing table rows, if any.
<div id="tableDIV">
<table id="tableID">
<thead>
<tr>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
if data exist
loop{
<tr>
<td></td>
<td></td>
<td class="edit">edit</td>
</tr>
} // loops ends
if close
</tbody>
</table>
</div>
<div id="noDataImageDiv"> No data image</div>
html code :
<div id="tableDIV">
<table id="tableID">
<thead>
<tr>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
if data exist
loop{
<tr>
<td class="user-name"></td>
<td></td>
<td class="edit" data-user-id="">edit</td> //set user_id in attr data-user-id
</tr>
} // loops ends
if close
</tbody>
</table>
</div>
<div id="noDataImageDiv"> No data image</div>
jquery code :
you should use click event on document
$(document).on('click', '.edit', function () {
var btn = $(this);
var user_id = btn.attr("data-user-id"); //user_id of user will update
// extra user data
$.ajax({
method: "POST",
url: url,
data: {
'id': id,
// extra data to send
},
success: function (data) {
if (data.status) // successfully user updated
{
var user = data.user;
/* you can set user data like this */
btn.closest('tr').find('.user-name').html(user.name);
}
}
});
});

Angularjs checkbox like gmail

Im newbie in angularjs.
Now im having issues with a checkbox.
First: my web are paging , sorting and change page size by call api to get data from server in factory:
this.getData = function(page , sortColumm, sortType , pageSize, searchText){
$http({
method = get;
url = "api/data?page="+page +"&sortColumm=" +sortColumm +"&sortType="+sortType +"&pagesize="+pageSize+"&search="+searchText;
}).success(function (data) {
}).error(function (error) {
});
}
now i have a page using angularjs to bind data using funciton getData.
html:
<thead>
<tr>
<th><input type="checkbox" ng-model="selectedAll" ng-click="checkAll()"></th>
<th>Administrator Name</th>
<th >Administrator Type</th>
<th>Roles</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="gladmin in gladmins" ng-switch on="gladmin.isEnabled"
on-finish-render="resizetable">
<td><input type="checkbox" ng-model="gladmin.Selected" value="{{gladmin.id}}" ng-click="selectionAdmin(gladmin.id)"></td>
<td>{{gladmin.name}}</td>
<td>{{gladmin.type}}</td>
</tr>
</tbody>
controller
app.controller('adminPage',function(){
// call getData
$scope.checkAll=function(){
// what should i write here?
// i want check all is for current page, next page checkall = fall,
// same GMAIL
}
$scope.selectionAdmin = function(id){
// what should i do here
}
})
All i want is the checkall same the checkall in gmail ? anyone help me ? please, i am new bie in this!
i find some link here : http://plnkr.co/edit/N98IKTcHoZMCs18FjSRF?p=preview
http://vitalets.github.io/checklist-model/
but it work not same i want !
It is hard for me to understand the question but I think you just want a property of ng-checked="isAllChecked" in your ng-repeat for the inputs. When that value is set to true all of them should check.

Angularjs ng-repeat doesn't update when the model changes

I Have this code in my project. I try to add data from database using $http, but ng-repeat doesn't update de table, only shows a blank row.
When I check the scope, data is already there.
I've read many answers but they does not seem to be related to my problem.
<div ng-controller="TweetsController">
<table class="table table-striped table-bordered table-hover" width="100%">
<thead>
<tr>
<th class="col-md-5"><i class="fa fa-fw fa-twitter text-muted hidden-md hidden-sm hidden-xs"></i> Texto</th>
<th class="col-md-1 text-center"> Lista</th>
<th class="col-md-1 text-center"> Cuenta</th>
<th class="col-md-1 text-center"> Red</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="tuit in filtrado">
<td>{{tuit.texto}}</td>
<td class="text-center">{{tuit.lista.nombre}}</td>
<td class="text-center">{{tuit.lista.cuenta.nombre}}</td>
<td class="text-center">{{tuit.lista.cuenta.red.tipo}}</td>
</tr>
</tbody>
</table>
<div>
<pagination total-items="ufilter.length" itemsPerPage="itemsPerPage" ng-model="currentPage"></pagination>
</div>
</div>
Controller:
.controller('TweetsController', ['$scope','$http','filterFilter', function($scope,$http,filterFilter) {
$scope.currentPage = 1;
$scope.itemsPerPage = 10;
$scope.filtrado = [];
$scope.setPage = function (pageNo) {
$scope.currentPage = pageNo;
};
// retrieve tweets
$http.get('admin/twitter').success(function(tweets) {
$scope.tweets = tweets;
});
$scope.saveTweet = function(isValid) {
if(isValid) {
var tuit = {
texto: $scope.texto,
lista_id: $scope.lista
};
$http.post('admin/twitter', tuit).success(function(t) {
$scope.tweets.push(t);
});
}
};
$scope.filtrar = function(filtro) {
if($scope.tweets != undefined) {
$scope.ufilter = filterFilter(filtro, $scope.buscar);
var inicio = ($scope.currentPage - 1) * $scope.itemsPerPage;
var fin = inicio + $scope.itemsPerPage;
$scope.filtrado = $scope.ufilter.slice(inicio, fin);
}
};
$scope.$watch('tweets', function() {
$scope.filtrar($scope.tweets);
});
$scope.$watch('currentPage', function() {
$scope.filtrar($scope.tweets);
});
$scope.$watch('buscar', function() {
$scope.filtrar($scope.tweets);
$scope.setPage(1);
});
}])
EDIT:
I Solved it!
The problem is the way how the retrieve data is wrapped
$scope.tweets.push(t[0])
You need to apply to the scope
$http.get('admin/twitter').success(function(tweets) {
$scope.tweets = tweets;
$scope.$apply()
});
This is a great blog post that explains it:
http://jimhoskins.com/2012/12/17/angularjs-and-apply.html
The reason why your ng-repeat is not updating after the $http request is due to the $http is async and your javascript turn for the controller has finished before the response is back from your $http request. So you must notify the scope that things have changed and push it to the scope.
The problem is the way how the retrieve data is wrapped
instead of this:
$http.post('admin/twitter', tuit).success(function(t) {
$scope.tweets.push(t);
});
this:
$http.post('admin/twitter', tuit).success(function(t) {
$scope.tweets.push(t[0]);
});
There is the easy way and the correct way.
This is the easy way
//Utility Functions
function Digest($scope) {
//this may not be future proof, if it isn't then you may have to try $timeout(function(){//do something})
if (!$scope.$$phase) {
try {
//using digest over apply, because apply fires at root, and I generally don't need root.
$scope.$digest();
}
catch (e) { }
}
//console.log('$scope snapshot:', $scope);
}
In your code use this by calling
$http.post('admin/twitter', tuit).success(function(t) {
Digest($scope.tweets.push(t));
});
This is the correct way.
https://docs.angularjs.org/api/ng/service/$q
Have your HTTP call wrapped in a $q promise. Then when it is either succeed or error, then fulfill the promise. The repeater will honor the promise.
For anyone else who may run into this issue where the UI just isn't updating - even after doing an $apply or sticking within a $timeout. I've just realised I made a stupid mistake and had added :: to the embedded property and forgotten about it.
So I had my ng-repeat and inside I had a {{ ::item.name }}. For those of you who don't know what the :: does; it tells angular to set the data and not set any additional watches, which in turn then stops angular trying to update that property. This is useful for data that you know won't ever change. See the below link for more information on one time bindings:
http://blog.thoughtram.io/angularjs/2014/10/14/exploring-angular-1.3-one-time-bindings.html
So my solution was just to remove the :: so: {{ ::item.name }} became {{ item.name }}

HTML not updating after successful use of $filter(orderBy) in custom directive

I'm creating a custom directive called ngSortable that can use the orderBy filter on an array when the element is clicked. I've stepped through and confirmed the array is being sorted correctly, but the view is not updating. The filter is wrapped in scope.$apply(), so I'm not sure why it doesn't update. I've also put a scope.$watch on the array to see if the change was being broadcast and the $watch doesn't catch anything. I'm using jquery click event listener if that makes any difference. If anyone can help me figure out what I'm doing wrong here I would greatly appreciate it.
HTML
<table class="table table-bordered table-condensed">
<thead>
<tr>
<th ng-sortable="policyAcordTypes" enable-multiple="true" data-field="description" style="width: 40%;">Acord Type</th>
.....
</tr>
</thead>
<tbody>
<tr ng-repeat="item in policyAcordTypes" >
<td style="width: 40%; min-width: 140px">
<div>{{item.description}}</div>
</td>
....
</tr>
</tbody>
Javascript
scope.$apply(function () {
$filter('orderBy')(scope.$eval(attr.ngSortable), 'description');
});
And I've tried this...
Javascript
scope.$apply(function () {
var list = scope.$eval(attr.ngSortable);
list = $filter('orderBy')(scope.$eval(attr.ngSortable), 'description');
});
$filter('orderBy') does not modify the passed in array, it returns a new ordered array. You need to assign the returned array back to the scope's property.
Instead of using scope.$eval(attr.ngSortable), you should use [] operator (standard javascript)
Try:
scope.$apply(function () {
scope[attr.ngSortable] = $filter('orderBy')(scope[attr.ngSortable], 'description');
});
DEMO (clicking on the header will sort the row)

Data sorting in a dojox.grid.EnhancedGrid

First of all, thank you a lot for what you are doing here. That is very nice.
I am developing a web application with Dojo, and I am facing a problem with ordering my rows in a dojox.grid.EnhancedGrid.
Let's say i have a product with product status, my JSON file looks like:
[
{"id":1,"name":"Car A","price":"1000","productstatus":{"id":1,"name":"new"}},
{"id":2,"name":"Car B","code":"2000","productstatus":{"id":2,"name":"old"}}
]
I want to put that data in my grid and change the order of the rows by clicking in the header.
I have in my HTML file:
<table id="lstProduct" jsId="lstProduct" dojoType="dojox.grid.EnhancedGrid" >
<thead>
<tr>
<th field="id">Id</th>
<th field="name" width="100px">Name</th>
<th field="price" width="100px">Price</th>
<th field="id" formatter="formatterStatus">Status</th>
</tr>
</thead>
</table>
and my Javascript file:
dojo.addOnLoad(function() {
productStore = new dojo.data.ItemFileReadStore({data: { items: ${products} }});
dijit.byId("lstProduct").setStore(productStore);
});
// formatter
function formatterStatus(val, rowIndex) {
return lstTasks.getItem(rowIndex)['productstatus'][0]['name'];
}
The problem? I cannot order by status (status's name), it only orders by product.id when I click in the status header.
Any workaround for that?
Thanks in advance.
I believe you need to add clientSort="true" to enable client-side sorting. Add this to the <table> declaration.

Categories