Dynamic parameter in href option in Aurelia Routing config.map - aurelia

This seems like a really simple issue, but it's driving me crazy...
Does anyone know how I can specify a dynamic :id parameter in the href routing configuration option?
The following unfortunately doesn't work:
config.map([
// ... default parameterless routing here
{
route:[':id/request'],
moduleId:'processes/bdd/request/request',
name:'Request', title:'Request', href:`#/bdd/request/${id}/request`, settings:{type:'Request', icon:''}, nav:true,
},
{
route:[':id/requestAuth'],
moduleId:'processes/bdd/request/requestauthorization',
name:'RequestAuthorization', title:'Request Authorization', href:`#/bdd/request/${id}/requestAuth`, settings:{type:'Request', icon:''}, nav:true,
},
// ... some additional mappings here
]);

The href property is static. If you want to generate a route for a link using this route, you could use the route-href custom attribute like this:
route-href="route: request; params.bind: { id: someProp }"
Note that I changed the route name to be camelCase (all lowercase since it is one word here) to match the route naming convention.

I had a similar use case and I was able to get this to work by adding a pipeline step to the router that alters the config on the fly.
My use case may be a little different in that I only want the item to appear in the nav bar when the route is active -- say I have routes /abc, /def/:id, and /ghi -- when the active route is ABC or GHI, only those items will appear in the nav bar, but when the active route is DEF, it should appear in the nav bar, and clicking it should lead to the same DEF ID you're currently looking at. So my route configuration includes a setting that indicates the route should only appear in the nav bar when it's the active route.
Here are the interesting parts of my actual configureRouter function:
configureRouter(config, router) {
config.addPreActivateStep(Preactivate); // explained below
config.map([
// some routes
{ route: 'patients/:patientId', name: 'patient-master',
moduleId: 'patients-and-cases/patient-master/patient-master',
title: 'Patient', nav: true, settings: { renderOnlyWhenActive: true },
href: '#' // it doesn't matter what this is
},
// more routes
]);
}
And here is the Preactivate class that sets the href on preactivate:
class Preactivate {
run(routingContext, next) {
routingContext.config.href = routingContext.fragment;
return next();
}
}
If, unlike me, you want this to display in a nav bar all the time, this will still work -- the href will simply remain set to the last thing it was set to when the route was active. In that case you'd probably want to initialize it to some default value that makes sense.

Related

Vue: Setting Data by matching route query

I'm attempting to set data fields provided by an array based on the Vue Router query. For example, when someone lands on my website using example.com/?location=texas, I want to set the location data by an array.
An example the array:
locations {
{
slug: "texas",
tagline: "Welcome to Texas",
}, {
slug: "california",
tagline: "Welcome to California",
}
}
I know this should be done using a computed property, however I am unable to get anything functioning. I've tried simple tests like if (this.slug.location === "texas"), and I cannot get the location data to populate. I would also like to provide default data in case there are no route matches.
Any help is extremely appreciated!
Edit:
I can accomplish this in a very manual way. Right now, I'm setting the query in data by the following:
slug: this.$route.query.location
I can display specific text by doing something like:
h3(v-if="slug === 'texas'") This will show for texas
h3(v-else-if="slug === 'california'") This will show for California
h3(v-else) This is default
The issue with this approach is there are various elements I need to customize depending on the slug. Is there any way I can create an array, and move whichever array matches a key in an array to the data??
You should be able to access a query param using the following (link to Vue Router documentation):
this.$route.query.location
So based on what you listed I would do something like...
export default {
computed: {
displayBasedOnLocationQueryParam() {
switch(this.$route.query.location) {
case 'texas':
return 'Welcome to Texas'
default:
return 'hello there, generic person'
}
}
}
}
Note that I'm not using your array explicitly there. The switch statement can be the sole source of that logic, if need be.

Vuex state structure and fetching when using same type of data with different values on different routes

