Using $interval inside Angular UI Router templateProvider - angularjs

I'm trying to get my view template updated every 10 minutes. Using this setup in the state config doesn't work:
.state('home', {
url: '/',
controller: 'landing',
templateProvider: function($templateFactory, $interval) {
var templateId = 0;
return $interval(function(){
var template = templateId % 2 == 0 ?
'landing-primary.html':'landing-secondary.html';
templateId++;
return $templateFactory.fromUrl('views/templates/' + template);
}, 600000);
}
})
What's the best way to achieve such work?

The templateProvider option should be a function that returns an html string or a promise that resolves with an html string. It's mainly present so that you can generate html that somehow makes use of $stateParams.
What you're trying to do wouldn't normally be done through route/state definitions; what you've described is logic that should be in a directive or controller. While it's hard to say for sure without more information about exactly what you're trying to achieve, it sounds like what you really need is a single "landing" page with the content of both templates divided by an ng-switch directive that gets bound to a model property. Then in your controller you would set an $interval to change that model property every 10 minutes.
Alternatively, maybe what you want is two states, in which case the pair of states would each have in their corresponding controllers a $timeout that toggles to the other state.

Related

How to pass data stored in controller from one page to another

I'm currently using one controller for my web app. Data is loaded from a JSON file and shown to the user via ng-repeat. When a user makes a selection, only data for the user's selection is in the scope.
I'd like to be able to keep the same scope data and use it across different web pages (or states using UI-Router).
I've looked into using ui-router but it seems like the controller would be refreshed with every state change.
I'd prefer to use ui-router due to design requirements.
Part of my controller code:
(function() {
"use strict";
angular
.module("parkit", ["ngMap"])
.controller("parkitController", function($scope, $rootScope, $http,
parkitFactory, NgMap) {
parkitFactory.getSpots().then(function(spots) {
$scope.spots = spots.data;
});
$scope.showSpot = function(spot) {
$scope.spot = spot;
}
});
})();
Factory code for loading JSON data:
(function() {
"use strict";
angular.module("parkit")
.factory("parkitFactory", function($http) {
function getSpots() {
return $http.get('/data/spots.json');
}
return {
getSpots: getSpots
}
});
})();
As it has been answered before, you can use a factory or service to keep track of the selected item(s). This would store the selected values in the instance of the service/factory and therefore would be lost if someone refreshes the page.
A more resilient, and in my opinion beautiful solution, would be to add the selected item(s) as state parameter in ui-router. Using this method, you will also be able to deep-link to certain selected states and if someone refreshes the page, the same items would still be selected, as you would add your state parameters in the url.
See URL Parameters in the documentation: https://github.com/angular-ui/ui-router/wiki/URL-Routing
You may probably create a new property in the factory function to keep track of the selected item.Set the property when user does a selection. Get the property in other components whereever you need to use the data.
use $rootScope instead of $scope to save your data, That will allow you to use it anywhere in controllers of same domain.
example: $rootScope.yourData = yourData; and then you can assign $rootScope.yourData to any controller in same domain.

ngIf executing too early

