Vue Router, construct a route - javascript

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 }})
}
}

Related

How to Reload to same Page while sending a parameter?

I need to reload the page whenever different buttons are pressed, while sending a String to the same page so that on created() it takes that String and sends an HTTP Get into my Database.
Currently I have the following:
export default {
data(){
return{
events: [],
formData:{
sportType: 'Ténis'
}
}
},
created(){
//Do something here to get the value sent from the reloading
axios.get(`http://localhost:8001/evento`, {headers: {sportType: this.formData.sportType}})
.then((response)=>{
this.events = response.events
},(error) =>{
console.log(error);
});
},
pickSport(item){
}
The function pickSport() is called whenever the buttons are pressed and each sends a value to this function that is a String. The idea now is to be able to reload the page when this function is called, while sending this item to the reloaded page, so I can update the value of sportType. I tried:
pickDesporto(item){
this.$router.push({
path: '/betting',
params: item
});
}
But with no success, since it keeps giving me a NavigationDuplicated error. How can I solve this?
They are many way to do this. You may want to use Watch: {} to keep track of certain parameter. It will be trigger once the parameter change and you can re-render the same page with new parameter.
There is also a way called key-binding but it need to bind the key to the component. I personally suggest this one because it will be easy to understand.

Submitting a post that belongs to current user - how to define the relation on client-side in Vue?

Im building an app using Node.js, specifically Express server-side and Vue client-side, with SQLite + Sequelize for managing the database.
Part of the functionality is that a user can make a post. This is currently possible but I needed to implement a relation so a post can be associated with the author.
I did this server-side in sequelize and all seems to be well on that end as the table columns all look correct with foreign key and references etc.
So now I need to somehow presumably set the current UserId for the post before it gets submitted. Here is the script element for the Vue component which is to be used as the interface to make posts.
<script>
import PostService from '#/services/PostService'
export default {
data () {
return {
post: {
title: null,
body: null
}
}
},
methods: {
async submit () {
try {
await PostService.post(this.post)
} catch (err) {
console.log(err.response.data.error)
}
}
}
}
</script>
I'm using Vuex to manage the state, where the 'user' object response from the database upon login is stored as simply user in my store.
So I was guessing all I had to do was the following to access the user:
post: {
title: null,
body: null
UserId: this.$store.state.user.id
}
The problem is any time I insert this line, the component stops working in some way. Doing as above stops the component displaying at all. So I tried setting it to null as default, then instead doing this inside my submit method:
this.post.UserId = this.$store.state.user.id
If I do this, the component displays but the submit method no longer runs; I also tried both these methods without the .id just incase, no difference.
I then tried removed from the data model completely, after reading that sequelize may be generating a setUser method for my post model. So tried the following in submit method before sending the post:
this.post.setUser(this.$store.state.user)
... again the method stops running. Again, even if adding .id.
Without trying to set any ID at all, everything works until the server returns an error that 'Post.UserID cannot be null', and everything worked perfectly before I implemented the relation.
I haven't really found anything useful beyond what I already tried from doing a few searches. Can anyone help out? I'm totally new to Node.
I have accessed other parts of the state such as isUserLoggedIn, and it all works fine and no crashes occur in those cases.
I just managed to get it working correctly, I'm not sure why it suddenly started working as I am sure I had tried this before, but I solved it by doing the following within my component's script element:
<script>
import PostService from '#/services/PostService'
export default {
data () {
return {
post: {
title: null,
body: null,
UserId: null
}
}
},
methods: {
async submit () {
this.post.UserId = this.$store.state.user.id
try {
await PostService.post(this.post)
} catch (err) {
console.log(err.response.data.error)
}
}
}
}
</script>
Posts now create and display as normal. If anyone knows anything I'm technicially not doing right in any of my approach though please let me know, as I am still learning Node, Express and Vue.

Angular: Reference query params in redirectTo

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.

Ember not calling setupController of router

So, I have two paths in my route. I created the two routes as the doc recommends.
My router is the following:
// router.js
Router.map(function() {
this.route('photos');
this.route('photo', { path: '/photo/:photo_id' });
});
If I visit firstly the route /photo/ID and then go to /photos, it will only show one object on the latter. (wrong)
If I visit /photos first it shows all the objects and I can go to /photo/ID later on and it will be fine. (right)
I want to make it work both ways. How to do this? You can see my code for each route down below:
// photo.js
export default Ember.Route.extend({
model(params) {
return this.get('store').findRecord('photo', params.photo_id);
}
});
// photos.js
export default Ember.Route.extend({
setupController(controller, model) {
let photos = this.get('store').findAll('photo');
console.log('add all...');
// convert to an array so I can modify it later
photos.then(()=> {
controller.set('photos', photos.toArray());
});
},
});
I can always call the findAll() function regardless where the user goes, but I don't think this is smart.
The way I am dealing with the page transitions:
To go to photos I use:
{{#link-to 'photos'}}All{{/link-to}}
To go to /photo/ID I inject the service '-routing' and I use in one event click like this:
routing: Ember.inject.service('-routing'),
actions() {
selectRow(row) {
this.get("routing").transitionTo('photo', [row.get('id')]);
}
}
findAll will get it from a store and return immediately and later on it will request the server and update the store. but in your case, as you are not using route model hook, so this live array will not be updated so it will not reflect it in the template.
If I visit firstly the route /photo/ID and then go to /photos, it will
only show one object on the latter.
In the above case, store will contain only one reocrd, so when you ask for store data using findAll it will return the existing single record.
Another option is,
avoiding this photos.toArray() - It will break live-array update, I am not sure why do you need it here. since photos is DS.RecordArray.
Note: It's important to note that DS.RecordArray is not a JavaScript
array, it's an object that implements Ember.Enumerable. This is
important because, for example, if you want to retrieve records by
index, the [] notation will not work--you'll have to use
objectAt(index) instead.

Ember How to retain query parameters while manually refreshing the page?

I am using ember 2.7.0.while manually refreshing the page ember clears the ember-data as well us query parameters, so i am unable to load the page in setupController while refreshing. Is there any possible way to retain both model & query parameters, at least retaining query parameter would be fine to reload my page.
route.js
model(params) {
return this.store.peekRecord("book",params.book_id);
},
setupController(controller,model,params){
if(!model){
//fetch book record again if the model is null
}
controller.set('isdn',params.queryParams.isdn);
controller.set('book',model);
}
Any help should be appreciable.
Edited setupController as per Adam Cooper comment :
setupController(controller,model,params){
var isdn = params.queryParams.msisdn;
controller.set('isdn',isdn);
if(!model){
this.store.findRecord('book', isdn).then((customer) => {
this.set('book',customer);
},(resp,status) => {
this.set('errorMessage', `Book with this ${isdn} does not exist.`);
this.set('book', []);
});
}else{
controller.set('device',model);
}
}
Page gets rendered before "findRecord" returning promise.Is there any way to stop page rendering till find record resolves the promise?
You are setting in route properties instead of controller..
setupController(controller, model, params){
var isdn = params.queryParams.msisdn;
controller.set('isdn', isdn);
if(!model){
this.store.findRecord('book', isdn).then((customer) => {
controller.set('book', customer);
}, (resp, status) => {
controller.set('errorMessage', `Book with this ${isdn} does not exist.`);
controller.set('book', []);
});
}else{
controller.set('device', model);
}
}
Only the controller properties will decorate template.
You can even try the below, why don't you give opportunity to model hook to resolve since that will wait for the Promises to resolve.
model(params) {
var result = this.store.peekRecord("book",params.book_id);
if(result !== null){
result= this.store.findRecord('book', params.book_id)
}
return result;
}
setupController(controller,model){
controller.set('book',model);
}
You will need to generate an actual controller for your route and then define a queryParams property in the controller. It looks like the query param you're trying to hold onto is isdn so your controller should look something like:
export default Ember.Controller.extend({
queryParams: ['isdn']
});
"manually refreshing the page ember clears the ember-data as well us query parameters"
Once you completely refresh the browser, a new ember app instance is created and hence ember-data cannot be retained. Ember-data is just for the app on the UI, once ember is exited it will not be retained.
"as well us query parameters"
your query params are part of your url and it should not get cleared. Make sure the below two are present
Include queryParams in ur controller i.e.
queryParams: ['param1', 'param2']
And in your route make sure you have done
queryParams : {
param1: {
refreshModel: true
},
param2: {
refreshModel: true
}
}
"Page gets rendered before "findRecord" returning promise"
You are not doing something right, is the adapter, model, serializer etc defined correctly(if required) in order to use findRecord? Just to debug return a plain object and make sure ur setupController is called before rendering. i.e.
model() {
return {dummy: 'dummy'};
}

Categories