How to poll server in ng-repeat and stop polling on condition - javascript

I have to start polling the server to get the data for multiple table rows, I am having these in ng-repeat.
<table class="table table-bordered" >
<tr>
<th>Id</th>
<th>Title</th>
<th>Amount</th>
<th>...
</tr>
<tr ng-repeat="data in dataList">
<td>
<div>{{ data.id }}</div>
</td>
<td>
<div>{{ data.title }}</div>
</td>
<td>
<div>{{ data.amount }}</div>
</td>
</tr>
</table>
here I have to poll for each row separately ,so that the amount increased or decreased can be viewed . When I amount reaches 100 I have to stop polling for that row.
$scope.pollServer = function(id) {
$scope.pollTimeout = $interval(function() {
$scope.getAmount(id);
}, 1000);
};
can anyone suggest the best way to poll multiple threads in angular . And how to stop for an individual thread.

TL;DR Only update model, ngRepeat is magical.
Do your polling in a service, bind it to $scope in your controller.
app.factory('dataService', ['$http', '$q', function($http, $q){
return {
pollServer: function() {
return $http.get('url'); // do some call to API
}
}
}]);
In controller:
$scope.dataListPromise = dataService.pollServer();
$q.all([dataListPromise]).then(function(results){
$scope.dataList = results[0];
});
Remember to inject $q to controller as well. You can keep your ngRepeat the same as it is now, but it's probably a good idea to track your database results with the id from database. And because you are now dealing with a deferred, you can use ngCloak with ngRepeat to avoid showing pre-rendered $scope variables to user.

Related

Angular $scope function not working outside of ng-repeat

