Angularjs data is not updated on call of $apply - angularjs

I have a controller that reads data via rest and displays it in a table - this part is working fine. Additionally i added a WebSocket and want to update the table if Websocket receives data. Here is the Code:
app.controller('allBookingsCtrl', function($scope, $http, currentUser){
$scope.bookingsList = [];
var ws = new WebSocket(wsRootUrl + '/allbookings');
ws.onmessage = function(message){
$scope.$apply(function(){
$scope.bookingsList = message.data;
alert(message.data);//displays correct data!
});
};
$http.get(rootUrl + '/timetracker/booking/all').success(function(response) {
$scope.bookingsList = response;
});
});
The problem is the table is not updated on call of apply. I debugged and could trigger onmessage by changing data from another browser. the content of data is also correct, no error is thrown.
So how to update the table/scope with data received by websocket?
here is html:
<table ng-controller="allBookingsCtrl" class="table">
<thead>
<tr>
<th>#</th>
<th>user</th>
<th>project</th>
<th>start</th>
<th>end</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="booking in bookingsList">
<td>{{$index + 1}}</td>
<td>{{booking.usersProjects.user.name}}</td>
<td>{{booking.usersProjects.project.name}}</td>
<td>{{booking.start | date:'yyyy-MM-dd HH:mm:ss' }}</td>
<td>{{booking.end | date:'yyyy-MM-dd HH:mm:ss' }}</td>
</tr>
</tbody>
</table>
Small Edit:
if i add alert(data); at the end of on message i see the alert with correct data! So only the apply with the list isn't working correctly.
added plunkr
I tried to reproduce this in plunkr and with no websocket - tried to get the update on ng-click. But this isn't working neither - here the click is not doing anything.

Can you change this line
$scope.bookingsList = data;
to
$scope.bookingsList = message.data;

Related

ng-repeat isn't loading my array of objects

