angular ui-router - how to detect direct navigation to a state - javascript

I'm using $transitions to detect state changes and take some action based on the difference between some data defined in the 'from' and 'to' states. This is working great for me, but this code is not hit when the user navigates directly to a state via url. For example, browsing to mysite/search does not trigger this, but navigating within the site from the home page to the search page triggers this.
Is there any way to detect that the state was directly navigated to? That would allow me to execute the logic that I need to.
$transitions.onSuccess({}, trans => {
myFunc(trans.$from().data, trans.$to().data);
return true;
});

My problem was that I didn't register the transaction early enough; it was in a service that wasn't loaded until after transitions were handled for the initial launch of the app.

Related

Detect previous path in react router if i came from an outside application?

I am using react router. I want to detect the previous page (outside app example: coming from gmail to my application) from where I am coming from. I have the router in my context. But, I don't see any properties like "previous path" or history on the router object. How do I do it?
example 2: user places an order we send them an email confirmation via email where onclick on view-order-details they are navigated back to our web app, now how to detect which link they came from?
Here's another context to understand better
I have to implement a back button in my web app, when user comes from email to my webapp he's navigated to https://example.com/orders/{orderID} directly and if i just use props.history.goBack it'll go back to email page. But i want to user to be navigated to https://example.com/dashboard.
Now you might say use props.history.push('/dashboard') this will become static value. if user is navigated internally(not coming from email or external page) from dashboard->all-orders->orders/{orderID} on click of back button should be navigated to dashboard->all-orders page and not to dashboard.
To read the history in React-Router use the history object that's passed along with the routeComponentProps or the useHistory hook.
But that won't give you what you're looking for either:
There is no way to read entries in the browser history. Browsers simply don't expose that to JS at the moment. See this entry on the history API on MDN
In order to detect which site a visitor came from you can check the referrer. document.referrer on MDN
Example for a button that goes back one step in history for internal referrers, but goes to the dashboard if the site was loaded from a bookmark or external link:
button.onclick - () => {
if (document.referrer && document.referrer.length > 0 && document.referrer.startsWith('https://yoursite.com')) {
// user came from an internal link
window.history.goBack(1);
}
else {
// user came from a bookmark or an external link
window.location = 'https://yoursite.com/dashboard';
}
};
The HTML5 history allows you to go back to an arbitrary point in history, but you can't know the details about that point in history. You can only go back to it by a numerical index.

Re-initializing Angular application on a specific event: Angular 5

I'm using Angular 5 for building a web application and would like to know if I can re-initialize the entire application OR use something like:
ApplicationRef.tick();
to execute all the changes that happens after a specific event. The event is my scenario is authentication token renewal, because for some reason my application's change detection starts breaking unless I run each action inside
NgZone.run()
(I'm using ADAL for authentication in case anybody is interested), but when the token is renewed (using a hidden iframe) the application change detection, routing, etc starts breaking. But when the page is refreshed it starts working perfectly fine till the next time token expires and ADAL has to create an iFrame to renew the token. So I was thinking if at least I could re-initialize the application after the token is renewed so that user doesn't have to refresh the application manually (till I find a more solid solution).
You can call the change detection explictly like below.
constructor(private changeDetector: ChangeDetectorRef) {
this.someEvents.subscribe((data) => {
this.changeDetector.detectChanges();
} );
}
https://angular.io/api/core/ChangeDetectorRef
It's mandatory, if you are calling any third party API or executing third party library Codes. We need to manually call ngzone.run(), which will internally call ApplicationRef.tick() to notify angular to perform change detection from Application root component to the child component (i.e whole application).
ngZone.run(()=>{
// Adal callback Function
});
If you are need to trigger change detection only to the current component and their childrens (not whole application). You can use any one option.
1) setTimeout(()=>{}, 0);
2) this.cdr.detectChanges();
3) For Components with OnPush Change Detection Stratergy, you can call this.cdr.markForCheck() inside setTimeout();
setTimeout(()=>{
this.cdr.markForCheck();
}, 0);

Backbone js prevent url hash change when back/forward

