Root VM's activate() won't receive query params - aurelia

In Aurelia, the root view-model's (app.ts, containing router config) activate(params) method doesn't receive query string parameters.
For the address: http://localhost:5000/home?param=1, the Home view-model (home.ts) is able to read query string param via activate() method, but activate() params in App view-model (app.ts) are empty.
I have router configured to use pushstate, but the behavior is the same with disabled pushshate.
Is there a way how to read query string params inside app root view-model?

It's not going to get parameters passed to it b/c it isn't activated by the router. That being said, you can simply use VanillaJS to do this. There is a new API, that is polyfillable that you could use: https://davidwalsh.name/query-string-javascript
Remember, Aurelia is just JavaScript. This is something I say in every intro to Aurelia talk I give around the world, and it's important to take this to heart.

Related

Vue Router Parameter Object Type Change On Browser Back Button

I am passing a parameter forum, which is an Object to a route in Vue.js (using vue-router)
{
path: '/forum/:forum',
name: 'ForumView',
component: ForumView,
props: true
}
When i leave that specific forum route by using another link( to something like forum/post) on the page and after that try to go back to the form page using the back button in the browser I get the following error in the console
Invalid prop: type check failed for prop "forum". Expected Object, got String with value "[object Object]".
So the router passes the javscript object when using the router link. But when using the back button of the browser (accessing the history), the object is converted into a string.
Curious if there is a fix for that or if I can't / shouldn't pass objects as parameters
"But when using the back button of the browser (accessing the history), the object is converted into a string"
...this is not entirely correct. The object is converted to a string much sooner - at the time you are navigating to your forum route to be precise. When you use back button, there is no object at all. There is only URL with dynamic :forum segment replaced by the text "[object Object]" (just check browser's URL bar)
This is the problem when passing objects through the router. Because even if you use Router in history mode and the browser's history API is used behind the scenes, router doesn't store any parameters when calling history.pushState. It pushes only URL. So if you really need something to pass to a target component, that information should by encoded in the URL (route definition) in the form of dynamic segment or as query string...
Another benefit of such solution is that your app doesn't stop working when user copy/paste the URLs :)
Or you can use something like Vuex to create shared state and only pass around some simple identifier which is much easier to put in the url/route...

Best practice to dynamically choose view model based on dynamic variable using the same Aurelia route?

I know that Aurelia route modules can be specified dynamically using navigationStrategy but that does not work for my purposes because the toggle value resides in the RouterConfiguration that only runs once. I need a way to route to different views where the toggle value can change multiple times during one session. Additionally, not all routes have multiple views.
What is the best practice for routing to different views on the same route based on a dynamic value?
I've come up with a few different strategies but I'm not sure which one is the most acceptable way to do this.
Using viewPorts where the route will have a static moduleId to a view that injects the name into an instance of <router-view name="view1_index"></router-view> using a global string, e.g. 'view1' may be passed down as 'view2', etc.
Using 2 or more instances of <compose> where the route will again have a static moduleId to a view that will use a global variable to toggle an if.bind in the <compose> instances
Using canActivate in the route module and redirecting to the secondary viewport if conditions are met
Using a pipeline step in the router config to evaluate whether it should direct to a different module (if this is possible)
Which of these strategies, if any, is most accepted? If all of these are odd ways of routing to different views per route, what should be done?
What is the best practice for routing to different views on the same
route based on a dynamic value?
Compose is your best bet. It is common to pass a parameter to the route, capture the parameter in the activate(params) callback, set a variable on your view model using the params, and then use that variable to set the view-model attribute of <compose>.
Using a pipeline step in the router config to evaluate whether it
should direct to a different module (if this is possible)
This is very possible. A common use case is authentication, where you use the AuthorizeStep to check whether a user is authorized and redirect him away if he is not. See http://foreverframe.net/exploring-aurelia-pipelines/. This can be activated in the PreactivateStep as well.
Using viewPorts where the route will have a static moduleId to a view
that injects the name into an instance of using a global string, e.g. 'view1'
may be passed down as 'view2', etc.
I recommend against using viewports for anything other than associating routes to views on a certain section of the screen.
Edit:
A third option you might be interested in is returning a Redirect from your canActivate() function.
import { Redirect } from 'aurelia-router';
canActivate(params) {
let thing = this.thing = await this.service.getById(params.id);
if (!thing) {
return new Redirect('somewhere');
}
return true;
}

What is `_router` in express?

I'm new to express and often see the code like this:
app.once('mount',function onmount(parent){
parent._router.stack.pop();
});
Not sure what is _router and why they pop it? Any reasons behind it?
The mount event occurs when a sub-app is registered with a parent app. The parent argument is the parent app object.
parent._router is the router associated with that parent app object.
parent._router.stack is the array of routes registered with that route.
parent._router.stack.pop() is removing the last registered route from that router.
There isn't enough context here for us to know why that last route is being removed. It's possible they are trying to remove the 404 error route (just a guess).
FYI, this direct manipulation of private instance variables is not documented behavior.

Can we have state base routing in Aurelia

Can we have a state base routing in Aurelia JS like angular ui router
Currently I have routes like this
root/<userid>/<feature>.
Now I am just trying to implement the same in following way
root/<userid> and feature should be pass as a parameter.The problem is that
Once the component is loaded in memory it is not getting updated on params change.
TIA
You need to configure the lifecycle to be invoked any time the URL parameters change.
See "Reusing an Existing VM" at the link below.
http://aurelia.io/hub#/doc/article/aurelia/framework/latest/cheat-sheet/7
"Since the VM's navigation life-cycle is called only once you may have problems recognizing that the user switched the route from Product A to Product B (see below). To work around this issue implement the method determineActivationStrategy in your VM and return hints for the router about what you'd like to happen."
In your view model write the following method:
determineActivationStrategy {
return activationStrategy.invokeLifecycle;
}

Mutations using Relay Environment

I'm using Relay with React Native and have a problem during login & logout.
After login or logout, Relay keeps the store from the previous user. To solve this I use Relay.Renderer and Relay.Environment. As in, in each Renderer I put singleton object of Environment.
The problem is that I previously did a mutation on object of Relay.Store, as in
Relay.Store.commitUpdate(new CreateProfile(), callback).
Now it doesn't work. I guess this is because Relay.Store doesn't know anything about server endpoints. But Relay.Environment does.
And now I'm using something like this this.props.relay.commitUpdate(new CreateProfile(), callback). It works pretty well when the parent component is wrapped as Relay.Container, so it has relay object in props.
But what should I do in components which are not Relay.Containers and don't have Relay object in props?
Relay.Store is a globally accessible singleton instance of Relay.Environment and Relay.Store.commitUpdate() updates data in that global environment. But since you're using your own instance of Relay.Environment, to update it you need to use this.props.relay.commitUpdate(), as you noted. This updates the environment the container was rendered with.
If need to make mutations from child components of containers, that are not wrapped in a Relay.Container, there are two ways to do that. You could simply pass the relay prop to them, so in the render function of your container you would have:
<Child relay={this.props.relay} />
However, since those plain components are not in a Relay container, they don't currently need to know anything about Relay. If you want to keep them that way, you could write the method that does the update in your container component like this:
onCreateProfile = () => {
this.props.relay.commitUpdate(new CreateProfile());
};
and only pass a callback to your child component in render:
<Child onCreateProfile={this.onCreateProfile} />
If you need to make a mutation from a component that does not have a Relay.Container above it in the component hierarchy at all, you could create the Relay.Environment in a shared root component higher up and pass it down using props (or pass a callback using the strategy shown above).