I want to show one of two sections in a single view.
<div ng-if="countApps() > 0">
//stuff to show
</div>
or
<div ng-if="countApps() == 0">
//stuff to show
</div>
On page load, the controller will retrieve the apps and populate the $scope.apps variable. I have a function in the controller called $scope.countApps that will return the length of $scope.apps.
The problem is that the "0 apps" part of the view will appear while the results are still being fetched and then it will remove this and replace it with other part of the view if 1 or more apps is found.
How can I make it so that only the correct view is shown? I'm aware of resolve but I don't want to block the whole view, only the ng-if parts.
Instead of a function that returns the count, just bind to the count. So on controller initialization have the countApps set to a non-value (undefined) and then whatever sets the apps, would also set the countApps to the correct value. ng-if would resolve false in both cases on initial load. The rest of the page would load, and those sections would catch up when they finished.
If you are using ngRoute look into the resolve option on the $routeProvider's when method.
That will allow you to update the scope when the asynchronous process is complete. So initially you would hide the view, then on resolve switch a flag to show everything.
Scott Allen has a nice post on it here.
John Papa also has a slightly different post here.
Take a look at resolve on the route side, meaning before you load the html, it will load data from the service, it can be a http service or something else.
$routeProvider
.when('/',
{
templateUrl: "app.html",
controller: "AppCtrl"
resolve: {
apps: function (appsService) {
return appsService.fetchApps();
}
}
}
Then in your controller,
.controller('myController', ['$scope', 'apps', function($scope, apps) {
$scope.apps = apps;
}
]);
Now, the html will wait for the items to be resolved before it shows it. Meaning before the controller and view loads, it will get the apps data from the appsService service.

AngularJS - To Make Or Not To Make a Directive

I know that if I want to create a reusable item, such as a date picker, then creating it as a Directive is recommended.
However, let's say that on my homepage, I have a Welcome section that displays the quote of the day with a background image that comes from a Rest service. Should this be a Directive that can encapsulate the markup and controller logic? Or should it be a simple AngularJs Controller that binds to markup in my index.html?
What constitutes whether or not something should be created as a Directive?
Directive is only a wrapper for a controller. It means if you have a controller you can use it. But you also may use the same controller as a controller of a directive for example instead of link function use controller.
This allow as to draw clear line where to use directive and where to use a controller.
We have to use Controller if we want to reproduce logic of piece of HTML markup. When we want to use the same $scope assignments, the same functions inside $scope, ... but HTML markup is always different for every other place where we use this controller.
We have to use directive when we have same logic in a controller of a directive and same HTML markup.
So in your case it is definitely a directive.
This is my own common sense of course, and may not be ideal.
There are three things you will require to implement this functionality:
AngularJS Template a.k.a. Markup to display quote with an image next to it.
AngularJS service to encompass the REST call in order to fetch above details from the server.
AngularJS controller to consume the AngularJS service to feed the data back the template (point 1) to update it accordingly after every rest call.
So the fact is you can achieve this without even writing an AngularJS Directive but what if you need to replicate the same feature in many places. In that sense, you will probably have to copy the same template somewhere else which will again need a separate controller to consume the same service (as using the same controller multiple times in the DOM is not recommended and a bad practice).
With the Directive API, you can put the markup in a directive template and consume the service in a directive controller to render the UI. So the next time if you want multiple instance of the widget, you just have to inject the directive, that's it - rest will work without any issue.
App = angular.module('App', []);
App.directive('welcomeQuote', function(QuoteService) {
return {
restrict: 'E',
template: '<div><img ng-src="{{quote.img}}" /><span ng-bind="quote.title"></span></div>',
controller: function(scope) {
// returns {img: 'angular.png', title: 'AngularJS';
QuoteService.fetch().then(function(data) {
scope.quote = data;
});
}
}
});
App.factory('QuoteService', function($http) {
return function() {
fetch: function() {
return $http.get('http://quote-server.com/new')
}
};
});
Finally you can use the widget as:
<welecome-quote></welcome-quote>

Duplicate controller when using angularjs

I'm usnig AngularJs 1.2.25. I have a route config:
$routeProvider.when(
'/config-root',
{
templateUrl: configRootTemplate,
controller: "ClientConfig",
}
).when(
'/config-action/:action/:_id',
{
templateUrl: configActionTemplate,
controller: "ClientConfigAction",
}
).otherwise({redirectTo: '/'});
In the ClientCOnfig controller, I have a $scope.listConfig variable binding with template for creating, updating list of config.
Everything ok when the first time I visit "config-root" route, I can generate, update... the list.
But when the second time or so far, $scope.listConfig change properly when generate list, update... but the template does not change.
I think when I visit that route again, a new instance of $scope created and does not binding with the template.
How can I fix it?
You can use rootScope to preserve the value for the next time.
change that $scope to $rootScope and access that when ever its required.
If you don't want to pollute the $rootScope you can just store the values in a localStorage as a string and fetch it when ever its required.
// To store the value
localStorage['config'] = JSON.stringify(someObj)
// To fetch the value
var myConfigObj = JSON.parse(localStorage['config'])

AngularJS better way to manage partials and controller data

I've been using directives in AngularJS which build a HTML element with data fetched from the $scope of the controller. I have my controller set a $scope.ready=true variable when it has fetched it's JSON data from the server. This way the directive won't have to build the page over and over each time data is fetched.
Here is the order of events that occur:
The controller page loads a route and fires the controller function.
The page scans the directives and this particular directive is fired.
The directive builds the element and evaluates its expressions and goes forward, but when the directive link function is fired, it waits for the controller to be "ready".
When ready, an inner function is fired which then continues building the partial.
This works, but the code is messy. My question is that is there an easier way to do this? Can I abstract my code so that it gets fired after my controller fires an event? Instead of having to make this onReady inner method.
Here's what it looks like (its works, but it's messy hard to test):
angular.module('App', []).directive('someDirective',function() {
return {
link : function($scope, element, attrs) {
var onReady = function() {
//now lets do the normal stuff
};
var readyKey = 'ready';
if($scope[readyKey] != true) {
$scope.$watch(readyKey, function() {
if($scope[readyKey] == true) {
onReady();
}
});
}
else {
onReady();
}
}
};
});
You could use $scope.$emit in your controller and $rootScope.on("bradcastEventName",...); in your directive. The good point is that directive is decoupled and you can pull it out from project any time. You can reuse same pattern for all directives and other "running" components of your app to respond to this event.
There are two issues that I have discovered:
Having any XHR requests fire in the background will not prevent the template from loading.
There is a difference between having the data be applied to the $scope variable and actually having that data be applied to the bindings of the page (when the $scope is digested). So if you set your data to the scope and then fire an event to inform the partial that the scope is ready then this won't ensure that the data binding for that partial is ready.
So to get around this, then the best solution is to:
Use this plugin to manage the event handling between the controller and any directives below:
https://github.com/yearofmoo/AngularJS-Scope.onReady
Do not put any data into your directive template HTML that you expect the JavaScript function to pickup and use. So if for example you have a link that looks like this:
<a data-user-id="{{ user_id }}" href="/path/to/:user_id/page">My Page</a>
Then the problem is that the directive will have to prepare the :user_id value from the data-user-id attribute, get the href value and replace the data. This means that the directive will have to continuously check the data-user-id attribute to see if it's there (by checking the attrs hash every few moments).
Instead, place a different scope variable directly into the URL
My Page
And then place this in your directive:
$scope.whenReady(function() {
$scope.directive_user_id = $scope.user_id;
});

Resources