How to initiate refresh of Vuejs page elements w/ new data? - vue.js

I have a vuejs app using vue-router with the following routes.
const routes = [
{ path: '/list', component: list, alias: '/' },
{ path: '/resources/:id?', component: resources },
{ path: '/emails', component: emails },
{ path: '/list/:id', component: editHousehold, props: true },
{ path: '/list/turn-off/:id', component: editHousehold, props: true }
]
The first time the page loads the start event calls /resources w/o an ":id" and the page loads a list of resources (see below).
start: function () {
this.$http.get('/resources')
.then((res) => {
let gdriveInfo = res.data;
this.files = gdriveInfo.files;
}
);
},
Resource1
Resource2
Rescouce3
...
When the user clicks on one of the resources in the list I want to have /resources/1 called so a different set of resource data can be loaded and displayed.
I have a file click event attached to each resource where the "id" is appended to the path. This calls the server side module which would retrieve new data which would replace the "files" data in the component which I would expect would cause vuejs to "react" and update the contents of the page.
onFileClick: function (id, mimeType, event) {
const _this = this;
this.$http.get('/resources/' + id)
.then((res) => {
let gdriveInfo = res.data;
this.files = gdriveInfo.files;
}
);
}
However, calling above does not initiate a call to the server module.
this.$http.get('/resources/' + id)
I've also tried
this.$router.push('/resources/' + id)
which did not work.
Being new to vuejs, any help in how to achieve this functionality would be appreciated.

You lack host, because this.$http.get('/resources/' + id) is u component resources, this not json...

It looks like you're not making the REST call correctly. I think you're getting routing and REST calls mixed up. What you show above is for routing not making calls to the server.
You're not calling the server here:
this.$http.get('/resources/' + id)
and doing this is just for the routing:
this.$router.push('/resources/' + id)
Look at using axios for REST calls:
https://github.com/axios/axios

Related

VueJS: $router.push not working with query parameters