I have a function that returns several jsons via rest requests, and the data of these jsons are stored in a array.
Then this array put in $scope and use it in a ng-repeat, but isn't working.
//JS
async function exec_q(access_token, start_date,end_date,programa_gql){
var people = [];
...requests and filling the array...
people.push({
"atrib1": res.1 === null ? '' : res.1,
"atrib2": res.2 === null ? '' : res.2
}
return people
}
people = await exec_q(access_token, start_date,end_date,programa_gql)
$scope.people = people
//HTML
<tbody id="table_apd_body">
<tr ng-repeat="p in people">
<th>{{{$index + 1}}</th>
<th>{{p.atrib1}}</th>
<th>{{p.atrib2}}</th>
</tr>
</tbody>
Obs:. the function that call the code above is async as well to make the await works.
The ES6 promises used by async/await are not integrated with the AngularJS framework and its digest cycle.
people = await exec_q(access_token, start_date,end_date,programa_gql)
$scope.people = people
$scope.$apply();
Use $apply to integrate $scope changes.
//Remove extra { in {{$index + 1}}
<tbody id="table_apd_body">
<tr ng-repeat="p in people">
<th>{{$index + 1}}</th>
<th>{{p.atrib1}}</th>
<th>{{p.atrib2}}</th>
</tr>
</tbody>

angular edit details using REST($resource) Service

I have a table with result set and there is edit option existing in each row. After clicking on the 'edit' link, i am able to populating the content on the fields.
But how do i edit the existing content using REST Service.
<table class="table-bordered table">
<thead>
<tr>
<th>id</th>
<th>Director</th>
<th>genre</th>
<th>releaseYear</th>
<th>title</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="result in results | orderBy:'_id'">
<td>{{result._id}}</td>
<td>{{result.director}}</td>
<td>{{result.genre}}</td>
<td>{{result.releaseYear}}</td>
<td>{{result.title}}</td>
<td>Edit | Delete</td>
</tr>
</tbody>
</table>
controller
$scope.saveContact = function(){
//save or edit contact
};
I have created in plunker.
content EDIT using REST API
I put it into work. You were close to the solution.
Here is the working plunker
What i did :
1 - I changed your way of getting the element for the form.
HTML
//I give the complete object instead of the id
ng-click="edit(result)"
Controller
//I prefer to pass the entire object
$scope.edit = function(result){
$scope.cineresultsFrm = angular.copy(result);
};
Service
I just removed the service. It wasn't useful anymore.
2 - I used the method of the ressource on your object
Controller
$scope.saveContact = function(){
//The function given to $update() is the .then() function
$scope.cineresultsFrm.$update(function(){
//If the update succeed i update the whole list. Could be done a better way but if you don't have any performance issues that will do the job.
$scope.getMovies();
});
};
I also changed the way you handle the "then" in the promise. But this is just a matter of taste.
$scope.getMovies = function(){
$scope.movieResults = movie.query(function() {
$scope.results = $scope.movieResults;
});
};
$scope.getMovies();
Hope it helped you

Output is concatenated

New click on [Filter] does not clear previous output but adding to exists.
For example, if filtered by "banned" I see the banned users list, next filter by "registered" does not remove the "banned" but adding the "registered" to the end of the table.
In controller $scope.site_users overwritten, but somewhere it still saves the previous filter output.
Why is that happens? May be something on packages side?
Installed packages:
urigo:angular - Angular
angularui:angular-ui-router
accounts-password
accounts-ui
twbs:bootstrap
Removed packages:
insecure
autopublish
Or in code
Controller:
angular.module("sis_admin_am").controller("UsersListCtrl", ['$scope', '$meteor',
function($scope, $meteor){
$scope.filter = function(){
$scope.site_users = '';
$scope.site_users = $meteor.collection(Users).subscribe('site_users_filtered', {status: $scope.userStatus});
};
}
]);
View:
<form ng-submit="filter()">
<button>Filter</button>
<select ng-model="userStatus" >
<option ng-selected="selected">banned</option>
<option>registered</option>
<option>active</option>
</select>
</form>
<p></p>
<table class="table">
<tr class="panel panel-default">
<th>Name</th>
<th>Email</th>
</tr>
<tr ng-repeat="user in site_users">
<td>{{ user.username }}</td>
<td>{{ user.email }}</td>
</tr>
</table>
Server part:
Meteor.publish('site_users_filtered', function(options) {
console.log('options:', options);
return Users.find(options);
});
That's because how Subscriptions in Meteor works.
If you add or change a subscription without closing the ones before, it will just add them all together (which is good but you have to be aware of it).
If you want to filter with subscriptions (for security reasons) you should change your code like that:
angular.module("sis_admin_am").controller("UsersListCtrl", ['$scope', '$meteor',
function($scope, $meteor){
var savedSubscriptionHandle = null;
$scope.filter = function(){
savedSubscriptionHandle.stop();
$scope.site_users = '';
$scope.site_users = $meteor.collection(Users);
$scope.$meteorSubscribe('site_users_filtered', {status: $scope.userStatus}).then(function(handle){
savedSubscriptionHandle = handle;
});
};
}
]);
But if you don't mind keeping all the data in the local cache it might be easier to use Angular's filters or Meteor's cursor syntax to filter the display.
More detailed explanation here:
http://angular-meteor.com/tutorial/step_12
I think your problem is, ngSelected NOT necessarly select only one choice of your dropdown.
and that is because,tchnically you should de-select your unwanted userStatus manually as they do in the official doc :
https://docs.angularjs.org/api/ng/directive/ngSelected
You can try to send your ng-model (userState) along to the filter() function and actually filter with that value directly without having to check the scope.

ng-table data not showing on page load

I have integrated ngTable into my mean.io stack and I'm having trouble with populating the table on page load. If I select one of the column headers, the data shows up and the table works as advertised.
Here is my html
<table ng-table="tableParams" class="table">
<tbody ng-repeat="p in $data">
<tr id="tr{{p._id}}" ng-class-odd="'odd'" ng-class-even="'even'">
<td class="rowTd" data-title="'Task Code'" sortable="'task_code'">{{p.task_code}}</td>
<td class="rowTd" data-title="'Task Name'" sortable="'task_name'">{{p.task_name}}</td>
<td class="rowTd" ><input type=button id="editRowBtn{{p._id}}" value="edit"
ng-click="setEditId(p._id)"></td>
</tr>
<tr ng-show="editId===p._id" ng-if="editId===p._id">
<td colspan="7" ng-include src="'editRow.html'"></td>
</tr>
</tbody>
</table>
Here is my controller code.
var data = GeneralTasks.query();
$scope.tableParams = new ngTableParams({
page: 1,
count: 10
},{
total: data.length,
getData: function($defer, params) {
params.total(data.length);
$defer.resolve(data.slice((params.page() - 1) * params.count(), params.page() * params.count()));
}
});
$scope.editId = -1;
$scope.setEditId = function(pid) {
$scope.editId = pid;
};
I am new to using this table so i'm sure there is something i'm overlooking.
Wanted to provide the answer to my question so it may help others. Anytime an item in the table is added or removed, the table must be reloaded. Since $save and $remove invoke a callback function, just inserted the following for updating the table.
$scope.add = function() {
if (!$scope.tasks) $scope.tasks = [];
var task = new GeneralTasks({
task_code: $scope.task_code,
trade: $scope.trade,
task: $scope.task,
task_name: $scope.task_name
});
task.$save(function(response) {
$scope.tasks.push(response);
var data = $scope.tasks;
$scope.tableParams.total(data.length);
$scope.tableParams.reload();
});
this.task_code = this.trade = this.task = this.task_name = '';
};
First i update the $scope list with the response and then update the tables data and length. Then just call reload.
As I've stated earlier, i do this for $save and $remove. Here is the $remove code.
$scope.remove = function(task) {
for (var i in $scope.tasks) {
if ($scope.tasks[i] === task) {
$scope.tasks.splice(i, 1);
}
}
task.$remove();
var data = $scope.tasks;
$scope.tableParams.total(data.length);
$scope.tableParams.reload();
};
I have noticed that when I edit a name in the list and then cancel, the name does not reset. I suppose I should add similar code for the cancel action but I'm lazy and that's the least of my worries for now. :)
Hope this helps someone else.

Angularjs state retension

I am adding row dynamically using angularjs. But the problem is that I want to retain this state all over the application. For example in view-1 I add one dynamic row to the table and move to view-2 after coming from view-2 the added row should be available. So is there any method to retain the state of view in angularjs. Following is the code I used to add row dynamically:
angular.module('MyApp', [])
.controller('MainController', [ '$scope', function($scope) {
$scope.rows = ['Row 1'];
$scope.counter = 2;
$scope.addRow = function() {
$scope.rows.push('Row ' + $scope.counter);
$scope.counter++;
}
}]);
<body ng-controller="MainController">
Add Row {{counter}}
<table>
<thead>
<tr>
<th width="200">Some Header</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="rowContent in rows">
<td>{{rowContent}}</td>
</tr>
</tbody>
</table>
</body>
Thanks.
Yes. You should save the model used to build the table (with the new rows aswell) to a Service. Then you inject that service in your controllers.
To be able to see a little bit of your code would be nice, though. But here goes an example of how to do this:
DISCLAIMER
Untested example
angular.module('yourapp').factory('MySharedService',function(){
var myTableModel = [];
function addRow(row){
myTableModel.push(row);
return myTableModel;
}
return {
addRow: addRow,
tableModel: myTableModel
}
})
.controller('MyFirstController',['$scope','MySharedService',function($scope,
MySharedService){
$scope.tableModel = MySharedService.tableModel;
$scope.addRow = function(row){
$scope.tableModel = MySharedService.addRow(row);
}
}]).controller('MySecondController',['$scope','MySharedService',function($scope,
MySharedService){...}]);
EDIT:
After researching a bit further on this, I've found a similar question with a couple ways of achieving this and with sample code here
Check it out and see if it helps you.

Resources