I have a bunch of boxes in my app that route to a page where you can see all of the items that are in the box. As it stands my dynamic route in the index.js file looks like this:
{
path: '/box/:idBox',
name: 'ItemListByBox',
component: ItemListByBox,
},
All of my boxIDs (idBox) are all strings such as Box 1, Box 2, etc.
Everything works totally fine and routes you to the right page, but the page address at the top winds up looking like http://localhost:8080/box/Box%202
Is there a way to have the address look like Box-2?
Cheers!
You can use a computed to transform the user input (mentioned in comments) so that spaces are replaced with dashes. You don't need to change your route configuration.
Imagine you have a data variable that gets modified by user input called boxID and may contain spaces. Pass the computed as the route param like this:
<router-link :to="{ name: 'ItemListByBox', params: { idBox: boxFormatted }}">
CREATE BOX
</router-link>
export default {
data: () => ({
boxID: 'Box 1'
}),
computed: {
boxFormatted() {
return this.boxID.replace(/\s+/g, '-')
}
}
}
Related
I was trying to change a route parameter with Vue router.
My route is the following :
/document/:id?/:lang?/edit
Id and lang (language) are the parameters.
I want to change the parameter lang using JavaScript without altering the rest of the route. I tried applying the following solution (found here). The solution was made for Vue 2 and not 3. So I tried to make an updated version.
This is my code :
let params = this.$route.params
params.lang = 'en'
this.$router.push({params:params})
When I execute my code, there is no change in the route parameters nor an error logged in the console. Does anyone know a solution to this problem in Vue 3.
Thanks in advance.
It's a bad way to change your route parameter like this.
Instead, replace your route with the same name, same id, but different lang:
this.$router.replace({
name: this.$route.name,
params: {
id: this.$route.params.id,
lang: 'en' // or whatever you want
}
})
Don't forget to watch for the route changes if needed:
watch: {
$route(to, from) {
// if anything needs to be done when the route changes
}
}
I would like to provide a path that redirects to a given page based on query parameters. For example:
/redirect?page=hero&id=1
should redirect to:
/hero/1
Is there any way to do this in the route config? Something like:
{ path: 'redirect?page&id', redirectTo: ':page/:id' }
I can get the redirectTo to respect path parameters but not query parameters. Any ideas?
You can try to use redirectTo: '/:page/:id' and provide extracted from your URL page and id values using custom UrlMatcher():
...
const appRoutes: Routes = [
{
path: 'hero/:id',
component: TestComponent
},
{
matcher: redirectMatcher,
redirectTo: '/:page/:id'
}
];
...
/**
* custom url matcher for router config
*/
export function redirectMatcher(url: UrlSegment[]) {
if (url[0] && url[0].path.includes('redirect')) {
const path = url[0].path;
// sanity check
if (path.includes('page') && path.includes('id')) {
return {
consumed: url,
posParams: {
page: new UrlSegment(path.match(/page=([^&]*)/)[1], {}),
id: new UrlSegment(path.match(/id=([^&]*)/)[1], {})
}
}
}
}
return null;
}
STACKBLITZ: https://stackblitz.com/edit/angular-t3tsak?file=app%2Ftest.component.ts
There is another issue when using redirectTo: ..., active link is not updated, actually isActive flag is not set to true, it is seen on my stackblitz when acrive redirection links are not colored in red
No, there is no way of doing it by a configuration. YOu see, Angular's router does not explicitly define query parameters - any url can have an arbitrary number of query params, and the paths '/page/id' and '/page/id?key=value' are treated as the same in Angular and do map to the same component. There are other, more cumbersome workarounds. One is to create a dummy component and redirect based on ActivatedRoute.queryParams Observable from the component's ngOnInit method. You can easily see why this is a bad idea.
Another way is to create a resolver, this way you maybe can dismiss the component declaration and just redirect from the resolver, again, based on the ActivatedRoute.queryParams Observable, which seems cleaner.
But I do not really get why one would need such a route in a front end application, if you want someone to visit '/page/id', then just navigate them to the page, without any intermediary tricks.
I am making a data table with a search field in my application and I would like to implement the following behaviour:
When a user types a research, the URL gets updated. If a user clicks
on a link and hit "back" from his navigator, he gets back to his last
search.
For the moment, I am doing the following thing:
export default {
data () {
return {
search: ''
}
},
watch: {
search (value) {
this.$router.replace('/products?search=' + value)
}
}
}
As you can see, I am using the replace function of the router. That allows me to make it work. And it's better than the push method because if a user hits "back" during a search, he will go to his last page and not last research, and that's exactly what I want.
However, I don't like how I hard write the URL.
I would love to make something like that:
watch: {
search (value) {
let route = new Route(this.$route)
route.query.search = value
this.$router.replace(route.getPath())
}
}
Because, let's imagine that the user has other parameters like pagination or something like that, they would be erased. I would like a method that takes my actual url and add/replace a query string parameter.
Is this something I can achieve with Vue Router or do I need to do something else: Other package / Vanilla JS?
Why not just use the object location parameter $router.replace? Eg:
watch: {
search (value) {
this.$router.replace({ path: 'products', query: { search: value }})
}
}
In my Angular 2 app I have a tab area where users can select from a group of independent, but contextually related components. When they click on one of these links, the relevant component loads according to what's defined in the routerLink, like this:
<a class="page-content-header-item" routerLink="/page1" routerLinkActive="selected">Page 1</a>
This was working well. However, since then we've built the app to save various user-selected filter selections as params in the URL. This way when they re-load the component, they'll have their most recent selections still visible and applied to the data. So the URL might look like this after the user had made some filter selections:
http://somesite.com/page1;language_filter=true;language_selection=English
The component code for this looks like this:
public changePage(page, value, type, body)
{
this.onUserSelection(value, type, body, page);
this.route.params.subscribe(
(params: any) => {
this.page = params['page'];
this.language_filter = params['language_filter'];
this.language_selection = params['language_selection'];
}
);
this.router.navigate(
['/page1', {
page: page,
language_filter: this.language_filter,
language_selection: this.language_selection,
}]);
}
This works well for the main navigation methods, that are accomplished via a routing file, where each one looks like this:
{ path: 'page1', component: Page1Component, canActivate: [AuthGuard], data: {contentId: 'page1'} }
However, for this tab area I mentioned, it's loading components according to a hard-coded routerLink param. So I realize now that when a user navigates BACK to a component that way, as opposed to via one of the other ways we make available, it actually overrides the URL params - because it's literally loading "page1" -- because of this <a class="page-content-header-item" routerLink="/page1" routerLinkActive="selected">Page 1</a>
... and thus the URL params that had been added previously are wiped out.
So, my question is, is there a way I can edit this code:
<a class="page-content-header-item" routerLink="/page1" routerLinkActive="selected">Page 1</a>
... so it allows for some dynamic variables? Or do I have to find a new way to handle the navigation in this tab area?
Here is the solution I came to using queryParams.
First, you can pass parameters in your routerLink directive using the queryParams directive:
<a routerLink="/page1" [queryParams]="fooParams">Page 1</a>
<a routerLink="/page2">Page 2</a>
<router-outlet></router-outlet>
where fooParams is a plain object:
export class MainComponent {
fooParams = {
language_filter: true,
language_selection: 'english'
}
}
Angular will output this url like
href="localhost:4200/page1?language_filter=true&language_selection=english"
What you have to do next, is set up a way to intercept the ActivatedRoute, so you can extract the values of the params from the ActivatedRouteSnapshot. You can do this either using a resolver or directly in your component. In my example I used a resolver:
#Injectable()
export class RoutingResolve implements Resolve<any> {
resolve(routeSnapshot: ActivatedRouteSnapshot) {
const { language_filter, language_selection } = routeSnapshot.queryParams;
return { language_filter, language_selection };
}
}
And then pass that resolver in the route definition:
const ROUTES: Routes = [
{
path: 'page1',
component: PageOneComponent,
// In this route we define a resolver so we can intercept the
// activatedRoute snapshot before the component is loaded
resolve: {
routing: RoutingResolve
}
},
{
path: 'page2',
component: PageTwoComponent
}
];
Then, inside PageOneComponent you can subscribe to the resolved data and do whatever you want with it, like for example, setting the value of a form with it and updating the queryParams on form change.
For the full example, check out this plunker
Make sure to open the preview in a separate window, so you can see the route changes in your browser.
If you navigate to Page 1, change the values in the form, then navigate to Page 2, and then press back in your browser, you can see the values loaded in the form correspond to the params in the url.
I have a nested route setup as I would like to have my templates nested as well. The route setup looks like this:
...
this.route('posts', function() {
this.route('post', {path: ':post_id'}, function() {
this.route('comments', {path: 'comments'}, function() {
this.route('comment', {path: ':comment_id'});
});
});
});
...
Potentially, my URL could look something like this:
/posts/:post_id/comments/:comment_id
If I navigate via {{link-to}} then I have no problem, however, if I go directly to a specific URL via the browser, that's when things going wrong. Imagine that my comments page lists the comments associated with the post (post_id) I'm looking at. The problem is going there directly, like typing in /posts/123/comments/56 doesn't load the list of comments, only the comment itself. Here is how my routes are setup:
// routes/posts.js
...
model() {
return this.get('store').findAll('post');
}
...
// routes/posts/post.js
...
model(params) {
return this.get('store').findRecord('post', params.post_id);
}
...
// routes/posts/post/comments.js
...
model(params) {
let post=this.modelFor('posts/post');
return post.get('comments');
}
...
// routes/posts/post/comments/comment.js
...
model(params) {
return this.store.findRecord('comment', params.comment_id);
}
...
If I type in /posts/123/comments/56, no list is displayed from the comments.js template.
If I type in /posts/123/comments, only the first comment is displayed from the comments.js.
I'm not sure what I'm doing wrong though I feel like it has something to do with modelFor. How do I properly populate/hydrate the post's comments list when I go directly to a comment in that post URL rather than navigating there via link-to?
UPDATE 1:
Here are the models:
// post.js
export default DS.Model.extend({
title: DS.attr("string"),
articles: DS.hasMany('comment', {async: true})
});
// comment.js
export default DS.Model.extend({
title: DS.attr("string")
});
I've connected it to mirage.
Here is a sample of the comments fixture:
[
{
id: 1,
title: "Apple"
},
...
and the posts fixture:
[
{
id: 1,
title: "A post with fruits for comments",
commentIds: [1, 2]
},
...
UPDATE 2:
I've added my template code for templates/posts/post/comments.js
Comments.js!
{{outlet}}
<ol>
{{#each model as |comment|}}
<li>{{#link-to "posts.post.comments.comment" comment}} {{comment.title}}{{/link-to}}</li>
{{/each}}
</ol>
I've also updated the comment.js route above, reducing its complexity.
Based on your router definitions - the URL /post/123/comments/56 does not exists. Your router defines URL with the following structure /posts/56/comment/123 (notice singular comment not comments) - your route name is comments however you told ember that the path it should resolve is comment - singular. So, try accessing /post/123/comment/56 instead of comments.
Also, no reason to call this.modelFor('posts/post') from nested comments route - all you should need is modeFor('post').
And the last thing, inside the comment route you should only need to look for a give comment no need to resolve the parent model - you already have a unique comment_id - store.findRecord('comment', comment_id) should work.