I have a <table> element, at which I declared a controller (only one in the app at the moment). I also have a ng-repeat on <tr> in the <tbody> element, which is working just fine, creating multiple table rows as intended. In the controller i have some api calls which are called for single table rows and which work just fine, and a function called from one of the <th>'s in <thead>, which I can't get to work. I know it's a scope thing, but I just can't grasp what I'm doing wrong.
To sum it up:
simplified html fragment:
<table ng-controller='myController'>
<thead>
<tr>
<th>Username</th>
<th><button ng-click='doStuff()'>Do stuff</button></th>
</tr>
</thead>
<tbody>
<tr ng-repeat='user in users'>
<td>{{user.name}}</td>
<td><button ng-click='delete(user)'>Delete</button>
</tr>
</tbody>
</table>
simplified js fragment:
app.controller('myController', ['$scope', '$http', users, function($scope, $http, users) {
//api call to get users, working fine
users.getAll().success(function(data) {
$scope.users = data;
});
//api call to delete users, also working fine
$scope.delete = function(user) {
users.delete(user).success(function() {});
};
//can't get this to fire
$scope.doStuff = function() {
alert('I do stuff');
};
}]);
Any insight would be helpful, thanks in advance!
EDIT
I assume the issue is comming from one of the modules, so I copied the whole thing into a plnkr. Sorry for the styling, removed it for more code simplicity.
EDIT 2
After studying the plnkr I came to see that it was an unclosed <div> element in the <thead>. Ouch. Thanks for the replies, and please excuse my carelessness.
EDIT 3
An even more carefull study revealed, that the problem has in fact been also lying in poorly designed jasmine unit tests.
It appears to be firing. I've had to simulate the asynchronous loading of users, but that shouldn't make a difference. Can you add a snippet to demonstrate the issue?
angular.module('app', []).controller('myController', ['$scope', '$http', '$timeout',
function($scope, $http, $timeout) {
//api call to get users, working fine
$timeout(function() {
$scope.users = [{
name: 'A'
}, {
name: 'B'
}];
}, 1000);
//api call to delete users, also working fine
$scope.delete = function(user) {
console.log('delete', user);
};
$scope.doStuff = function() {
console.log('I do stuff');
};
}
]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.js"></script>
<div ng-app="app">
<table ng-controller='myController'>
<thead>
<tr>
<th>Username</th>
<th>
<button ng-click='doStuff()'>Do stuff</button>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat='user in users'>
<td>{{user.name}}</td>
<td>
<button ng-click='delete(user)'>Delete</button>
</tr>
</tbody>
</table>
</div>

How to keep track of Array index in angularJS?

Very new to Angularjs. So I have some JSON files that I am reading into my webpage that contain and array of objects that are cars. What I am trying to do is have my "button" when pressed alert me to the data specific to that button.
The ng-repeat is running 8 times so that is the length of the array, but in angularJs i'm not sure how to basically store the array index for each time the ng-repeat passes in my button function.
This is my a snippet of my .html:
<div class="carTable table-responsive text-center" ng-controller="ButtonController" >
<table class="table specTable">
<thead>
<tr>
<th>Make</th>
<th>Model</th>
<th>Year</th>
<th>Color</th>
<th>Milage</th>
<th>Doors</th>
<th class="reserve">Horsepower</th>
<th>Price</th>
<th class="reserve"></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="cars in car | orderBy:'year'">
<td>{{cars.year}}</td>
<td>{{cars.model}}</td>
<td>{{cars.make}}</td>
<td>{{cars.color}}</td>
<td>{{cars.mileage | number}}</td>
<td>{{cars.doors}}</td>
<td>{{cars.horsepower}}</td>
<td>{{cars.price | number}}</td>
<td><div class="panel panel-default">Reserve</div></td>
</tr>
</tbody>
</table>
</div>
The portion in question is at the bottom where I have a Reserve "button"
I'm leaving out my JSON files, works properly there. I'm just not sure how to keep track of the array index as the ng-repeat does its thing.
Here is the angular:
(function(){
var app = angular.module("myReserveApp", []);
app.controller("ButtonController", ["$scope", "$window", function($scope, $window){
$scope.buttonPress = function(){
$window.alert(JSON.stringify($scope.car[0]));
}
}]);
var MainController = function($scope, $http, $window){
var onGatherBoatData = function(response){
$scope.boat = response.data;
};
var onError = function(reason){
$scope.error = "Could not fetch Boat Data";
};
var onGatherCarData = function(response){
$scope.car = response.data;
};
var onError = function(reason){
$scope.error = "Could not fetch Car Data";
};
var onGatherTruckData = function(response){
$scope.truck = response.data;
};
var onError = function(reason){
$scope.error = "Could not fetch Truck Data";
};
$scope.message = "Hello, Angular Here!";
};
app.controller("MainController", ["$scope", "$http", "$window", MainController]);
}());
Currently in the top portion of the code I just have it alerting object[0] but I want it to be specific to which button is pressed. Any help is appreciated, thank you.
$index refers to the index in ng-repeat. So if you want to pass your function the index in array on the button click, change buttonPress() to buttonPress($index)
you'll have to change your controller to something like the following:
$scope.buttonPress = function(index){
$window.alert(JSON.stringify($scope.car[index]));
}
To do the following, you can just pass the current data in the ngRepeat. Moreover,if you want the current index, the ngRepeat directive provide specials properties, as the $index, which is an iterator.
$scope.buttonPress = function(car, index){
//Retrieve current data of the ngRepeat loop
console.log(car);
//Current index of your data into the array
console.log(index);
}
Then you can call your function like this :
Reserve
First, thank you both for the quick responses. Both of these answers work. I found another way to do it as well before reading your posts.
<div class="panel panel-default">
Reserve:{{car.indexOf(cars)}}
</div>
Using (car.indexOf(cars)) gives me the same result
$scope.buttonPress = function(index){
$window.alert(JSON.stringify(index));
}
Now when I click on the "button" it sends me back the array index, so now I should be able to play with that data. Thank you again both, for your help.

Retrieving the id from Model to Controller in Angular JS

i have a table which is having the data retrieved from an api call from my memberController which is displayed inside ng-repeat and its working fine.
I need each Business Name of the member list to link to a separate page(edit_form.html) and display the id value, so that i can pass this along with the api call to get only this particular member detail. So i have added ng-init in my edit form page which calls the function test_funct when the page loads and retrieve each persons id there. unfortunately i am unable to retrieve the id value inside the function.
HTML Template
<div class="page" data-ng-controller="memberController">
<table>
<thead >
<tr>
<th>Business Name</th>
<th>Contact Name</th>
<th>Trade Balance</th>
<th>Cash Balance</th>
<th>Telephone</th>
<th>Account Number </th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="member in details | filter:search">
<td>{{member.businessname}}</td>
<td>{{member.person}}</td>
<td>{{member.balance_trade}}</td>
<td>{{member.balance_cash}}</td>
<td>{{member.telephone}}</td>
<td>{{member.accountnumber}}</td>
</tr>
</tbody>
</table>
</div>
I have the following controller
function memberController($scope, $http, $cookieStore) {
var token = $cookieStore.get('token');
var conId = $cookieStore.get('Cont_Id');
var exId = $cookieStore.get('ex_Id');
var member_list = "http://www.vb.com/functions/member_list.html?exchangeid=" + exId +
"&contactid=" + conId + "&token=" + token;
$http.get(member_list)
.success(function(response) {
$scope.details = response;
});
$scope.test_funct = function(id) {
$scope.myid = id;
alert($scope.myid); // getting undefined in alert, i expect the id(eg:1123)
}
}
edit_form.html
<div class="page" data-ng-controller="memberController">
<div class="panel-body" ng-init="test_funct()"></div>
</div>
Please assist me on this. Thanks in advance.
There are 2 things going on here.
First, you should separate controllers for the different views, so you end up with something like this:
<div class="page" data-ng-controller="memberController">
<table>
<!-- your whole big table here -->
</table>
</div>
And your editing form as follows:
<div class="page" data-ng-controller="editController">
<div class="panel-body"></div>
</div>
Notice that you now have two distinct controllers - your "editController" and your "memberController".
The second question then becomes, how do you transfer the selected ID from the list view ("memberController") to the edit view ("editController").
There are 2 ways of doing that.
First, you could use a service shared between the controller:
.factory('SelectedId',function() {
return {};
});
And then in your "member" view, you would set it upon clicking:
{{member.businessname}}
Notice the ng-click, which then needs a function in the memberController and the injected service:
.controller('memberController',function($scope,SelectedId) {
$scope.setId = function(id) {
SelectedId.id = id;
};
});
While the editController retrieves it:
.controller('editController',function($scope,SelectedId) {
$scope.id = SelectedId.id;
});
The above option works well, especially for complex things like shopping carts. If all you are doing is passing an ID, I would just stick it in the URL:
{{member.businessname}}
So that the ID is part of the URL. You then can retrieve it in the "editController":
.controller('editController',function($scope,$routeParams) {
$scope.id = $routeParams.member;
});
assuming you are using ng-route, and your route would look like:
$routeProvider.when('/pages/edit_form/:member',{templateUrl:'/route/to/template.html',controller:'editController'});
In html do that
<td>{{member.businessname}}</td>
...
In app.js or where you define route do that
.when('/edit/:memberid',
{
templateUrl:'partials/edit.html',
controller:'editController'
})
In controller you have to take this id by doing that
app.controller("editController",function($routeParams,$scope){
$scope.memberid= $routeParams.memberid;
//Now use this member id to fetch all data
});

Angular controller not display data from JSON

I am using Angular and TingoDB (Mongo) inside Node Webkit for a single page application. However I have a strange problem that I have been unable to resolve.
When I use an object literal (option 2) the data displays correctly in the html page. However changing the code to return data from the database (option 1) the results do not appear on the html page. I have converted both styles of data into the a JSON string to prove consistency and then using the angular.fromJSON to return an object. Both methods return the same JSON string in console.log and before anyone asks I have either Option 1 or Option 2 commented out so both are not running concurrently.
I have copied the JSON string based on the data passed from TingoDB into the console.log and re-entered it into the code below to ensure that no differences between the 2 versions of the data existed without changing any other code, but the problem still persists.
Can anyone shed light on why this occurs and how to fix it?
var app = angular.module('myApp', []);
var Engine = require('tingodb')(),
assert = require('assert');
var db = new Engine.Db('./db', {});
var collection = db.collection("clean.db");
app.controller('tingoDataCtrl', ['$scope', function($scope) {
function getData(callback) {
//Option 1
collection.find().toArray(function(err, docs){
callback (JSON.stringify(docs));
});
//Option 2
var docs = [
{name:"tingo1", description:"56",_id:2},
{name:"tingo2", description:"33",_id:3},
{name:"tingo3", description:"22",_id:4},
{name:"tingo4", description:"76",_id:5},
{name:"tingo5", description:"99",_id:6}
];
callback (JSON.stringify(docs));
}
function info(b) {
// I'm the callback
console.log(b);
$scope.items = angular.fromJson(b)
}
getData(info);
}]);
And the Html
<body ng-app="myApp" id="main">
<div class="page page-data ng-scope">
<section class="panel panel-default" ng-controller="tingoDataCtrl">
<div class="panel-heading"><span class="glyphicon glyphicon-th"></span> Tingo Data</div>
<table class="table">
<thead>
<th class="col-md-4">
Name
</th>
<th class="col-md-8">
Description
</th>
<th class="col-md-8">
ID
</th>
<th></th>
<tr>
</tr>
</thead>
<tbody>
<!-- <tr class="reveal-animation" ng-repeat="item in items | filter:query"> -->
<tr ng-repeat="item in items | filter:query">
<td>{{item.name}}</td>
<td>{{item.description}}</td>
<td>{{item._id}}</td>
</tr>
</tbody>
</table>
</section>
</div>
<script src="js/tingo_problem.js"></script>
</body>
TingoDB is an asynchronous API which will work in the background without stop your app. This means that a syncronous code have no time to wait for an answer and in return it gives undefined.
In your case, you have done a asynchronous call, and it returns correctly the answer to the memory, but too late, the DOM have been updated with undefined already even if your javascript has the data (try console.log to see that it was there).
Angular has a way to be forced to update again the DOM with the new elements of the controller. it is called $apply. And the best way to use it to avoid unexpected behaviours is:
function info(b) {
// I'm the callback
console.log(b);
$scope.items = angular.fromJson(b);
if (!$scope.$$phase) {
$scope.$apply(); //forces update the view
}
}//$scope is NECESARY to be defined in the controler, avoid using it with "ControlerAs"

ng-class directive call executed twice

Simple html:
<table class="table table-condensed">
<tr data-ng-repeat="customer in customers" data-ng-class="customerSelectedClass(customer)">
<td>
{{customer.Name}}
</td>
</tr>
</table>
In my controller - two functions to select customer and return proper class to highlight a table row:
$scope.customerSelectedClass = function (customer) {
if (customer == $scope.selectedCustomer) {
console.log('returing info for ' + customer.Name);
return "info";
}
return "";
};
$scope.selectCustomer = function (customer) {
console.log('selecting ' + customer.Name);
$scope.selectedCustomer = customer;
}
I noticed that when I click on a customer link, customerSelectedClass function executes twice. selectCustomer function on ng-click directive executes once, as it should. Angular is only included once on the page. I wonder if this is a bug in Angular or something that I am doing wrong?
Behind the scenes, angular is setting up a $watch on the function that is resolving the class name. Because angular uses dirty checking to see if there has been a change, this method will be called twice during the $digest cycle. This is ok.
I would suggest that you don't add this code the the controller though, because if you are managing many css classes, you could be adding a lot of unnecessary code. Try something like this instead:
<table class="table table-condensed">
<tr data-ng-repeat="customer in customers" data-ng-class="{'info': customer == selectedCustomer}">
<td>
{{customer.Name}}
</td>
</tr>
</table>
Then, there is no need for a controller function customerSelectedClass. This will only add the info class if the right-hand side of the : resolves to true. And there is no problem resolving the correct customer in the ng-repeat.
Hope this helps.

Categories

Resources