Refresh a ng-repeat list from server every x seconds - javascript

As the title suggests I want to be able to refresh a ng-repeat list from the server every 30 seconds or so. More data can be added on the backend, so I want my list to reflect that. Right now I have the regular $http.get( ) working fine which is here:
$scope.select = function() {
$scope.searchText = '';
$scope.selectedItem = null;
var url = 'http:xxxxxxxxxxxx.com';
url += $scope.selectModel.name;
console.debug("GOING TO: " + url);
$http.get(url).success(function(data2) {
$scope.records = [];
data2.forEach(function(r) {
$scope.records.push(r);
});
});
};
and the portion of the web page it supplies is:
<div style="margin: 1em">
<h4>Search</h4>
<div role="form">
<!-- start dropdown -->
<div class="form-group">
<select class="form-control" ng-options="model as model.name for model in allModels" ng-model="selectModel" ng-change="select()">
<option value="">Choose Model</option>
</select>
</div>
<!-- /end dropdown-->
<div class="form-group">
<input id="start_date" type="text" class="form-control" placeholder="Threat Date">
</div>
</div>
<div>
<table class="table table-hover table-striped" ng-show="records">
<thead>
<th>#</th>
<th>Name</th>
<th>Score</th>
</thead>
<tr data-ng-repeat=" item in records | orderBy : '-score' | limitTo : 10 " ng-click="moreInfo(item)">
<td>{{$index+1}}</td>
<td>{{item.name.slice(5)}}</td>
<td>{{item.score.toFixed(3)}}</td>
</tr>
</table>
</div>
</div>
Is there a way to choose a time # which the list will refresh? And it has to be without hitting a refresh button or something like that. Thanks in advance.
EDIT This is the error I get when I try and use $interval as suggested:
ReferenceError: $interval is not defined
at Scope.$scope.select (http:xxxxxxxxxx.com/UserQuery/js/script.js:24:7)
at fn (eval at <anonymous> (https://code.angularjs.org/1.4.0-rc.0/angular.js:12822:15), <anonymous>:2:209)
at Scope.$get.Scope.$eval (https://code.angularjs.org/1.4.0-rc.0/angular.js:15465:28)
at https://code.angularjs.org/1.4.0-rc.0/angular.js:21825:13
at https://code.angularjs.org/1.4.0-rc.0/angular.js:24485:9
at forEach (https://code.angularjs.org/1.4.0-rc.0/angular.js:332:20)
at NgModelController.$$writeModelToScope (https://code.angularjs.org/1.4.0-rc.0/angular.js:24483:5)
at writeToModelIfNeeded (https://code.angularjs.org/1.4.0-rc.0/angular.js:24476:14)
at https://code.angularjs.org/1.4.0-rc.0/angular.js:24470:9
at validationDone (https://code.angularjs.org/1.4.0-rc.0/angular.js:24398:9)
SOLUTION With combined efforts from this and another question, I came to a solution. First off, like many on this question mentioned, the key here is the use of $interval. There are a few important things to not about using it though.
It must be included in the dependencies for the controller as
#mcpDESIGNS mentioned.
In my case, where there is a dropdown therefor multiple things I want
to $interval over, it is important to close one when you open a new
one.
$scope.select = function() {
$scope.searchText = '';
$scope.selectedItem = null;
$interval.cancel(mainInterval);
$scope.url = '';
url = 'http:xxxxxxxxxxxxxx.com';
url += $scope.selectModel.name;
console.debug("GOING TO: " + url);
$http.get(url).success(function(data2) {
$scope.records = [];
data2.forEach(function(r) {
$scope.records.push(r);
});
});
mainInterval = $interval(function() {
console.debug("UPDATING....");
console.debug("GETTING NEW FROM " + url);
$http.get(url).success(function(data2) {
$scope.records = [];
data2.forEach(function(r) {
$scope.records.push(r);
});
});
}, 5000);
};

Have a look at this:
https://docs.angularjs.org/api/ng/service/$interval
It wraps JavaScript's native setInterval function. You can set it to do the poll every 30 seconds.
It also returns a promise so you can cancel the interval when required.
However, please bear this in mind:
"Intervals created by this service must be explicitly destroyed when you are finished with them. In particular they are not automatically destroyed when a controller's scope or a directive's element are destroyed. You should take this into consideration and make sure to always cancel the interval at the appropriate moment. See the example below for more details on how and when to do this."
EDIT
Taking your code:
$scope.select = function() {
$scope.searchText = '';
$scope.selectedItem = null;
var url = 'http:xxxxxxxxxxxx.com';
url += $scope.selectModel.name;
console.debug("GOING TO: " + url);
$http.get(url).success(function(data2) {
$scope.records = [];
data2.forEach(function(r) {
$scope.records.push(r);
});
});
};
Try changing to this:
$scope.select = function() {
$scope.searchText = '';
$scope.selectedItem = null;
var url = 'http:xxxxxxxxxxxx.com';
url += $scope.selectModel.name;
console.debug("GOING TO: " + url);
$http.get(url).success(function(data2) {
$scope.records = [];
data2.forEach(function(r) {
$scope.records.push(r);
});
});
$interval(function() {
$http.get(url).success(function(data2) {
$scope.records = [];
data2.forEach(function(r) {
$scope.records.push(r);
});
});
}, 30000);
};
If that works you can then refactor the actual $http.get out into a named function to remove the code smell.

Just use a $interval() around your $http to make it refresh every 30 seconds.
$interval(function () {
$http({
/* run your AJAX and update your $scope / etc */
});
}, 30000); // in milliseconds
Note: $interval must be dependency injected into your controller / service / etc to work!
// for examples sake
.controller('MyController', ['$interval', function ($interval) { }]);

Use ng-table instead.
Have a look http://bazalt-cms.com/ng-table/
And u can call its reload propery, which will refresh your table,
The reload you can call inside $timeout service provided by angular.
$timeout(function(){
tablename.reload();
},3000);
or
Just call the select function inside the timeout
$timeout(function(){
$scope.select();
},3000);

Try this:
Ok, I think the plan is each time select is fired, the existing interval needs to be cancelled and another one started. Have a look at this:
var intervalObj;
$scope.select = function() {
if (intervalObj !== null) {
$interval.cancel(intervalObj);
intervalObj = null;
}
$scope.searchText = '';
$scope.selectedItem = null;
var url = 'http:xxxxxxxxxxxx.com';
url += $scope.selectModel.name;
console.debug("GOING TO: " + url);
$http.get(url).success(function(data2) {
$scope.records = [];
data2.forEach(function(r) {
$scope.records.push(r);
});
});
intervalObj = $interval(function() {
$http.get(url).success(function(data2) {
$scope.records = [];
data2.forEach(function(r) {
$scope.records.push(r);
});
});
}, 30000);
};
I have not been able to fully test this, but the principle is sound.

Related

AngularJS select items not resetting to first / default value

I'm at my wits end! In angular I've got a controller and a view.
There are 2 dropdowns on the page which need to reset to default once the restart button has been clicked.
I can set the value of the boxes as they render by pushing a "select" option into the collection inside the controller. However, when the reset button is pressed, which runs the init() method again, the dropdowns should be set back to the first value. This doesn't occur, the values for $scope.selectedYear and $scope.selectedReport remain as they did before the reset button was pressed.
This is the full code for the controller
function TreeReportsCHLController($scope, $q, $routeParams, reportsDashboardResource, navigationService, notificationsService, dialogService, entriesManageDashboardResource, $timeout) {
// Methods
var generalError = function () {
notificationsService.error("Ooops", "There was an error fetching the data");
$scope.actionInProgress = false;
}
// Scope
$scope.selectedYear = "";
$scope.init = function () {
$scope.hasCompleted = false;
$scope.actionInProgress = false;
$scope.yearSelected = false;
$scope.reportTypes = ["Choose", "Entered", "ShortListed", "Winner", "Recieved"];
$scope.selectedReport = "";
$scope.entryYears = new Array();
$scope.entryYears.push("Choose a Year");
entriesManageDashboardResource.getEntryYears().then(function (response) {
angular.forEach(response, function (value, key) {
$scope.entryYears.push(value);
});
});
$scope.selectedYear = $scope.entryYears[0];
$scope.selectedReport = $scope.reportTypes[0];
};
$scope.yearHasSelected = function(selectedYear) {
$scope.yearSelected = true;
$scope.selectedYear = selectedYear;
};
$scope.generateFile = function (selectedReport) {
$scope.actionInProgress = true;
var reportDetail = {
entryYear: $scope.selectedYear,
chosenEntryStatus: selectedReport
};
reportsDashboardResource.generateEntriesReportDownloadLink(reportDetail).then(function (response) {
if (response.Successful) {
$scope.hasCompleted = true;
} else {
notificationsService.error("Ooops", response.ErrorMessage);
}
$scope.actionInProgress = false;
}, generalError);
};
$scope.restart = function () {
$scope.init();
}
// Initialise Page
$scope.init();
}
angular.module("umbraco").controller("ReportsDashboardController", TreeReportsCHLController)
this is the code with the dropdowns in it;
<table>
<tr>
<td>Get a report for year: {{selectedYear}}</td>
<td><select ng-model="selectedYear" ng-change="yearHasSelected(selectedYear)" ng-options="year for year in entryYears" no-dirty-check></select></td>
</tr>
<tr ng-show="!hasCompleted && yearSelected">
<td>
Get Report Type:
</td>
<td>
<select ng-model="selectedReport" ng-change="generateFile(selectedReport)" ng-options="status for status in reportTypes" no-dirty-check ng-disabled="actionInProgress"></select>
</td>
</tr>
</table>
I've also done a further test where I simply set $scope.selectedYear to $scope.entryYears[0] within the reset method. When I console.log $scope.selectedYear here, the value confirms it has been changed, but strangely where I've outputted the $scope.selectedYear / {{selectedYear}} to the page for testing, this does not update. It's almost as though the binding between the controller and the view isn't occuring.
Any help?
Thank-you.
Here's a working plunk that is somewhat stripped down since I didn't have access to of the services that your are injecting into your controller. The changes I made in the controller are:
First,
$scope.entryYears = new Array();
becomes
$scope.entryYears = [];
as this is the preferred way to declare an array in js.
Second, I removed $scope.apply() that was wrapping
$scope.selectedYear = $scope.entryYears[0];
$scope.selectedReport = $scope.reportTypes[0];
as this was causing infinite digest cycles.

Is there a way to trigger callback function just once in $watchGroup | Angularjs?

I'm watching two values in my angular project like that: $scope.$watchGroup( ['currentPage', 'newStatus'], $scope.setPage ).
And when both values are changing the function $scope.setPage executes two times accordingly, but it should once. How to implement this?
My code:
JS
app.controller('CategoryListCtrl', ['$scope', '$http', '$location', '$route', function($scope, $http, $location, $route) {
$scope.numPerPage = 5;
$scope.currentPage = 1;
$scope.newStatus = -1;
var currentPageChanged = false;
var newStatusChanged = false;
function setPage() {
// function's code based on requests to db
};
setPage();
// pagination based on db requests
$scope.$watchGroup( ['currentPage', 'newStatus'], function(newValues, oldValues) {
if ((newValues[0] != oldValues[0]) && !newStatusChanged) {
console.log(1);
// currentPage has changed
setPage();
currentPageChanged = true;
} else if ((newValues[1] != oldValues[1]) && !currentPageChanged) {
console.log(2);
// newStatus has changed
newStatusChanged = true;
} else {
console.log(3);
newStatusChanged = false;
currentPageChanged = false;
}
});
...
HTML
...
<th>
<select class="form-control"
ng-model="isPublic"
ng-options="isPublic.name for isPublic in isPublicScope"
ng-change="newStatus = isPublic.status; currentPage = 1;">
<option value="">Show all</option>
</select>
</th>
<tr ng-repeat="category in categories | filter:search">
</tr>
...
You can add a flag, which changes when both the values change for the first time (only once). After which you can disable the watch.
Here is an example: jsbin example
can also be written like this: editted
In this example, two textboxes are given, only when both the textbox values are changed for the first time, the watch increased the count to 1. After that, the watch is disabled and hence the count does not increase even when changes occur in the textboxes.
With a slight change in the code, you may be able to get what you want from this example.
This should probably do the trick (not tested):
var w = $scope.$watchGroup( ['currentPage', 'newStatus'],
function(newValues, oldValues) {
if ((newValues[0] != oldValues[0]) || (newValues[1] != oldValues[1])) {
$scope.setPage;
// clear $watchGroup
w();
}
}
)

Why can't I do a $scope update within a jQuery scope?

I'm using typeahead to get some suggestions on an input text, this is inside a div controlled by an Angular controller.
The code for the suggestion tasks works with a jQuery plugin, so when I select, something I'm trying to assign a value to $scope, however this is NEVER happening.
I already tried getting the scope of the element with var scope = angular.element($("#providersSearchInput").scope() and then assign it as suggested here but it didn't work.
This is what I'm trying:
<div class="modal-body" ng-controller="ProvidersController" ng-init="orderReviewTab = 'observations'">
<input type="text" id="providersSearchInput" data-provide="typeahead" class="form-control input-md" placeholder="Buscar proovedores">
{{currentProvider}}
</div>
The controller looks like this:
tv3App.controller('ProvidersController', function($scope, $rootScope, $http, $timeout) {
var resultsCache = [];
$("#providersSearchInput").typeahead({
source: function (query, process) {
return $.get("/search/providers/?query=" + query, function (results) {
resultsCache = results;
return process(results);
},'json');
},
matcher: function (item) {
var name = item.name.toLowerCase();
var email = item.email.toLowerCase();
var contact_name = item.contact_name.toLowerCase();
//console.log(name);
var q = this.query.toLowerCase();
return (name.indexOf(q) != -1 || email.indexOf(q) != -1 || contact_name.indexOf(q) != -1);
},
scrollHeight: 20,
highlighter: function (itemName) {
var selected = _.find(resultsCache,{name:itemName});
var div = $('<div></div>');
var name = $('<span ></span>').html('<strong style="font-weight:bold">Empresa: </strong> ' + selected.name);
var contact = $('<span ></span>').html(' <strong style="font-weight:bold">Contacto: </strong> ' + selected.contact_name);
var email = $('<span ></span>').html(' <strong style="font-weight:bold">e-mail:</strong> ' + selected.email);
return $(div).append(name).append(contact).append(email).html();
},
minLength: 3,
items: 15,
afterSelect: function (item) {
console.log(item);
$scope.$emit('providerSelected',item);
}
});
$scope.$on('providerSelected', function (event,provider) {
console.log(provider);
$scope.currentProvider = provider;
$scope.$apply();
});
});
Edit
I tried this to check any changes:
$scope.$watch('currentProvider',function (newValue,oldValue) {
console.log(oldValue);
console.log(newValue);
});
So when selecting something it actually triggers and $scope.currentProvider seems to be updated but its never getting rendered at view ...
get https://angular-ui.github.io/bootstrap/
once you do, in your code make sure
angular.module('myModule', ['ui.bootstrap']);
and for typeahead have
<input type="text" ng-model="currentProvider" typeahead="provider for provider in getProviders($viewValue) | limitTo:8" class="form-control">
In your controller make sure you have
$scope.getProviders = function(val){
return $http.get('/search/providers/?query=' + val).then(function(response){
return response.data;
})
}
This should do the trick although I haven't tested

When I keep refreshing my angularJS page, the content sometimes shows up, sometimes doesn't

I have a page running angularJS. Sometimes, when I open the document, the data that needs to appear only sometimes shows up. When I keep trying to refresh the page, it's pretty much random: sometimes the content appears, sometimes it doesn't.
The section of the code that runs this looks like this:
<div class="row">
<div class="col-md-12" ng-repeat="(observer,hosts2) in bugDuration">
{{observer}}
<div class="row">
<div class="col-md-3" ng-repeat="(host, bugs2) in hosts2"> {{host}}
<div ng-repeat="(bug, duration) in bugs2">
{{bug}} for {{duration}} seconds.
</div>
</div>
</div>
</div>
</div>
As you can see, it is using ng-repeat, and my best guess is that when this code is running, the ng-repeat objects, such as bugDuration are empty, so none of it runs.
My script that initializes all of these variables is located after, in my document. Is there something I should do in the controller or whatever so the variables can be refreshed and the content can be shown everytime?
Edit
Here is the code where bugDuration is initialized:
bugDuration = {};
bugTracker = {};
$.getJSON('../java_output/bugs.json', function (data) {
for ( var observer in data ) {
bugDuration[observer] = {};
for (var host in data[observer]) {
bugDuration[observer][host] = {};
for (var bug in data[observer][host]) {
bugDuration[observer][host][bug] = data[observer][host][bug].duration;
}
}
}
console.log (bugDuration);
});
$.getJSON('../java_output/bug_summary.json', function (data) {
var numObservers = data.numObservers;
delete data['numObservers'];
JSONbugsList = data;
var bugTracker = {};
for (var observer = 1; observer <= numObservers; observer++) {
observers.push(observer);
observerKeys = Object.keys(data);
// observerKeys.splice(observerKeys.indexOf('numObservers'));
for (var host in data["observer" + observer]) {
if (hosts.indexOf(host) == -1) {
hosts.push(host);
}
hostKeys = Object.keys(data["observer" + observer]);
for (var bug in data["observer" + observer][host]) {
if (bugs.indexOf(bug) == -1) {
bugs.push(bug);
}
for (var i in data["observer" + observer][host][bug]) {
bugTracker[bug] = true;
var dateVar = data["observer" + observer][host][bug][i];
var intoList = {"observer":observer, "host":host, "bug":bug, "start":(new Date(1000*dateVar.start)), "end":(dateVar.end==null?' the end.':(new Date(1000*dateVar.end)))}
}
}
}
}
// Removed unimportant stuff here//
$scope.$apply();
$scope.hostsS = hosts;
$scope.bugsS = bugs;
$scope.observersS = observers;
$scope.JSONbugsList = JSONbugsList;
$scope.hostKeys = hostKeys;
$scope.observerKeys = observerKeys;
$scope.start = 'start';
$scope.end = 'end';
$scope.bugDuration = bugDuration;
$scope.$apply();
The biggest problem among others is that $scope.$apply() needs to happen after the data gets set on the $scope. Since $.getJSON is asynchronous, by the time the callback gets triggered, the $scope.$apply() lines at the bottom will have already been fired.
$.getJSON('../java_output/bug_summary.json', function (data) {
/*do stuff outside of angular context when the ASYNC callback fires*/
$scope.stuff = data;
/*then call $scope.$apply()*/
$scope.$apply();
});

Resetting an input to initial value when a search result is clicked with AngularJS

I've a list on ng-repeat that displays a list of results from a $http query (bind to an input). I'd like both for the list to disappear when the user clicks on one of the results and for the initial empty value of the model to be restored.
Basically, the functionality is as follows:
User searches term, list displays results, user clicks on result, list disappears, user clicks on input again to make another search, list with new results appear.
So far I've managed to make the list disappear, but not to make it appear again when the user makes another search.
Here's the relevant code:
<input type="text" ng-model="name" ng-click="Research()"/>
<ul ng-hide="clicked" ng-show="retype">
<li ng-repeat="result in results" ng-click="getDetails(result.id)">{{result.title}}</li>
</ul>
And the JS:
function Ctrl($scope, $http) {
var get_results = function(name) {
if (name) {
$http.get('http://api.discogs.com/database/search?type=artist&q='+ name +'&page=1&per_page=8').
success(function(data3) {
$scope.results = data3.results;
});
}
}
$scope.name = '';
$scope.$watch('name', get_results, true);
$scope.getDetails = function (id) {
$http.get('http://api.discogs.com/artists/' + id).
success(function(data) {
$scope.artist = data;
});
$http.get('http://api.discogs.com/artists/' + id + '/releases?page=1&per_page=500').
success(function(data2) {
$scope.releases = data2.releases;
});
$scope.clicked = true;
}
function Research(){
$scope.retype = true,
$scope.name = '';
}
Plunkr is down, I'll make one as soon as possible. Any idea about what am I missing?
I tidied up your code a little bit. Please note that the div is shown only when artist is defined. So when it is set to undefined by the $scope.clear() method, the mentioned div is hidden.
Html part:
<div ng-controller="Ctrl">
<input type="text" ng-model="name" ng-focus="clear()"/>
<ul>
<li ng-repeat="result in results" ng-click="getDetails(result.id)">{{result.title}}</li>
</ul>
<div ng-show="artist">
<h1>Artist</h1>
<ul>
<li>{{artist.name}}</li>
<li>{{artist.release_url}}</li>
<li>{{artist.uri}}</li>
<li>{{artist.resource_url}}</li>
</ul>
</div>
</div>
JavaScript part:
var myApp = angular.module('myApp',[]);
function Ctrl($scope, $http) {
$scope.name = undefined;
$scope.artist = undefined;
$scope.results = undefined;
var search = function (name) {
if (name) {
$http.get('http://api.discogs.com/database/search?type=artist&q='+ name +'&page=1&per_page=8').
success(function(data3) {
$scope.results = data3.results;
});
}
}
$scope.$watch('name', search, true);
$scope.getDetails = function (id) {
$http.get('http://api.discogs.com/artists/' + id).
success(function(data) {
$scope.artist = data;
});
$http.get('http://api.discogs.com/artists/' + id + '/releases?page=1&per_page=500').
success(function(data2) {
$scope.releases = data2.releases;
});
}
$scope.clear = function () {
$scope.name = undefined;
$scope.artist = undefined;
$scope.results = undefined;
}
}
There is working JSFiddle.
Your Research function is unnecessary because you don't need ng-show and ng-hide same time...
secondly you set clicked to ok but never set it false again after your research done...
here is working PLUNKER
Try using just one ng-hide or ng-show, instead of both. Since you never set clicked back to false, it is probably overriding the retype.
Both functions are two-way, so you can just use ng-hide="clicked", and inside function Research, set $scope.clicked to false.

Categories

Resources