Check if url leads to a defined state in AngularJS - angularjs

I use UI-Router and if the requested state doesn't exist in my config I want the app go to a certain state depending on whether the user is authenticated or not. Either 'home' or 'dashboard'.
I tried to use $urlRouterProvider.otherwise but the authenticated is not available there (in app.config). Then I tried in app.run to use 'stateChangeError' and 'stateNotFound' which interestingly have no effect at all here.
So my idea to work around this is to check if the state exists by using the URL - which I can get with $location.path(). But a $state.get() with this always delivers null because it expects the name of a state, not the URL.
So how can I find out, based on what the user enters, whether there is an according state?

Related

ReachRouter navigate in component constructor

If a user goes to a page that requires a context beyond what's on the url, I'd like to redirect them elsewhere. The use case is:
/todos/list - this page shows the user their list of todos. It contains links to:
/todos/edit?id=1 - this page allows the user to view/edit details about a particular todo.
If a user were to go directly to /todos/edit (with no id), I'd like to redirect them to /todos/list. I have tried doing this via navigate('list') conditionally in the constructor. This does update the browser url correctly, but it doesn't render the /todos/list page. Is this possible to do? Or is this not possible to do the para below?
I understand the more common url would be /todos/edit/1 so that reach router would handle my issue w/out me needing to deal with it. However, I'm just using this as an example of a piece of information required to render the page that isn't necessarily part of the the url path.
of course as soon as I type the question in stackoverflow, I find the answer is in the docs right in front of my face:
https://reach.tech/router/api/Redirect

DeepStateRedirect in angular ui-router 1 - how to reset the deep state?

I'm using angularJS and migrating to ui-router v1. I'm trying to get deep state redirects working like they used to in the previous version of ui-router.
I've successfully implemented the DSRPlugin in my config modules, and deep state redirects are firing and work as expected. However, I'm unable to reset the deep state. I need to be able to reset the deep state on a button click, which means logic within my component. Previously I could inject $deepStateRedirect into my controllers and simply call $deepStateRedirect.reset({}), but I'm no longer able to inject $deepStateRedirect. How can I access the reset method in ui-router v 1?
I have also noticed that when using DSR as a config object you can specify a function to determine if the redirect occurs. I could alternatively use this to determine whether to do the redirect or not, but the documentation is lacking. It shows that I should return a truthy value to do the redirect or a falsey value to prevent the redirect. In testing, returning true or false only causes a transition error: "i.state is not a function".
I'm not using a build process, just plain script includes.
Anyone have any ideas on how to make this work through either of the above methods?
This may not be the best practice way of doing the reset, but I found a solution after logging out various ui-router objects.
Inside of your controller you must inject the $uiRouter object. Then, you can set a variable to $uiRouter._plugins["deep-state-redirect"]. The reset() and other methods are available on the plugin's prototype.
You can then use that object and call those methods similar to how it worked in the previous version when injecting $deepStateRedirect.
var $deepStateRedirect = $uiRouter._plugins["deep-state-redirect"];
$deepStateRedirect.reset({});
I found this only in the source code and then in the documentation: https://ui-router.github.io/ng1/docs/latest/classes/core.uirouter.html#getplugin
The more correct way is to use UIRouter#getPlugin(pluginName), that is
var $deepStateRedirect = $uiRouter.getPlugin('deep-state-redirect');
$deepStateRedirect.reset(...);

Refreshing page removes non-URL $stateParam (ui-router)

Im using UI-Router in my application and I have a state parameter where it doesn't really make sense for it to be part of the url. According to the documentation I am creating the parameter without specifying it in the state url, as such:
.state('contact', {
url: "/:contactId/contact",
params: {
otherParam: null
},
templateUrl: 'contacts.html'
})
This works as intended, but I noticed that if I manually refresh the page, the parameter gets reset to the default null.
For example, if I redirect to the state as such:
$state.go('contact', {contactId: 42, otherParam: 11});
Everything works as expected ($stateParams.contactId is 42, $stateParams.otherParam is 11)
However, once I refresh the page, $stateParams.contactId is still 42, but $stateParams.otherParam has been set back to null. Is it possible to persist this non-URL parameter across a browser refresh?
URLs in a SPA are meant for managing the UI state, i.e.: which components to display and with which info (ex. detail component with id). The router resolves the state from this. A good example is to think of the url as something to be sent by email (or whatever) to someone else and expect for their router to resolve the state. So, briefly put I would say no to your question.
Your problem is that you want to persist state of the application that is specific to the browser session, right? Browsers are equipped with mechanisms for that and I'd recommend taking a look into local storage or session storage to solve your problem and retrieve the info you need on the resolve method in your state declaration.
It's sort of possible, but you shouldn't do it. If it is something that should persist on refresh, that means that by definition it SHOULD be in the URL, since you expect it to be a component of the resource.
If you really really want to break convention and do this, you'll need to do something that saves the data in window.name and retrieves it on reload. This is definitely an anti-pattern, and anyone could get access to that data, but like I said, it's possible.

