What is the Angular2 equivalent to an AngularJS $routeChangeStart? - angularjs

In AngularJS we were able to specify route change event to observe changes in route object using the $routeChangeStart/End event of the $rootScope. What is the equivalent of the route change event in Angular2?
how can we do this exact functionality of below code in Angular2
$scope.$on('$routeChangeStart', function (scope, next, current) {
//do what you want
});
I got some disucussions here, But it don't have more details, So I asked a new question.
angular2 $routeChangeStart , $routeChangeSuccess ,$routeChangeError

You can listen the events of the router by doing the following:
import {
Router, ActivatedRoute,
NavigationEnd, NavigationStart,
NavigationError, NavigationCancel,
} from '#angular/router';
// constructor method of some angular element
constructor(
private _router: Router,
) {
this._router.events
.filter(event => event instanceof NavigationStart)
.subscribe(event => {
console.log("New route");
});
}
EDIT: Im not completely sure is that is actually what you need, after taking a closer look to the angularjs docs seems like those events are more related to the resolution/result of a guard in angular2

Related

Use routing from Angular Components in Hybrid mode

I'm using AngularJS 1.5 and Angular 7 in hybrid mode. How can I change the url in an Angular component? I tried like to access the current url as follows:
constructor(private router: Router, private route: ActivatedRoute) {
console.log(this.route.snapshot.queryParams);
}
The query parameters are always empty. In a parent AngularJS component I get the query parameters using the $location service.
If I try to change the route using the router, it says invalid route. How can I change the current url in an Angular component? Currently I'm emitting an event, which in turn is grabbed by the parent AngularJS component who can use the $location service.
I managed to do it by upgrading the AngularJS $location service:
#Injectable()
export class LocationService {
search(queryParameter?: string, value?: string): string | any {
return undefined;
}
}
In AppModule do the following
{provide: LocationService, useFactory: (i: any) => i.get('$location'), deps: ['$injector']},
Use the following way to get the query params :
constructor(private router: Router, private route: ActivatedRoute) {
this.route.queryParams.subscribe(params => {
console.log(params);
});
}
to navigate to other url (as you specified, ie to change url ) then use:
this.router.navigate(['/someurl']);

Angular 1.5 $routerOnActivate on Root Router

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.

How to $emit events from Angular 1.4 new router components to parent controller?

In new angular components, $scope cannot be injected so I can't use the standard $emit.
How to I emit a value from a component to parent controller?
I have this in appCtrl:
$scope.$on('eventName', function (event, args) {
this.pageTitle = args.pageTitle;
}.bind(this));
Usually I have done it using:
this.scope.$emit('eventName', { pageTitle: _this.campaign.title });
I have a similar issue. I need to do a $scope.apply() in a component.
Check this issue here How can we watch expressions inside a controller in angular 1.4 using angular-new-router
I am not sure if using / injecting $scope into a component is the way to go because of the migraiton path to Angular 2. Maybe there is a better way using zone.js https://github.com/angular/zone.js/

Angular js Routing

I am a rails developer new to angular js.
In MVC framework languages we can specify routes for an action or method in controller.
Is there any way to call a function of controller in angular js using angular ui-router
Well, you won't automatically fire a method by specifying correct routing, but you can manage it on your own, like this:
angular('app').config(function( $routeProvider ){
$routeProvider.when("/ctrl",
{
action: "ctrl.default"
}
);
});
angular('app').controller('ctrl', function($route) {
render = function() {
$scope.action = $route.current.action.split( "." );
}
$scope.$on("$routeChangeSuccess",function( $currentRoute, $previousRoute ){
render();
});
Basicly, event gets fired every time elements in route are changed, meanwhile in render() method you can work on case switching correct action for provided parameters in $scope.action
As you can see, the variable action is the one, which holds info that is passed to controller.
Above code is written without testing, so beware :p

Backbone.js history 'on route change' event?

Is there a general event that fires every time we navigate to a different URL?
window.App =
Models: {}
Collections: {}
Views: {}
Routers: {}
init: ->
# Initialize Routers
new App.Routers.Main()
# Initialize History
Backbone.history.start(pushState: true)
# BIND VIEW CHANGE?
$(#).on 'changeOfRoute', ->
console.log "Different Page"
$(document).ready ->
App.init()
Doing this per view is possible, but I'm looking for a general solution.
There is the "route" event on the Router:
http://backbonejs.org/#Events-catalog
"route" (router, route, params) — Fired by history (or router) when any route has been matched.
This allows you to bind to specific routes.
If you want to fire a handler after any route, bind to "route", and the route will be the first argument:
myRouter.on("route", function(route, params) {
console.log("Different Page: " + route);
});
This will only trigger events for your explicitly defined routes. If you want to trigger events for routes that are not explicitly defined, then add a 'splat' route as per How to detect invalid route and trigger function in Backbone.Controller
From the Backbone docs
This method is called internally within the router, whenever a route matches and its corresponding callback is about to be executed. Override it to perform custom parsing or wrapping of your routes, for example, to parse query strings before handing them to your route callback, like so:
var Router = Backbone.Router.extend({
execute: function(callback, args) {
args.push(parseQueryString(args.pop()));
if (callback) callback.apply(this, args);
}
});
This site has some useful code for redefining the Router to support 'before' and 'after' hooks, though it would require updating with each version-change of Backbone.
#TTT: Unfortunately Backbone doesn't give us a before/after event, so you will need to overwrite or extend the Router.route. You can find the way to do that in this answer: https://stackoverflow.com/a/16298966/2330244
May be this extension would be useful for you:
https://github.com/zelibobla/Backbone.RewindableRoute/blob/master/backbone.rewindableRoute.js

Resources