I am working on a very basic SPA using Backbone.js. My app has few routes. Among them there are 2 that give me issues: the index route ("/#index") and menu route ("/#mainmenu").
A simple workflow in my app is as follows: the user fills a form -> clicks to login -> trigger ajax request -> if login successful go to "/#mainmenu" route. if login failed, remain on "/#index" route.
On "/#mainmenu" if the user clicks on logout -> ajax request -> if logout success go to "/#index". if logout failed remain on "/#mainmenu".
The issues that I am struggling with are:
A clean way to trigger transition to "/#mainmenu" after successful login (I currently use router.navigate("mainmenu", {trigger: true}); but read that should avoid using this approach, in derrick bailey's article https://lostechies.com/derickbailey/2011/08/28/dont-execute-a-backbone-js-route-handler-from-your-code/ )
A clean way to prevent the user to go back to the "/#index" when pressing Back button in the browser from "/#mainmenu" route. I will also would like to preserve the url hash to reflect the current view.
Prevent the user to go forward to "/#mainmenu" after successful logout.
Is that even possible to prevent url hash change when clicking browsers back/forward buttons?
When I say "clean" I refer to "what are the best practices?". I partially solved some issues by saving url hashes and restore the appropriate hash (by router.navigate(currentRoute, {replace: true}); ) but I feel that it's a hacky approach.
Any feedback is welcome and much appreciated.
One way to solve this problem is by applying an async before filter on the routes that require an auth status check before the actual callback route is executed.
For example:
https://github.com/fantactuka/backbone-route-filter
The philosophy of avoiding {trigger: true} is based on the fact that when the router gets triggered with this flag, the entire initialization procedure for that route gets triggered. You will lose the benefit of having previously defined appstates because the app will have to re-initialize all content while this work had alrady been done before.
In practice, I think that it is useful to assess what your web app actually does. If losing appstate isn't an issue because the views you want to render are entirely new, then I don't see a problem with creating a client side redirect that re-inintializes your app.
If, on the other hand, your app has many views already rendered for which you want to maintain the same state as before, you can listen for an auth state event on each component that requires it, and make only those views re-render accordingly if they need to.
I don't think there's anything wrong with triggering routes, have been doing this without any issue for 2+ years. It all boils down to your requirements, read the article looks like a lot of work to me.
There are multiple ways to do this. First, you can disable back/forward buttons using window.history.forward(). Second, my favourite, is to do the processing in Router#execute. A sample might look like :
execute: function(callback, args, name) {
if (!loggedIn) {
goToLogin();
return false; //the privileged route won't trigger
}
if (callback) callback.apply(this, args);
}

Load initial data after log in with an angularjs app

I have a completely separate (from backend) AngularJS app. It uses tokens (JWT) for authentication. I'm also using ui-router and Restangular.
My problem:
I have a sidebar/profile area that displays information from the currently logged in user on every "page". It is implemented as a directive+controller and is outside of the ui-view directive context. Inside of the controller for this sidebar I'm making a request to get the profile information and attach it to the $scope. Now, when a user first visits the app that sidebar and controller get invoked and the request is made to get the profile information. However, if and since the user is not logged in, the request fails and no data is bound with the $scope.
In the meantime (notice this all happens really fast), the event listener (listening for $stateChangeSuccess from ui-router) determines that the user is not logged in (essentially if they don't have a token) and does a $state.go('login') to forward the user to the login view. The user logs in, and a $state.go('dashboard') is invoked to bring them back to the dashboard. However, being that sidebar is outside of the context of the router the controller for it is not instantiated again to cause a new request for profile information. So the sidebar is just empty with no profile information.
I'm aware that there are going to be several possible solutions to this, but I'm trying to find a descent one.
How would you architect an angular app in order to solve or avoid the problem I'm having?
P.S. I think I may be having analysis paralysis over this.
It's hard for me to answer without seeing your code specifically. If I understand correctly your directive is firing prior to the user logging in, and since there is no user profile, the side bar doesn't initiate correctly. What I would suggest is possibly doing an ng-if on the tag that fires the directive something like:
<side-bar ng-if='userData' />
That way the tag isn't inserted into the DOM until the userData exists and therefore doesn't fire the directive on the login page.
Assuming that the sidebar is the highest angular controller in your application and the other controllers are nested inside it you should be able to put a function on it that will load the information that you need. Then you can call $rootScope.loadme() anywhere you inject $rootScope.
var mainMod = angular.module('mainMod',[]);
mainMod .controller('mainController', function($scope)
{
$scope.loadMe = function()
{
//load stuff here
};
$scope.loadMe();
});
mainMod .controller('secondController', function($rootScope, $scope)
{
$rootScope.loadMe();
});
SudoCode probably wont work with copy paste but the idea should be sound.

ui-router lazy load state using url

I am trying to see if I can use ui-router to delegate setting of state to my app's sub-components by implementing lazy loading of the states. While I managed to get the lazy loading part to work using $state.go or equivalent, I can't get it to work using the URL.
For example, on launch my app will only setup the following 2 states: view1 and view2. When view1 state is loaded, it then setup it's own children states of: view1.profile and view1.interest. Take a look at this sample site from Gist:
http://bl.ocks.org/marcoslin/raw/b59cfa9a9a44bde04f9f/
As you will see from the example above, View1Profile is not a valid link on launch, but if you click on it, it will load view1 and then load view1profile with resulting url:
http://bl.ocks.org/marcoslin/raw/b59cfa9a9a44bde04f9f/#/view1/profile
However, if you click on the generated url above, the app reloads and no longer knows about view1profile and redirect you back to home. Any recommendation on how I can address this? More specifically, is there anyway I can get the url to trigger $stateNotFound event?
Perhaps the answer is in part of their cryptic documentation on How to: Lazy load states. I wasn't able to figure out what they mean by:
how to set the retry promise on the event
how to define the unfoundState using stored provider and resolve the promise
Marcos,
See http://christopherthielen.github.io/ui-router-extras/example/future/index.html for integration of FutureState with AngularAMD.
See http://christopherthielen.github.io/ui-router-extras/#/future/ for API and overview of the $futureStateProvider.

Categories