Double state change in ui-router when using $state.go in $stateChangeStart - javascript

Here's my plunker. If you click on the profile link and look at the generated state change list:
stateChanges = [
" -> home",
"home -> profile",
"home -> signIn",
"signIn -> signIn"
]
you can see there's an unexpected extra state change "signIn -> signIn".
I've done some debugging and it seems it's ui-router's reaction to $locationChangeSuccess via the afterLocationChange function. But I'm not sure if that's a bug in ui-router or a result of how I configured ui-router and how I manage redirection. If the behavior is my fault, how do I fix it?
I would really appreciate the help, as the double state change causes my query parameters to be double URL encoded: /profile -> %2Fprofile -> %252Fprofile instead of just the first two.
UPDATE
It seems this is something that came with 0.2.15. I tried using older releases and even 0.2.14 works fine. After some investigation I found the problem came with commit 48aeaff. If I uncomment the code that was commented out by that commit, this issue goes away even on 0.2.15. For now I will use this modified version of the 0.2.15.
I also found that there's an issue in ui-router's issue tracker for this #1573.

kkIt looks like the problem lies in the Querystring you added in your $stateProvider
plunkr Demo
.state("signIn", {
parent: "frontpage",
url: "/signIn?returnTo", // <--- remove the ?returnTo
authorized: false,
views: {
"main#frontpage": {
template: "sign in page"
}
}
})
When this is changed to simply /signIn it should be fine. You might not be able to put ?queryStrings in your stateProvider allocations.
url: "/signIn" // It should work fine

As I went through your code, I found this block:
else if (authorized === true)
{
if (!service.authentication.authorized) {
$state.go("signIn", {
returnTo: toState.url
});
}
}
When you actually click from Home --> Profile, you would observer that authorized = true, and service.authentication.authorized = false. So, you're asking the browser to navigate to signIn state. However, you've clicked to Profile, and you're returning the browser to the same state that it was in.
I removed the statement event.preventDefault();from the above block, and it seems to be working fine now.

Related

Vue $route.push not working when called from method

I'm building a search bar for my app with vue-bootstrap-typeahead autocomplete lib, if your not familiar with it, when you click a suggested result it triggers the #hit event, passing the result data to the method.
<vue-bootstrap-typeahead
...
#hit="goToResult"
...
/>
Then I have the goToResult method
methods: {
goToResult(result) {
this.$router.push({name: 'market', params: {marketId: result.id}});
},
...
}
When I do search from a non-market route it works fine, redirecting the user to the desired /market/:marketId route, but when it's done from a "market" route it just changes the URL but doesn't redirects to the new market, it even triggers the "duplicated route" error if I click the same result twice, but still not redirecting.
Any suggestion?
Thanks
Check out the note at the bottom of the router.push section: https://router.vuejs.org/guide/essentials/navigation.html
Note: If the destination is the same as the current route and only params are changing (e.g. going from one profile to another /users/1 -> /users/2), you will have to use beforeRouteUpdate to react to changes (e.g. fetching the user information).
...and here is how to use beforeRouteUpdate:
https://router.vuejs.org/guide/essentials/dynamic-matching.html#reacting-to-params-changes
Hope this helps!

Ember - abort the route and reload the route with same model

In my ember app, when the user clicks the Back button of browser,I need to stop the transition (whereever it might take me as per Ember history) and reload the same url with same model. Have tried the below code, but doesnt seem to work:
search-route.js
var route = Ember.route.extend({
actions:{
willTransition: function(transition){
if(this.controller.get('order') === 1){
transition.abort();
this.transitionTo('search',model)
}
}
}
})
This doesnt seem to work and gives error about query params. So, i looked for what is there in transition object. Saw that as soon as I enter this code, the object transition contains prop queryParams but with the old values, not the current URL one. But there is another prop - transition.intent.preTransitionState.fullQueryParams which contains the current URL query params. Would that be used here somehow.
I looked for solutions and someone also suggested to put in this.refresh(), but also didn't work.
I'm trying on my own ember app and doing a transition.abort() followed with a this.refresh() works.

How to persist optional state parameter on browser back in ui-router?