In my NuxtJS(v. 2.10.2) application, I have a URL like below where pid is a post's id.
/post?pid=5e4844e34202d6075e593062
This URL works fine and loads the post as per the value passed to the pid query parameter. However, user can add new post by clicking Add Post button on the application bar that opens a dialog. Once the user clicks add, a request to back-end server is made to save the request. And once successful, user is redirected to the new post using vue router push like below
.then(data => {
if (data) {
this.$router.push({ path: `/post?pid=${data.id}` });
}
})
The problem is, user is not redirected to the new post, only the query parameter pid is updated. I suspect VueJS does not acknowledge this as a different URL and hence does nothing.
How to fix this?
Update: As an alternative tried the syntax below but getting the same behavior.
this.$router.push({ path: "post", query: { pid: data.id } });
Say you have a component post.vue which is mapped with /post URL.
Now if you redirect the user to /post?pid=13, the post.vue component won't mount again if it's already mounted ie. when you are already at /post or /post?pid=12.
[1] In this case, you can put a watch on the route to know if the route has been changed.
watch: {
'$route.path': {
handler (oldUrl, newUrl) {
let PID = this.$route.query.pid
// fetch data for this PID from the server.
// ...
}
}
}
OR
[2] If the component post.vue is mapped with some route say /post.
You can also use the lifecycle -> beforeRouteUpdate provided by vue-router
beforeRouteUpdate (to, from, next) {
let PID = to.query.pid
// fetch data for this PID from the server.
// ...
next()
}
By changing the approach component data can be updated as per the new query string value. Here is how it can be done.
Rather than trying to push to the same page again with different query string. The query string pid itself can be watched for change and on update new data can be fetched and the component data can be updated. In NuxtJS(v. 2.10.2) apps, this can be achieved with watchQuery. watchQuery is a NuxtJS property which watches changes to a query strings. And once it detects the change, all component methods(asyncData, fetch, validate..) are called. You can read more https://nuxtjs.org/api/pages-watchquery/
As for the solution, pushing to the same page with new query string remains the same.
.then(data => {
if (data) {
this.$router.push({ name: 'post', query: { pid: data.id } });
}
})
However, on the page.vue, where the data is fetched from the server. We need to add watchQuery property.
watchQuery: ["pid"],
async asyncData(context) {
let response = await context.$axios.$get(
`http://localhost:8080/find/id/${context.route.query.pid}`
);
return { postData: response };
},
data: () => ({
postData: null
})
Now, everytime the query string pid will change asyncData will be called. And that is it. An easy fix to updating component data when the query string value change.
try this solution
.then(data => {
if (data) {
this.$router.push({ name: 'post', query: { pid: data.id } });
}
})
hints:
// with query, resulting in /register?plan=private
router.push({ path: 'register', query: { plan: 'private' } })
Use watchQuery property (https://nuxtjs.org/docs/2.x/components-glossary/pages-watchquery)
export default {
watchQuery: true,
data: () => ...
}
In case anybody was looking for this:
Query parameters specified as a string do not work when passed to a path parameter:
router.push({path: 'route?query=params'})
When you want you use them as a string, just pass the whole string as an argument, like so: router.push('route?query=params')
It'll then be automagically picked by router and navigation will happen.
try this :
.then(data => {
if (data) {
this.$router.push('/post?pid=' + data.id);
}
})
hope it works!!!

How update a VUE component changing URL param in current page

In my vue SPA I have a simple router like this:
{path: '/page/:userId', name: 'user', component: user},
Which means an url like http://xxx.xxx/user/1 shows the component user having the content about user 1.
In case on the same page I have some link as
<vue-link :to="{name:'user', params={userId:3}}">
The router update only the URL but the content page (because it assumes the page is the same where I'm at)
My user content loads data using the url params in data and in watch too
data
userId: this.$route.params.userId || 1,
watch
watch: {
userId: function () {
return this.$route.params.userId || 1;
},
How to fix it without using router-view?
Thanks for any suggestion
If I correctly get your problem which is that you are not able to track your route change. You need to watch the route change, whenever your route changed on a same component it should do something.
watch: {
$route() {
this.updatePage(this.$route.params.userId)
}
},
methods: {
updatePage(param) {
// call api / do something
}
}

vuex getter does not update on another component depending on timing

My app uses
axios to fetch user information from a backend server
vuex to store users
vue-router to navigate on each user's page
In App.vue, the fetch is dispatched
export default {
name: 'app',
components: {
nav00
},
beforeCreate() {
this.$store.dispatch('fetchUsers')
}
}
In store.js, the users is an object with pk (primary key) to user information.
export default new Vuex.Store({
state: {
users: {},
},
getters: {
userCount: state => {
return Object.keys(state.users).length
}
},
mutations: {
SET_USERS(state, users) {
// users should be backend response
console.log(users.length)
users.forEach(u => state.users[u.pk] = u)
actions: {
fetchUsers({commit}) {
Backend.getUsers()
.then(response => {
commit('SET_USERS', response.data)
})
.catch(error => {
console.log("Cannot fetch users: ", error.response)
})
})
Here Backend.getUsers() is an axios call.
In another component which map to /about in the vue-router, it simply displays userCount via the getter.
Now the behavior of the app depends on timing. If I visit / first and wait 2-3 seconds, then go to /about, the userCount is displayed correctly. However, if I visit /about directly, or visit / first and quickly navigate to /about, the userCount is 0. But in the console, it still shows the correct user count (from the log in SET_USERS).
What did I miss here? Shouldn't the store getter see the update in users and render the HTML display again?
Since it's an object Vue can't detect the changes of the properties and it's even less reactive when it comes to computed properties.
Copied from https://vuex.vuejs.org/guide/mutations.html:
When adding new properties to an Object, you should either:
Use Vue.set(obj, 'newProp', 123), or
Replace that Object with a fresh one. For example, using the object spread syntax we can write it like this:
state.obj = { ...state.obj, newProp: 123 }

Params field is empty in $router.push

Consider this:
this.$root.$router.push({
path: '/dashboard',
params: { errors: 'error' },
query: { test: 'test' }
})
I use this in my component to redirect to another URL, and some error has occured. The problem is that when I want to access params field in dashboard component, it's empty. The query field works well. I'm trying to access it by this.$route.params.errors.
You can use params only with named paths (i think).
Example:
//route (in your router file should have "name")
{ path: '/errors', name: 'EXAMPLE', component: ... }
//navigating
this.$router.push({
name: 'EXAMPLE',
params: { errors: '123' }
});
Now it will have correct value in this.$route.params.
If you don't want to use named routes you can try this:
ES6
this.$root.$router.push({
path: `/dashboard/${error}`,
query: { test }
})
ES5
this.$root.$router.push({
path: '/dashboard/' + error,
query: { test: 'test' }
})
I faced the similar issue where in one of my views (component). I was trying to navigate (programmatically) from /foo/bar to /foo/bar/123, but the route param was not available later in the component. My relevant navigation code looked like below:
methods: {
save_obj() {
let vm = this;
// Make AJAX call to save vm.my_obj, and on success do:
let v = `${vm.$route.path}/${vm.my_obj.id}`;
console.log("Loading view at path: "+v);
vm.$router.push({ path: v });
},
...
}
It would print the expected log (e.g., Loading view at path: /foo/bar/112), however, the loading of data in the created() hook would not receive the value of route param. My failing created() code looked like below:
created: function() {
console.log("Loading object details.");
let vm = this;
let cid = vm.$route.params.id; // <---- This was the problem
vm.$http.get('api/'+cid)
.then(function (res) {
if (res.data.status == "OK") {
vm.my_obj = res.data.body;
} else {
vm.setStatusMessage(res.data.body);
}
})
.catch(function (error) {
console.log(error);
vm.setStatusMessage("Error: "+error);
});
}
The solution was indicated in the third note here quoted below :
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).
I had to do the following in my component:
Change the line let cid = vm.$route.params.id; in created() to let cid = vm.course.id
and, add the following to the component:
beforeRouteUpdate(to, from, next) {
if (to.params.id) {
this.my_obj.id = to.params.id;
}
// Some other code specific to my app
next();
}
I hope this helps someone stuck with the similar issue.
If you want to send a parameter with a query parameter you can use that syntax like that
this.$router.push({
path: this.localePath(`/bookings/${requestReservation?.attributes?.booking_id}`),
query: { requestReservation: requestReservation }
})
You can access it on the next page like that
this.$route.query.requestReservation
If you want send it fro nuxt-link than its syntax like that
<nuxt-link
:to="{ path: '/bookings/'+ requestReservation.attributes.booking_id,
query: { requestReservation } }">
Booking
</nuxt-link>
You can access it on the next page same like previous
this.$route.query.requestReservation

Dynamic build routes {or dynamic component import} Angular 2 [duplicate]

This question already has answers here:
Async load routes data and build route instruction for Angular 2
(4 answers)
Closed 6 years ago.
Maybe anyone know how to dynamicly build routes (or just dynamic import Components).
For example:
I have JSON that contains objects with RouteName, path, ComponentNames (string).
I want to iterate it and build dynamicly routes definitions (route config). But I don`t know, how to make dynamic Component import.
I can passs string "ComponentName" from JSON to import rule, because import want static definition (finded it on some soure from googling).
failed
let a = "MyComponentName"
import {a} from ......
(One idea that I came up with - its like create some map key-value, and keep into key - route, value - Component, and after that equals routename from JSON and my MAP and push needed component into final route config array. But its so ugly solution) Maybe another way exists?
I stuck. Many thanks for any help.....
You could leverage Async routes to do this. Based on your route configuration, you could load route from modules. In this case, you need to add the module path to get the components to associate with routes.
Here is a sample:
var routes = {
path: '/path',
name: 'some name',
module: './my.component',
component: 'MyComponentName'
}
routes.forEach((route : any) => {
this.routeConfigArray.push(
new AsyncRoute({
path : route.path,
loader : () => System.import(route.module).then(m => m[route.component]),
name : route.name
});
);
});
this._router.config(this.routeConfigArray);
Another approach could be to add a function to get the name of functions. Based on this you can check if you have a potential component that matches.
Here is a sample:
ngOnInit() {
this.routes = [
{
path: '/test', component: 'OtherComponent', name: 'Test'
}
];
this.configureRoutes(this.routes);
this.router.config( this.routes);
}
configureRoutes(routes) {
var potentialComponents = [ OtherComponent ];
routes.forEach((route) => {
route.component = potentialComponents.find((component) => {
return component.name === route.component;
});
});
}
See this plunkr: https://plnkr.co/edit/KKVagp?p=preview.
See this question for more details:
Dynamic Route Loading in Angular 2 Fails. (Beta)
https://github.com/angular/angular/issues/11437#issuecomment-245995186 provides an RC.6 Plunker
update
In the new router (>= RC.3) https://angular.io/docs/js/latest/api/router/index/Router-class.html#!#resetConfig-anchor resetConfig can be used
router.resetConfig([
{ path: 'team/:id', component: TeamCmp, children: [
{ path: 'simple', component: SimpleCmp },
{ path: 'user/:name', component: UserCmp }
] }
]);
original
What should work is
import from 'myComponents' as myComponents;
...
someFunc(name:string) {
console.debug(myComponents[name]);
}
Routes can be loaded using
constructor(private router:Router) { }
someFunc() {
this.router.config([
{ 'path': '/', 'component': IndexComp },
{ 'path': '/user/:id', 'component': UserComp },
]);
}
I haven't tried this myself.
See also this related question Angular2 App Routing through Services
say. three screens as page1, page2 and page3 and components as app/page1.ts, app/page2.ts and app/page3.ts
let screens : Array<string> = ["Page1","Page2","Page3"];
let aRouter : RouteDefinition;
this.routes = new Array<RouteDefinition>();
screens.map(function(screenId){
aRouter = new AsyncRoute({
path: "/" + screenId,
name: screenId,
loader: () => System.import("app/" + screenId).then(c => c[screenId]) // not import {page1, page2, page3}}
});
this.routes.push(aRouter);
}.bind(this)); //we need to bind to current "this" instead of global this
this.router.config(this.routes);
trick is .bind(this), which is vanella javscript
for whole sulution, check this
Dynamically load Angular 2 Async router
https://github.com/Longfld/DynamicalAsyncRouter