I am using Angular UIRouter and have the following states set up:
$stateProvider.state('forgot-password', {
url: '/forgot-password',
component: 'forgotPassword'
})
.state('forgot-password.reset', {
url: '/:token',
component: 'forgotPasswordReset'
});
When I navigate to /forgot-password I get the correct view however when I navigate to /forgot-password/abc I still get directed to forgotPassword. How do I get the router to send me to the forgot-password.reset state?
There are few different reasons why this might be happening. Just off the top of my head:
You don't have a ui-view directive in the template for your forgotPassword component.
One or both of the components aren't registered (but you should see console errors thrown if this is the case).
You're hitting the wrong URL to try to activate the state.
Some other state is being activated for some unknown reason. Maybe you've set up a redirectTo rule on a state somewhere, or you're doing it from your controller.
UI Router is quite large and can get unwieldy if you're not careful, so it's hard to give too detailed of an answer without knowing your project. Please give some more details if any of the above suggestions don't work.
Related
How do I push a page using React and Ionic with functional props?
I did try:
history.push({
pathname: '/edit_some_property',
state: {
onSetSomeProperty(val) { setSomeState(val) }
}
})
But that throws:
DOMException: Failed to execute 'pushState' on 'History': onSetSomeProperty(val) { setSomeState(val); } could not be cloned.
Apparently, state is serialized to a string, so passing functions isn't going to work.
Or is there another approach that would work better for pushing screens with callbacks to the previous screen?
Background and Detail:
I have a very hierarchical style React + Ionic app.
Choose an item from a list, push a screen with item details.
Choose a sub item from a list, push a screen with sub item details.
Tap to edit an attribute, push a screen with form fields, and callback to the previous screen with the result.
Now it appears that in order to navigate from screen to screen, Ionic requires you to use React Router, where each page has a unique URL. But this doesn't work so well here since each screen depends on the state of previous screens.
This means that there is no apparent way to communicate rich props between pages. If I have a callback to the previous page, I can't pass that along because I don't have direct access to component being pushed with the router in the way.
There's also ion-nav https://ionicframework.com/docs/api/nav which is closer to what I think I need, but that doesn't seem to have a react interface at all.
I tried:
<IonNav root={Home} />
Which throws this, deep in obfuscated internals:
Error: framework delegate is missing
My original answer was way off base sorry. I did some testing, and found that anything passed to state needs to be serializable. In order to do this for a function, others have reported that https://www.npmjs.com/package/safe-json-stringify helped them out.
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.
I am working on an application which has both AngularJS and Angular. Right now, I am invoking Angular component from a html page using the below line:
System.import('app')
Inside app\app.module.ts file, I have specified a specific parent component to be bootstrapped. This parent component in turn invokes a child component. I put some console.log statements in the constructor of the parent and child component and I see everything works fine for the first time.
But, when I navigate away and comeback to the html page again, I notice that the parent and child components are not getting initialized. The only workaround I have found is to refresh the entire page which is not ideal. I tried to unload the Angular components as soon as the user navigates away but I couldn't find any suitable SystemJS methods.
I know Angular components gets initialized only once which is probably why this is happening but is there a way to get past this issue?
Thanks
I have managed to find an answer to my question. The trick is to use the fully qualified URL when importing and deleting the module like below:
System.import('protocol//domainname:portnumber/modulename/filename')
System.delete('protocol//domainname:portnumber/modulename/filename')
Hope this helps someone.
Maybe AngularJS bootstrap interfers with angular2.
https://angular.io/guide/upgrade#bootstrapping-hybrid-applications explains how bootstrap hybrid applications.
You can also try to migrate Angular 2 to Angular 5.
Well, this might be a long shot. But have you tried making your component to implement OnChanges and OnInit. You can call ngOnInit inside ngOnChanges to reinitialize the component again.
//this was my fix for a similar problem
In component, say:
#Component({
selector: 'app-myapp',
templateUrl: './myapp.component.html',
styleUrls: ['./myapp.component.scss']
})
export class MyappComponent implements OnInit, OnChanges {
ngOnChanges() {
ngOnInit(){}; //reinitialize when onchanges is called again
}
}
You should also consider https://angular.io/api/core/OnChanges#usage-notes.
hope it helps
I have the following state tree, using ui-router,
1 login
2 root (abstract, resolves app-prefs, user-prefs)
2.1 home (builds a refresh button, should refresh whatever is being shown)
2.1.1 dashboard (resolves dashboard-prefs)
2.1.2 search (resolves search-prefs)
2.1.3 etc
2.2 etc
From home when user presses refresh button while in XYZ state, I would like to have the XYZ re-entered in such a way that it re-resolves its own XYZ-prefs but not things above in hierarchy. Something like
$state.go("dashboard", dashboardParams, {please-resolve-only-dashboard})
When I try, from home
$state.go("dashboard", dashboardParams, {reload:true})
that causes everything from root downwards to get re-resolved, which is problematic, and expensive, as I need to re-resolve only dashboard-prefs. I can setup a more elaborate scheme in some resolvers to not re-resolve themselves but that might become a task by itself I'm afraid. Is there another, more idiomatic way?
Thanks
There is a wokring plunker
There is a native way how to do that - just change the parameter of the state you want to reload.
To reproduce the above state definition let's have dashboard defined like this:
.state('dashboard', {
parnet: 'home',
url: "^/dashboard",
params: { updater : 1, },
...
})
What we can see, that we do not touch url at all. It will always be without any change just /dashboard
But we introduce really cool feature of the latest version - params: {}. It defines some parameter - updater in our case. Whenever this parameter is sent, and does differ form its current value, this child state (and only this child state) is re-init
Check it more about state params: {} here: http://angular-ui.github.io/ui-router/site/#/api/ui.router.state.$stateProvider
Now, we can create this reload link:
<a ui-sref="dashboard({updater : ($stateParams.updater + 1) })">reload</a>
And with this incrementation, we can be sure, reload will reload this state
Check it here
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¶m2¶m3',
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).