Angular 1.5 $routerOnActivate on Root Router - angularjs

I want to make sure that the user is logged in properly before proceeding to any of the components he/she's trying to reach, if they're not logged in. Send them to login.
My idea is to do a check in the $routerOnActivate in the root router. Which to me would solve the issue for any sub routes.
However nothing seems to happen if i just try to log something. Example:
angular
.module('app')
.config(function($locationProvider) {
$locationProvider.html5Mode(true);
})
.value('$routerRootComponent', 'app')
.component('app', {
templateUrl:'landing.html',
controller: MainController,
$routeConfig: [
{ path: '/', name: 'Dashboard', component: 'dashboard', useAsDefault: true },
{ path: '/media', name: 'Media', component: 'media'}
...
]
});
function MainController(){
this.$routerOnActivate = function(next, previous){
console.log('activated', next, previous);
};
}
The same code this.$routerOnActivate works if i put it in any of the Components which are specified in the routeConfig. However obviously I don't want to make the same check in every component, but rather solve it once globally.
What is the approach for 1.5?

Instead of performing a check when you load a page, use following:
Angular has a special component lifecycle hook to handle these edge cases named $canActivate - to check whether it should try to activate it or not. You may talk to a authentication/authorization service to validate that for a given user's state, are they allowed to reach your component or not.
This way your component will be smart enough to encapsulate any checks required to activate itself.
Also, you can inject any service like $http or your custom service to talk to your server. In the code snippet below, I mimic this call using $timeout that just returns true, if you return false, your component will not activate.
angular.module('app').component('someComponent', {
template: 'this is an inline component template',
$canActivate: function ($timeout) {
return $timeout(function(){
return true;
}, 2000);
}
});
Use another hook named $routerOnActivate to read the next and previous routes. If you are interested in the params of a route, use next.params which is an object that will always have the parameters that were passed to that route. e.g. next.params.id where id is the parameter that was passed to the requested route.
Use $canActivate using TypeScript:
I've written a post regarding how to use write AngularJS components in TypeScript and have some drafts to use router lifecycle hooks that I'll publish today. Till then, here is the code to use some hooks using TypeScript below:
class ReviewDetailsComponent implements ng.IComponentOptions {
templateUrl = 'app/components/review-details.component.html';
// function member for this hook doesn't work!!
// so either use lambda expression or function directly.
$canActivate = $timeout => $timeout(() => true, 3000);
controllerAs = 'model';
controller = ['$http', ReviewDetailsController];
}
angular.module('app').component('reviewDetails', new ReviewDetailsComponent());
The typescript code above is same as javascript code snippet above that uses $canActivate.
Unfortunately, it didn't worked when this is defined as a function member in class like $canActivate() and the generated javascript is this member defined using prototype like ReviewDetailsComponent.prototype.$canActivate.
But it works well when written using lambda expression syntax or a function directly. If you are using a class to define a component, it is good to choose lambda expression in this case.
Use $routerOnActivate using TypeScript
The linked controller in this case also uses another lifecycle hook named $routerOnActivate and this works well if defined as a function member:
interface IReviewDetailsController {
id: number;
$routerOnActivate(next, previous);
}
class ReviewDetailsController implements IReviewDetailsController {
id: number;
constructor(private $http: angular.IHttpService) { }
$routerOnActivate(next, previous) {
this.id = next.params.id;
// talk to cache or server to get item by id
// & show that on UI
}
}

Paste from my comment as requested
What about perform your check on the loading on the page ? This would run perfectly in an angular.run.
And if you want to handle session expiration, you can add an interceptor to all requests and watch for a 401 response.

Related

Why getting provider error when trying to inject resolved property in controller using ui-router?

I am unable to inject resolve property of ui-routing in controller.
It is giving
Error: $injector:unpr
Unknown Provider
When I'm using controller property in state definition object as following
.state('widget', {
url: '/widgets',
template: '<h1>{{name}}</h1>',
controller: function(widget, $scope) {
$scope.name = widget.name;
},
resolve: {
// standard resolve value promise definition
widget: function() {
return {
name: 'myWidget'
};
},
// resolve promise injects sibling promise
features: function(widget) {
return ['featureA', 'featureB'].map(function(feature) {
return widget.name+':'+feature;
});
}
}
});
Then it is working fine and I'm able to get the widget in controller and able to use in html.
Please see the fiddle for code.
http://jsfiddle.net/sunilmadaan07/ugsx6c1w/8/
Might be I'm making a silly mistake.
Before posting this question I have tried returning with simple object, promise object to the property.
Thanks In Advance.
You can not get resolved data in the directive with the code you did. Basically, you are trying to implement component based structure with an older version of angular 1.3.x.
You have two options to achieve this.
Create route controller then you can access resolve to the controller as local dependency then use that dependency as binding to the directive.
Here is example - http://plnkr.co/edit/TOPMLUXc7GhXTeYL0IFj?p=preview
Upgrade angular version to 1.5.x and use "ui-router-route-to-components": "^0.1.0"
Here working example of your code - http://jsfiddle.net/ugsx6c1w/25/
In order for the controller to be able to use resolvers, it should be route component (only in UI Router 1.x) or route controller.
widget and features are local dependencies, they aren't registered in the injector and aren't available anywhere in the application except route controller/component.
As explained here, resolvers can be passed to nested components in UI Router 0.3.x and injected directly to route components in 1.x.

How to pass down a callback from a parent component to a nested routed component?

