I have an Iron Router route where in waitOn I subscribe to a publication.
If I load my main page first and then click the link that references that route everything is fine. But if I reload the page the subscription doesn't work: it returns an empty collection.
Router.route('/profileEdit/:_id',
{
waitOn: function () {
return Meteor.subscribe('getUser');
},
data: function () {
let user = Meteor.users.findOne({_id:this.params._id});
if (!user) {
throw new Error('user undefined');
}
let profile = {
'profile.firstName': user.profile.firstName,
'profile.lastName': user.profile.lastName,
email: user.emails[0].address,
'profile.age':user.profile.age,
'profile.gender':user.profile.gender,
'profile.aboutMe':user.profile.aboutMe,
}
return profile;
},
name: 'profileEdit'
}
);
Related
I have a page with a lot of inputs where the user can change the details of an event and after that the user can click on the save button which would trigger the saveChanges() method which would re-render the page with the new inputs. This would be only a mock implementation for now as the backend is not ready so if there would be a full page reload the changes would disappear.
I store the changes in my store.state and would like to see if there is a store.state.newEvent in my preloader if there is then the params.event should be the store.state.newEvent if not then the original event object.
data() {
return {
event: this.$route.params.event,
newEvent: { ...this.$route.params.event},
...
};
},
saveChanges() {
store.setNewEvent(this.newEvent);
this.$router
.push({
name: RouteNames.EventManagement.name,
params: {
ID: this.event.id,
},
});
},
my router
const routes = [
{
name: RouteNames.EventManagement.name,
path: RouteNames.EventManagement.path,
beforeEnter: async to => {
await managementPreload.preload(to);
},
component: EventManagement,
},
]
my preloader
async preload(to) {
store.showSpinner();
console.log(store.state.newEvent);
if (store.state.newEvent) {
to.params.event= store.state.newEvent;
} else {
let data = await EventManagementManager.getById({
id: to.params.ID,
});
to.params.event= data.event;
}
store.hideSpinner();
return to;
}
When I try to call the saveChanges() the console.log does not even get triggered so I guess the program does not get to the preloader at all.
Not specific to Vue.js but to Javascript Single Page applications. If you have a form and a rather long running submit action, like saving something. The submit operation should save something and then pushing to a new route for a success message.
While waiting for the result, the user clicks on a different link and is going away.
See this fiddle:
https://jsfiddle.net/hajbgt28/4/
const Home = {
template: '<div><button #click="submit">Save and go Bar!</button></div>',
methods: {
async submit() {
await setTimeout(() => {
this.$router.push("/bar");
}, 5000);
}
}
};
const Foo = { template: '<div>Foo</div>' }
const Bar = { template: '<div>Bar</div>' }
const router = new VueRouter({
mode: 'history',
routes: [
{ path: '/', component: Home },
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
})
new Vue({
router,
el: '#app',
data: {
msg: 'Hello World'
}
})
Click Home
Click the button
Click on "Foo" immediately, you see "Foo"
Wait a few seconds
The Page changes to "Bar"
I have a two solutions in my mind:
I can check inside the submit operation if I am still on the route I expect and only proceed if the user is still on this page. It is rather complicated to check it every time
Disable all links on the page while loading. But this makes the page useless until the operation is finished.
What is the best practice for situations like this?
You could use a beforeRouteLeave navigation guard to abort that action (i.e., cancel the timer in your example) upon switching routes.
Assuming identifiable submit actions, save the ID of the operation result (i.e., save the timer ID from setTimeout's return value in your example).
Add a beforeRouteLeave handler to the component to cancel the submit action (i.e., clear the timer ID in your example).
const Home = {
methods: {
submit() {
this.timerId /* 1 */ = setTimeout(() => {
this.$router.push("/bar");
}, 5000);
}
},
beforeRouteLeave (to, from, next) {
clearTimeout(this.timerId) /* 2 */
next()
}
};
updated jsfiddle
Here's one idea: make a component that provides (using Vue's provide/inject API):
A function that starts an operation. This is called when a form is sent. It provides a whenDone callback which is either executed or ignored, depending on if the operation is cancelled.
A function that cancels all pending operations. The cancel function could be called when the user navigates away.
The implementation could look like this:
const CancellableOperationProvider = {
name: "CancellableOperationProvider",
props: {},
data: () => ({
pendingOperations: []
}),
/*
* Here we provide the theme and colorMode we received
* from the props
*/
provide() {
return {
$addOperation(func) {
this.pendingOperations.push(func);
func(function whenDone(callback) {
if (this.pendingOperations.includes(func)) callback();
});
},
$cancelAllOperations() {
this.pendingOperations = [];
}
};
},
render() {
return this.$slots.default[0];
}
};
The usage would look like this:
const Home = {
template: '<div><button #click="submit">Save and go Bar!</button></div>',
inject: ['$addOperation', '$cancelAllOperations'],
methods: {
async submit() {
this.$addOperation(whenDone => {
await setTimeout(() => {
whenDone(() => this.$router.push("/bar"));
}, 5000);
});
}
}
};
You could then add a navigation guard to the Vue Router so that $cancelAllOperations is called after clicking any link. Since $cancelAllOperations is only accessible through the inject API you will have to make a component that imperatively adds a navigation guard to the Vue router after mounting and removes it when unmounting.
Let me know if it doesn't work--I haven't done Vue in a while.
I used the answer from tony19 to make solution which fits my needs for use cases without setTimeout too:
const Home = {
template: '<div><button #click="submit">Save and go Bar!</button></div>',
data() {
return {
onThisPage: true
}
},
beforeRouteLeave(to, from, next) {
this.onThisPage = false;
next();
},
methods: {
submit() {
setTimeout(() => {
if (this.onThisPage) {
this.$router.push("/bar");
}
}, 5000);
}
}
};
See here: https://jsfiddle.net/ovmse1jg/
I am creating an application where I have a list of users, when I click on a single user, it takes me to that specific users profile (Profile.vue). I am using ASP.NET Core API with Vue.js as my front end. My API is working so when I click on the user, I am able to see the data coming from my database using Chrome dev Tools and Postman. When I open Vue Dev Tools in Chrome, I see that the data is "undefined". For example, I am just trying to get the users firstName to display so I know that its working.
This is how I am routing my page from the list of users to a specific users profile
methods: {
editItem(lastName) {
this.$http.get(`http://localhost:61601/api/GetInquiry/${lastName}`)
this.$router.push({ path: `/Profile/${lastName}` })
},
async GetAllInquiries() {
this.loading = true
try {
this.records = await api.GetAllInquiries()
} finally {
this.loading = false
}
},
Once I am routed, Here is my Profile.Vue that will show the users information
<template>
<div>
<h2>Student Info</h2>
Student Name: {{ records.firstName }}
<br />
</div>
</template>
<script>
import api from '../store/api.js'
export default {
data() {
return {
records: {
firstName: this.firstName
},
}
},
async created() {
this.GetInquiriesByUser()
},
methods: {
async GetInquiriesByUser() {
this.loading = true
},
post: function () {
this.$http.get('http://localhost:61601/api/inquiry', {
firstName: this.firstName
})
}
}
}
</script>
API.js
import Vue from 'vue'
import axios from 'axios'
const client = axios.create({
baseURL: 'http://localhost:61601/api/',
json: true
})
export default {
async execute(method, resource, data) {
return client({
method,
url: resource,
data,
}).then(req => {
return req.data
})
},
GetAllInquiries() {
return this.execute('get', '/Inquiry')
},
GetInquiriesByUser() {
return this.execute('get', '/GetInquiry/')
},
create(data) {
return this.execute('post', '/', data)
},
update(id, data) {
return this.execute('put', `/${id}`, data)
},
delete(id) {
return this.execute('delete', `/${id}`)
}
}
GetInquiryByUser Controller
[Route("api/[controller]")]
public class GetInquiryController : BaseController
{
private GetInquiryByUser manager;
public GetInquiryController(IConfiguration config) : base(config)
{
manager = new GetInquiryByUser(config);
}
[HttpGet("{lastName}")] //api/GetInquiry/yourlastname
public IEnumerable<InquiryModel> Get([FromRoute]string lastName)
{
return manager.GetInquiriesByUser(lastName);
}
}
The Inquiry contoller gets the list of all users and my GetInquiryByUser is passing the lastName to get that specific users profile. (eventually I will pass a unique id, just testing for now)
I am using hash mode for vue routing as well. At first I was confused on what mode I was using and I had a combination of history and hash, but I think I am all hash mode now.
If someone can point me into the right directions, that would be awesome! Please let me know if I need to porvide more details.
I'm trying to create a page for users so that when they are logged in they can see the content they've uploaded onto the site. I can't seem to get the page to render. I've searched all over, but can't seem to find anything. Any ideas of where I'm going wrong?
from my publication.js file
Meteor.publish('myTickets', function() {
var currentUserId = this.userId;
return Tickets.find({
createdBy: currentUserId
})
});
from the router.js
Router.route('/users/:_id', {
name: 'userPage',
waitOn: function() {
return Meteor.subscribe('myTickets');
},
data: function() {
return Tickets.find({
userId: this.userId
})
}
});
userpage.js
Template.userPage.helpers({
tickets: function() {
var currentUserId = Meteor.userId();
return Tickets.find({
createdBy: currentUserId
}, {
sort: {
createdAt: -1
}
});
}
});
I asked you in a comment what you exactly wanted to do (you have a route with a parameter, but you are never using it), but anyway, I think I got your problem;
You are using this.userId to get your current user ID in your router, but your should use Meteor.userId() instead; this.userId can only be used in publish methods.
I have a REST API that read/save data from a MongoDB database.
The application I use retrieves a form and create an object (a job) from it, then save it to the DB. After the form, I have a button which click event triggers the saving function of my controller, then redirects to another url.
Once I click on the button, I am said that the job has well been added to the DB but the application is jammed and the redirection is never called. However, if I reload my application, I can see that the new "job" has well been added to the DB. What's wrong with this ??? Thanks !
Here is my code:
Sample html(jade) code:
button.btn.btn-large.btn-primary(type='submit', ng:click="save()") Create
Controller of the angular module:
function myJobOfferListCtrl($scope, $location, myJobs) {
$scope.save = function() {
var newJob = new myJobs($scope.job);
newJob.$save(function(err) {
if(err)
console.log('Impossible to create new job');
else {
console.log('Ready to redirect');
$location.path('/offers');
}
});
};
}
Configuration of the angular module:
var myApp = angular.module('appProfile', ['ngResource']);
myApp.factory('myJobs',['$resource', function($resource) {
return $resource('/api/allMyPostedJobs',
{},
{
save: {
method: 'POST'
}
});
}]);
The routing in my nodejs application :
app.post('/job', pass.ensureAuthenticated, jobOffers_routes.create);
And finally the controller of my REST API:
exports.create = function(req, res) {
var user = req.user;
var job = new Job({ user: user,
title: req.body.title,
description: req.body.description,
salary: req.body.salary,
dueDate: new Date(req.body.dueDate),
category: req.body.category});
job.save(function(err) {
if(err) {
console.log(err);
res.redirect('/home');
}
else {
console.log('New job for user: ' + user.username + " has been posted."); //<--- Message displayed in the log
//res.redirect('/offers'); //<---- triggered but never render
res.send(JSON.stringify(job));
}
});
};
I finally found the solution ! The issue was somewhere 18inches behind the screen....
I modified the angular application controller like this :
$scope.save = function() {
var newJob = new myJobs($scope.job);
newJob.$save(function(job) {
if(!job) {
$log.log('Impossible to create new job');
}
else {
$window.location.href = '/offers';
}
});
};
The trick is that my REST api returned the created job as a json object, and I was dealing with it like it were an error ! So, each time I created a job object, I was returned a json object, and as it was non null, the log message was triggered and I was never redirected.
Furthermore, I now use the $window.location.href property to fully reload the page.