I'm creating a portfolio with vue, vuex and vue-router that will show images.
On the homepage i will show images with 'show_home: true'.
Then there is "tag" pages (portfolio/:tagSlug) that will show images based on a slug, eg. 'weddings' with infinite scroll (auto populate pagination).
An image object will look something like:
{
id: 1,
title: 'Lorem..',
path: '..',
show_home: true
tags: [ {id: 1, slug: 'weddings'} ]
},
{
id: 2,
title: 'Lorem2..',
path: '..',
show_home: false
tags: [ {id: 1, slug: 'weddings'}, {id: 2, slug: 'water'} ]
}
Endpoints examples:
Homepage: GET: ../images/homepage?p=1
Tag page: GET: ../images/:slug?p=1
I can't figure out how I should structure this in vuex and handle the fetching..
Should i just create i single 'images: []' state and populate it with ALL the images after fetching them from the api in each route, then filter them with getters? How can i get the pagination in there in that case? Or do you have a better solution?
Thanks in advance
My preferred approach is to "flatten" the relationships and pull them as needed. This also allows you to only pull what you need from the server or related modules.
tags vuex module:
all: {
1: { <-- indexed by tag id
name: "weddings"
images: [1,2,3,4] <-- image ids
}
}
active: false <-- When there is an active tag, this becomes the id of the tag.
The vuex images module would follow this same pattern:
all: {
1: { <-- indexed by image id
title: 'Lorem..',
path: '..',
show_home: true
tags: [1,2,3,4] <-- tag ids
}
}
active: false <-- When there is an active image, this becomes the id of the image.
Then use a getter to hydrate the images or tags from the respective vuex module.
There is a great write up on this approach on this blog: https://medium.com/js-dojo/structuring-vuex-modules-for-relationships-speed-and-durability-de25f7403643
With this approach you will have fewer and smaller api calls, pagination is manageable and you don't need to worry about stale data in your relationships.
EDITED -- API info:
Two approaches come to mind.
1) always load the images with the tag.
Tag index request would not load any images, just the basic info for each tag.
When the user clicks on a tag, this inits an API call for the tag details:
Tag show request (tags/1 or tags/weddings) would return the tag with loaded relationships:
public function show($id)
{
$tag = Tag::where('id', $id)->with('images')->firstOrFail();
return new TagResource($tag); <-- will have all related images loaded.
}
2) set up a nested REST endpoint if needed
You can use the the resource controllers to shortcut the boilerplate like this:
api.php
Route::apiResource('tags.images', 'tags\TagImageController');
This route will watch your api calls and determine if it is index/store/show/delete. From your front end you can make a call like https://backendsite.com/tags/1/images (If wedding tag has an id of 1)
Then in the TagImageController you would have something like this:
public function index(Request $request, $id)
{
$tag = MemTag::find($id);
$images = $tag->images()->get();
$images->load(Image::allowedIncludes); <- or you can manually list relationships you want to load
return ImageResource::collection($images);
}

Adding or not adding route in Aurelia

I am trying to add a route deppending on a boolean, so if the condtion is meet the route will show and be in the navbar and if not it will not be added.
I have trided to change nav: option depending on a condtion, but I have missed something...
{
route: 'aRoute',
moduleId: 'modules/something/mySite,
nav: cond ? true : false,
title: 'mySite',
isStatic: false
}
Probably I am stupid but I need help on this.. :/
I'm not 100% sure, but routes are probably built one time and reused, at least those that are part of navigation model (nav === true). That would mean that you cannot use dynamic expression for nav as it would be processed only once.
If you only need this for the purpose of displaying the route in navbar, then you can create a workaround using settings property to mark the route somehow and do condition check when creating navbar.
{
route: 'aRoute',
moduleId: 'modules/something/mySite,
nav: true,
title: 'mySite',
settings: { occasionallyVisible: true } // add your own properties here...
}
I used settings.occasionallyVisible property, but you can add whatever you like.
<li repeat.for="route of router.navigation"
if.bind="!route.settings || !route.settings.occasionallyVisible || cond">
<a href.bind="route.href">${route.title}</a>
</li>
Navigation route will be displayed if it doesn't have settings property OR settings.occasionallyVisible is falsy OR cond is true. Of course, cond would be that condition you need.
Not as elegant, but it should get the work done until you get the better answer :)
The following post should help you do what you want:
http://odetocode.com/blogs/scott/archive/2016/05/31/dynamically-add-routes-in-aurelia.aspx
It shows how to add a route dynamically by doing this:
this.router.addRoute(
{ route: "secret", name: 'secret', moduleId: "app/secret",
title:"Secret", nav:true
}
);
then refreshing the router with this method call (which you are apparently missing):
this.router.refreshNavigation();

Pass values to route