I have the following format where I have a global component that has 3 nested components that are activated based on a given route:
$stateProvider
.state('create-goal', {
url: '/create-goal',
component: 'createGoal',
redirectTo: 'create-goal.step-1'
})
.state('create-goal.step-1', {
url: '/step-1',
component: 'step1'
})
.state('create-goal.step-2', {
url: '/step-2',
component: 'step2'
})
.state('create-goal.step-3', {
url: '/step-3',
component: 'step3'
});
Inside of the main create-goal html file, I have the following:
<ui-view goal="$ctrl.goal"
goalInfo="$ctrl.goalInfo"
processStep1="$ctrl.processStep1">
</ui-view>
The goal and goalInfo work great as they are data that is one way data bound. However, when I want to pass down a function, such as processStep1 to compute some action on step-1 and so forth, that function does not show up in the step-1 component even though the goal and goalInfo do.
export default {
name: 'step1',
restrict: 'E',
bindings: {
goal: '<',
processStep1: '&'
},
template,
controller
};
Thoughts?
There a few ways you could go about doing this.
Access the function using $parent (I recommend against this, as it is relative and can cause problems
Pass it down using attributes to inject it into each child function using the '=function' or '&function' depending on your use case
Set the function in a helper service that can be injected anywhere. This is great if you plan on using this a lot and if it is just for data manipulation it really belongs in a service anyways to avoid bloated controllers.
The last way would be use models. This would be good for if it is a function to be run on a certain type of object like users that can easily be modeled. Such as a .save or .update function.
https://docs.angularjs.org/guide/directive
http://fdietz.github.io/recipes-with-angular-js/controllers/sharing-code-between-controllers-using-services.html
https://www.sitepoint.com/tidy-angular-controllers-factories-services/
http://www.webdeveasy.com/angularjs-data-model/

Sequential Angular stateProvider resolves

I need to perform router resolves in order. Meaning first run ProfileLoaded http call, when that finishes, run Access resolve. How can I do this?
$stateProvider.
state('sign-up', {
url : '/sign-up',
templateUrl: '/html/pages/registration.html',
controller: 'signupCtrl',
resolve : {
// Load profile first, then check if they are subscribed
ProfileLoaded : ['$rootScope', function ($rootScope) {
return $rootScope.loadProfile();
}],
// Then check Access
access: ['Access', function (Access) {
return Access.isSubscribed();
}]
}
})
Where $rootScope.loadProfile(); is an AJAX $http request and, where Access.isSubscribed relies on the loaded profile to perform auth'ed routing.
The logical choice would be to put access as a callback in loadProfile, but don't want it to get messy.
Assuming $rootScope.loadProfile returns a promise, simply add the required resolve property as a dependency, eg
access: ['Access', 'ProfileLoaded', function(Access, ProfileLoaded) {
return Access.isSubscribed();
}]
See http://angular-ui.github.io/ui-router/site/#/api/ui.router.util.$resolve#methods_resolve
Dependencies of invocables are resolved (in this order of precedence)
from the specified locals
from another invocable that is part of this $resolve call
from an invocable that is inherited from a parent call to $resolve (or recursively from any ancestor $resolve of that parent).
and
Invocables are invoked eagerly as soon as all dependencies are available.

Unable to inject service in angular js

I'm constantly failing to inject the $log service in a minify save way into a controller class of a component.
To check the injection is save, I added ng-strict-di to my app. This in terms causes an SearchResultController is not using explicit annotation and cannot be invoked in strict mode which is fine, since I'm relying on implicit injection right now.
So I added an explicit injection to my component implementation:
import htmlTemplate from './searchInput.html';
class SearchInputController {
constructor($log) {
this._log = $log;
this.searchText = 'Text to search for';
}
handleUpdate() {
this.onChange({value: this.searchText});
}
doubleMe(i) {
this._log.debug('SearchInputController.doubleMe: i='+i);
return i+i;
}
}
SearchInputController.$inject = ['$log'];
let searchInputComponent = {
template: htmlTemplate,
controller: SearchInputController,
bindings: {
onChange: '&'
}
};
export default searchInputComponent;
This has no effect the error message is still complaining about the missing injection.
I also tried ng-annotate-loader and ng-annotate-webpack-plugin. And also tried the /*#ngInject*/ and 'ngInject'; type annotations. All of this has no effect.
Any ideas how to get the dependency injection working?
Your annotation appears to be correct for SearchInputController, but the error message is complaining about a different controller SearchResultController. I suggest you need to annotate that one also.
Use https://babeljs.io/repl/ to check how ES6 compiles down to ES5. This can be useful when trying to trace errors in annotation. Using ng-annotate in some form should also work when you annotate the correct controller.

Execute a function before changing route in Backbone.js

How can I get notified before a Backbone router call the specific routing function?
I'd like to have a generic "reset" function before rendering every page.
Is there any event I can bind?
Update: the solutions I found are based on extending the router or the history in order to trigger the event.
It looks like the 1.1.x release of Backbone has what you want with the Router.execute method:
MyRouter = Backbone.Router.extend({
routes: {},
// fired before every route.
execute: function(callback, args) {
// ...
}
});
if execute function is present it will be called before every route change but you must pass the arguments in callback to properly execute other matching routes.
MyRouter = Backbone.Router.extend({
routes: {},
// fired before every route.
execute: function(callback, args, name) {
//your logic
if (callback) callback.apply(this, args); //this must be called to pass to next route
},
});
This plugin does what you want. It works with 0.5.3. I'm not certain if it works with 0.9.1 yet or not.
https://github.com/angelo0000/backbone_filters
I' am using this in the constructor and it's working fine
this.bind( "all", this.ACL );
Here ACL is a function

Resources