I'm having one parent state that has two children's state inside that I'm going to show one state based on the URL.
Out of those two states one is having to parameters like param1 and param2, I have use params option of ui-router inside state definition.
State
$stateProvider.state('tabs.account', {
url: '/account',
views: {
'content#tabs': {
templateUrl: 'account.html',
controller: function($scope, $stateParams) {
//This params are internally used to make ajax and show some data.
$scope.param1 = $stateParams.param1;
$scope.param2 = $stateParams.param2;
},
}
},
params: {
param1: { value: null }, //this are optional param
param2: { value: null } //because they are not used in url
}
});
If you look at my route the params option is not really introduced inside the URL, that's why I'm considering then as optional.
Problem
Look at plunkr, I've shown two tabs Account & Survey,
Click on Survey tab, then add some data in the textarea which are shown.
Click on Go to Account that will pass those textarea values to
the other Account tab by doing ui-sref="tabs.account({param1: thing1, param2: thing2})" on the anchor
Now you will see the param1 & param2 values on html which has been assigned to scope from $stateParams
Now again Click on Survey tab, you will land on the survey page.
Just click browser back, you will notice that param value is not getting null.
Problem Plunkr
I believe you got what I wanted to ask, why the optional parameter value has not been store? as they have been a part of state.
I know I can solve this issue by below two solutions.
By creating one service that will share data between two views.
By adding parameter inside the state URL. like url: '/account/:param1/:param2', (But i wouldn't prefer this)
I already tried angular-ui-routers sticky states but that doesn't seems to work for me. What is the better way to this?
Is there any way by which I can make my use case working, Any ideas would appreciate.
Github Issue Link Here
I would move the params definition to the parent state, so as to share the optional state params between your two child states.
The child states will inherit the $stateParams from your parent, as such there is no real 'workaround' needed.
Simply inject $stateParams as per usual in your child controllers and you will have full access to the params being passed around. If you don't want to utilise the params in a specific child state, simply avoid injecting them.
This works with;
Back button
Forward button
ui-sref (without params (will keep as-is))
ui-sref (with params (will overwrite))
$stateProvider
.state('parent', {
params: { p1: null, p2: null }
})
.state('parent.childOne', {
url: '/one',
controller: function ($stateParams) {
console.log($stateParams); // { p1: null, p2: null }
}
})
.state('parent.childTwo', {
url: '/two',
controller: function ($stateParams) {
console.log($stateParams); // { p1: null, p2: null }
}
})
If you at any point want to clear the params while travelling within the state tree of parent, you would have to do so manually.
That would be the only real caveat I can see by using this solution.
I realise manual clearing may not be desirable in the case you present, but you haven't taken an active stand against it, as such I feel the suggestion has merit.
updated plunker
One workaround solution is to cache the state params and conditionally load them when entering the tabs.account state. UI Router state config actually lets you provide an onEnter callback for these types of "do something on entering the state" situations.
Here's the basic logic using localStorage as the cache, with working Plunker here:
When you enter the tabs.account state, check for your state params
If you have them, cache them to local storage
If you don't, load them from local storage into $stateParams
Here's an example code snippet for reference (taken from the Plunker):
$stateProvider.state('tabs.account', {
...
onEnter: ['$stateParams', '$window', function($stateParams, $window) {
if($stateParams.param1) {
$window.localStorage.setItem('tabs.account.param1', $stateParams.param1);
} else {
$stateParams.param1 = $window.localStorage.getItem('tabs.account.param1');
}
if($stateParams.param2) {
$window.localStorage.setItem('tabs.account.param2', $stateParams.param2);
} else {
$stateParams.param2 = $window.localStorage.getItem('tabs.account.param2');
}
}],
...
}
One caveat is that your params will persist indefinitely (e.g. across refreshes and sessions). To get around this, you could clear out the cache on application load like in app.run.
One last note is that in the Plunker, I'm accessing local storage directly (through the Angular $window service). You might want to use some AngularJS module - I've used angular-local-storage in production.
I believe that what you want to achieve is not possible without using one of the two solution you provided.
The browser back-button is just keeping the URL history. He have no clue about the ui-router internal states and will just force the URL to change.
Forcing the URL to change will trigger internal ui-router machine but unfortunately ui-router will see the URL change the same as if someone would have change the url by hand.
Ui-router will fire a new route change to the route pointed by the URL. That mean he doesn't know you wanted to go "back" and will just change state to the new one without any parameters.
Summary
Clicking on back button will fire a state change to a new state according to the URL instead of going back to the previous state.
This is why adding the params to the URL solve the issue. Since the URL is discriminatory you'll finally land on the state you wanted.
Hope it helped.
To me this sounds as X Y problem. There are suggested ways to make state params persistent whereas the problem is located out of this surface.
By definition of the question there is data that should be kept independent of states. So it is kind of global relative to states. Thus there should be a service that keeps it and controllers of both states maintain it as required. Just don't pass data that is out of one state's scope in state params.
Sticky states would fit this approach easily since it allows to keep DOM and $scope while another state is active. But it has nothing to do with state params when the state is reactivated.

EmberJS optional parameter

I'm working on a rather large EmberJS app and I've come to the part where I want to be able to retrieve a tracking ID in the URL from any source. I've looked into Ember.Route.serialize, and a couple of similar questions here on SO, but no reply seems to properly solve the problem (i.e., I have been unable to implement them on my site). The serialize-hook for example is only called on transitions, and to make sure the parameter is read, you'd have to expressively specifiy child-routes to pick it up.
I would like to have a route like this:
mysite.com/#!/ --> start page.
mysite.com/#!/19 --> start page with tracking ID 19.
mysite.com/#!/about --> about page.
mysite.com/#!/about/19 --> about page with tracking ID 19.
It seems that currently, to be able to fetch a parameter from any URL, you'd have to manually create a child route for every single route to retrieve it in the router:
App.Router.map({
this.resource("index", { path: "/" }, function() {
this.resource("indexTid", { path: ":tId" });
});
this.resource("about", { path: "/about" }, function() {
this.resource("aboutTid", { path: ":tId" });
});
[ ... ]
However, this seems incredibly tedious and with so many routes, having to add individual route handlers (App.AboutTidRoute = Ember.Route.extend [...])...
I'm figuring someone's had this problem before. How do you best tackle this problem?
Alternative solutions are welcome, but note that the URLs should be possible do give to partners and the tracking should follow without further work put in from their side.
Thanks for your time.
Best regards,
dimhoLt
PS. My current solution uses this URL mysite.com/?trackingId=19#!/about, which solves the problem-ish by setting a cookie on the server, but isn't very pretty. A better solution is greatly appreciated.
Beta/canary versions of Ember.js(1.4.0-beta.2) include support for query params. The guides page can be found here.
You'll have to use a beta or canary version and manually enable the feature to use it:
ENV = {FEATURES: {'query-params-new': true}};
That guide includes an example of setting/accessing a globally available query param:
App = Ember.Application.create({
LOG_TRANSITIONS: true,
LOG_VIEW_LOOKUPS: true
});
App.ApplicationController = Ember.Controller.extend({
queryParams: ['iamglobal'],
iamglobal: 'foo'
});
App.IndexController = Ember.Controller.extend({
needs: 'application',
iamglobal: Ember.computed.alias(
'controllers.application.iamglobal'
)
});
jsbin example

Ember.js history not working after transitionTo

please see this functioning JSBin: http://jsbin.com/acUm/20
Here is the behavior I am working on. If I type 'Monroe' to filter the list down and then hit the browser back button, I expect Ember to process the route and fire the request for all patients. Instead it appears to do nothing. This is especially confounding since the back button seems to work in other areas.
Perhaps I have set up this transition improperly? Or is this an Ember.js bug?
When you transition to a route, it's a good idea to use the childest route in the hierarchy.
In your case you have this:
this.resource('patients', { path: '/' }, function() {
// this is created for you
// this.route('index');
this.route('filtered', { path: '/filtered/:last_name' });
});
By default is created a route index for that resource, so you have the patients.index.
But your transition goes to patients and it isn't the childest.
So to correct this, I have changed your sample to use PatientsIndex[Controller,Router etc], instead of Patients[Controller,Router etc].
Working demo http://jsbin.com/acUm/24/edit

Categories