I have a list of items. When the user clicks on an item, the user will be taken to item details page.
I want to pass an object containing item details(like item's image URL) to the route. However, I don't want to expose it in the routes url.
If there were a way to do something like <a route-href="route: details; settings.bind({url: item.url})">${item.name}</a> that would be gold.
I have seen properties can be passed to a route if defined in the route configuration. However, I don't know how to change that from the template. Another way could be is to define a singleton and store the values there and inject the object to the destination route.
Is there a way to pass values to routes from view (like angular ui-routers param object)?
Okay so I figured out a way to achieve something closer to what I wanted:
Objective: Pass data to route without exposing them in the location bar.
Let's say, we have a list of users and we want to pass the username to the user's profile page without defining it as a query parameter.
In the view-model, first inject Router and then add data to the destination router:
goToUser(username) {
let userprofile = this.router.routes.find(x => x.name === 'userprofile');
userprofile.name = username;
this.router.navigateToRoute('userprofile');
}
Now when the route changes to userprofile, you can access the route settings as the second parameter of activate method:
activate(params, routeData) {
console.log(routeData.name); //user name
}
For those #Sayem's answer didn't worked, you can put any additional data (even objects) into setting property like this:
let editEmployeeRoute = this.router.routes.find(x => x.name === 'employees/edit');
editEmployeeRoute.settings.editObject = employeeToEdit;
this.router.navigateToRoute('employees/edit', {id: employeeToEdit.id});
So editObject will be delivered on the other side:
activate(params, routeConfig, navigationInstruction) {
console.log(params, routeConfig, navigationInstruction);
this.editId = params.id;
this.editObject = routeConfig.settings.editObject;
}
hopes this helps others encountering same problem as me. TG.

Dynamically Adding / Removing Route in Durandal Router when application is loaded

I need help in dynamically adding/removing route in Durandal Router. What I want is after user is logged in then I would be able to add or remove specific route depending upon logged in user's type.
I tried to add/remove route from visibleRoutes/allRoutes array ... but get binding exception from knockout library...
I was hoping it would be common scenario... but still couldn't find any solution ... please help me in fixing this issue.
Thanks.
Wasim
POST COMMENTS:
I tried this function to dynamically hide/show route... and similary tried to add/remove route from allRoutes[] ... but then get exception on knockout bidning
showHideRoute: function (url,show) {
var routeFounded = false;
var theRoute = null;
$(allRoutes()).each(function (route) {
if (url === this.url) {
routeFounded = true;
var rt = this;
theRoute = rt;
return false;
}
});
if (routeFounded)
{
if (show)
{
visibleRoutes.push(theRoute);
}
else
{
visibleRoutes.remove(theRoute);
}
}
}
In Durandal 2.0.
You can enumerate the routes to find the one you wish to show/hide.
Then change the value of: nav property
Then run buildNavigationModel();
here is an example:
// see if we need to show/hide 'flickr' in the routes
for (var index in router.routes) {
var route = router.routes[index];
if (route.route == 'flickr') {
if (vm.UserDetail().ShowFlickr) { // got from ajax call
// show the route
route.nav = true; // or 1 or 2 or 3 or 4; to have it at a specific order
} else if (route.nav != false) {
route.nav = false;
}
router.buildNavigationModel();
break;
}
}
Durandal 2.0 no longer has the method visibleRoutes. I found that the following works for me.
router.reset();
router.map([
{ route: 'home', moduleId: 'home/index', title: 'Welcome', nav: true },
{ route: 'flickr', moduleId: 'flickr/index', title: '', nav: true }
])
.buildNavigationModel()
.mapUnknownRoutes('home/index', 'not-found');
This removes all previous routes, if you want to maintain current routes you could try using the router.routes property to rebuild the array of routes.
I had a similar requirement. If I were you, I would take another approach. Rather than adding/removing routes when application loads - get the right routes to begin with per user type.
Two options, (I use both)
1) have a json service provide the proper routes per user type, this approach would be good if you need to 'protect/obscure' routes... i.e. I don't want the route referenced on any client resource.
2) A simpler solution see Durandal.js: change navigation options per area
You can have a settings property identify the user type.
I hope this helps.
I had a similar problem: First, router.visibleRoutes() is an observable array. In other words, when you change its value, the routes automatically update. However, the items in this array are not observable, so to make a change you need to replace the entire array and not just make a change to a single item in it.
So, all you have to do is find which item in this array you want to remove, and then create a new array without this item, and set router.visibleRoutes() to this new array.
If, for example, you find out the it is the 3rd item, then one way of doing it is:
router.visibleRoutes(router.visibleRoutes().splice(2, 1))
Note that splice() returns a new array where an item is removed. This new array is put into router.visibleRoutes.