Prevent deep link in react-router

In my application I'd like to have certain portions of the app not be able to deep linked to. For example our users have a list of surveys and I'd like if someone tried to go directly to a particular survey directly such as /survey/1 that react router would pick up on this and immediately redirect them back to /survey and they would have to select the one they want. I've tried to write onEnter hooks but they seem to be very cumbersome since the only way I've been able to get them to behave correctly is to store some global state that says they have been to the main page and inspect that every time the route is navigated to.
Im using pushstate in my application if that makes any difference and react-router 2.0
I'd like to try to avoid having to write server rewrite rules for this since there are a lot of areas in my application where this rule is applicable.
I have a suggestion which is similar to the onEnter hook:
Wrap the component of the survey/:id route with a function which verifies if deep linking is allowed or not, let's call this function preventDeepLinking
The preventDeepLinking function checks if the location state contains a certain flag, let's say allowDeep. This flag would be set in the location state when navigating from another page of your app. Obviously, this flag will not be set when the user tries to navigate directly to the page of a survey.
The preventDeepLinking function will render the wrapped component only if deep linking is allowed, otherwise will redirect to a higher route.
I created a sample on codepen.io. You can play with it in the debug view of the Pen: http://s.codepen.io/alexchiri/debug/GZoRze.
In the debug view, click the Users link and then on a specific user from the list. Its name will be displayed below. Notice that its id is part of the url. Remove the hash including the ?_ and hit Enter. You will be redirected to /users.
The code of the Pen is here: http://codepen.io/alexchiri/pen/GZoRze
The preventDeepLinking function can be improved, but this is just to prove a point. Also, I would use the browserHistory in react-router but for some reason I couldn't get it running in codepen.
Hope this helps.

Set URL query parameters without state change using Angular ui-router

How should I update the address bar URL with a changing query parameter using AngularJS' ui-router to maintain state when refreshing the page?
Currently, I am using $state.transitionTo('search', {q: 'updated search term'}) whenever input changes, but the problem is that this reloads the controller, redraws the window and loses any text input focus.
Is there a way to update stateParams and sync it to the window URL?
I was having trouble with .transitionTo until I updated to ui-router 0.2.14. 0.2.14 properly changes the location bar (without reloading the controller) using a call like this:
$state.transitionTo('search', {q: 'updated search term'}, { notify: false });
edit: Played around with this some more today, and realized that angular ui-router has a similar option as the native routerProvider: "reloadOnSearch".
https://github.com/angular-ui/ui-router/wiki/Quick-Reference#options-1
It's set to true by default, but if you set it to false on your state, then the state change won't happen when the query parameters are changed. You can then call $location.search(params); or $location.search('param', value); and the URL will change, but ui-router won't re-build the entire controller/view. You'll probably also need to listen for the $locationChangeStart event on the root scope to handle back and forward history actions within your app, as these also won't cause state changes.
I'm also listening for the $stateChangeSuccess event on my controller's scope to capture the initial load of the page/route.
There is some discussion on github for using this feature with path changes (not just URL changes): https://github.com/angular-ui/ui-router/issues/125 but I haven't tested that at all since my use case was specific to query string parameters.
The previous version of my answer mentioned this github issue:
https://github.com/angular-ui/ui-router/issues/562
But that's a slightly separate issue, specifically showing a modal of one state over another state without the non-modal state changing. I tried the patch in that issue, but it's clear that it isn't meant for preventing the entire state from reloading on URL change.
Update May 19, 2015
As of ui-router 0.2.15, the issue of reloading the state on query parameter changes has been resolved. However, the new update broke the history API back and forward capabilities with query parameters. I have not been able to find a workaround for that.
Original
Jay's answer didn't work for me, and neither did a ton of other answers. Trying to listen to $locationChangeStart caused problems when trying to go back and forth in the browser history as it would cause me to run code twice: once when the new state changed and another because $loationChangeStart fired.
I tried using reloadOnSearch=false, but that prevented state changes even when the url path changed. So I finally got it to work by doing the following:
When you change $location.search() to update the query parameters, use a "hack" to temporarily disable reloading on search, set query parameters, then re-enable reloading.
$state.current.reloadOnSearch = false;
$location.search('query', [1,2]);
$timeout(function () {
$state.current.reloadOnSearch = undefined;
});
This will ensure that query parameter changes do not reload the state and that url path changes will reload the state properly.
However, this didn't get the browsers history to change the state (needed for knowing when a query parameter changes to re-read the URL) when a query parameter was part of the url. So I also had to add each query parameter's name to the url property of the state.
$locationProvider.html5Mode(true);
$stateProvider
.state('home', {
url: '/?param1&param2&param3',
templateUrl: 'home.html',
controller: 'homeCtrl as home',
});
Any parameter names on the url are optional when listed this way, but any changes to those parameter names will reload the state when hitting the back and forward buttons on the browser.
Hopefully others find this useful and it doesn't take them multiple days to figure out how to do it (like I did).

Resources