I'm developing a Vue.js app and I'm trying to make a log in system using multiple components.
on my App.vue component (which is the "primary" component and has the nav-bar) there is this button:
<a class="nav-item" v-link="'login'" v-if="!user.authenticated">Login</a>
...
import auth from '../auth'
export default {
data(){
return{
user = auth.user
}
}
}
In one other file (/auth/index.js) I define the "auth" methods as such:
...
user: {
authenticated: false
},
login(email, password) {
axios.post(LOGIN_URL, {
email,
password
}).then((response) => {
this.user.authenticated = true;
this.user = response.data;
router.go('/home');
})
.catch((error)=>{
console.log(error);
});
}
And then I have another component name Login.vue that handles the view for the login template and calls the "auth.login" method with the required params.
The thing is that I want to update the user from App.vue with the value from auth.user AFTER he logs in. How should I do that? Do I have to manage the v-on:click and v-link priorities and come up with a new method?
EDIT:
I'm also trying to use the beforeRouteEnter method by I am not being successful
I managed to find a solution adding this:
watch: {
$route () {
this.user = auth.user
}
},
Following the documentation here
Related
i am using Vuex and Firebase Authentication. I got stuck when reload the page. firebase.auth().onAuthStateChanged take time to response. But i need at the same time when reload the page. I have seen many tutorials in internet, most of them is router guard, but that i don’t want. I have some route where the user has login, then can navigate to this route.
App.vue where i am applying.
created () {
firebase.auth().onAuthStateChanged(async user =>{
if (user){
await this.$store.dispatch('autoSignIn',user)
}
})
}
Here is my vuex action theat trigger when page reload to auto sign in if a user was logged before reload the page.
autoSignIn ({commit}, payload) {
commit('setUser',{email:payload.email, userId:payload.uid})
}
This is my getter
isAuthenticated:state => {
return state.user !== null && state.user !== undefined ? state.user : null
}
Here is where i am calling my getter isAuthenticated.
getEventsByUser({getters,commit}){
let data = [];
firebase.database().ref('usuario/' + getters.isAuthenticated.userId + '/eventos/')
.on("value", eventos =>{
eventos.forEach(evento =>{
data.push({"id":evento.key, ...evento.val()})
});
commit('setEventsByUser',data)
})
},
And this is the component which dispatch the action
<template>
<div>
<div v-for="(event,id) in getEventsByUser" :key="id">
{{event}}
</div>
</div>
</template>
<script>
export default {
name: "MyEvents",
computed:{
getEventsByUser(){
return this.$store.getters.getEventsByUser;
},
},
mounted() {
this.$store.dispatch('getEventsByUser')
},
}
Here is the error when i reload the page
When the page loads, Firebase checks whether the ID token that is stored for the user is still valid. This requires that it calls the server, so it may take a moment. During this check the user will be null, so your code needs to handle that everywhere.
In your onAuthStateChanged handler you handle this correctly with:
firebase.auth().onAuthStateChanged(async user =>{
if (user){
But then in getEventsByUser you assume there is a user, which (as shown by the error message) is not true. So you'll want to add a check there, to see if there's a user, before attaching the listener to the database:
getEventsByUser({getters,commit}){
let data = [];
if (getters.isAuthenticated) {
firebase.database().ref('usuario/' + getters.isAuthenticated.userId + '/eventos/')
...
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 am new to vue-router navigation guards and so I recently realized that I needed to use beforeRouteUpdate guard for reused components where for example: Going from /foo/1 to /foo/2
However, while coming to /foo/1, I pulled data from database through an axios call and before going to /foo/2, I need to pull new data again through the axios call.
This is where I face a problem where the navigation guard beforeRouteUpdate loads the component /foo/2 before my data loads from the axios call and thus I get null in a few of my variables.
How can I make beforeRouteUpdate wait to load the next component so that all my data is loaded from the axios calls?
As for my code, it looks like this:
beforeRouteUpdate (to, from, next) {
Vue.set(this.$store.state.user, 'get_user', null)
this.$store.dispatch(OTHER_PROFILE_GET, to.params.id).then(resp => {
console.log(resp);
if(this.$store.getters.is_user_loaded) {
next()
} else {
this.$store.watch((state, getters) => getters.is_user_loaded, () =>
{
if(this.$store.getters.is_user_loaded) {
console.log(this.$store.state.user.get_user);
console.log('comes here');
next()
}
})
}
})
},
To explain my code further, I have called this method in my component and so I when I go from /user/1 to /user/2 I dispatch a Vuex action which makes an axios call to get the new profile details but before the axios call completes and loads the data in the Vuex state, the beforeRouteUpdate already loads the next component.
First, your action should perform any state mutation such as setting user.get_user to null. I'm also not sure why you've added a watch; your action should only resolve when complete. For example
actions: {
[OTHER_PROFILE_GET] ({ commit }, id) {
commit('clearUserGetUser') // sets state.user.get_user to null or something
return axios.get(`/some/other/profile/${encodeURIComponent(id)}`).then(res => {
commit('setSomeStateData', res.data) // mutate whatever needs to be set
})
}
}
then your route guard should have something like
beforeRouteUpdate (to, from, next) {
this.$store.dispatch(OTHER_PROFILE_GET, to.params.id).then(next)
}
In order to prevent errors from trying to render null data, use your getters. For example, say your getter is
getters: {
is_user_loaded (state) {
return !!state.user.get_user
}
}
in your component, you can map this to a computed property...
computed: {
isUserLoaded () {
return this.$store.getters.is_user_loaded // or use the mapGetters helper
},
user () {
return this.$store.state.user.get_user // or use the mapState helper
}
}
then in your template, use this logic to conditionally render some data
<div v-if="isUserLoaded">
Hello {{user}}
</div>
<div v-else>
Loading...
</div>
This is the suggested approach in the vue-router guide for beforeRouteUpdate
I am using vuejs 2 and I am having issues with using the Google auth signin.
I successfully setup and got the sign out and user profile functions working using vue:
export default {
data() {
return {
user: null
};
},
methods: {
getUserProfile() {
const profile = gapi.auth2.currentUser.get().getBasicProfile();
console.log(profile.getIdToken());
},
signOut() {
const auth2 = gapi.auth2.getAuthInstance();
auth2.signOut().then(function () {
console.log('user signed out');
});
}
},
};
My main issue here is the onSignIn(googleUser) function from
<div class="g-signin2" data-onsuccess="onSignIn"></div>
The data-onsuccess="onSignIn" is looking for a js function outside the vue instance.
I tried adding the onSignIn(googleUser) function in my HTML file like:
<script>
function onSignIn(googleUser) {
const auth2 = gapi.auth2.init();
if (auth2.isSignedIn.get()) {
const profile = auth2.currentUser.get().getBasicProfile();
console.log(profile.getName());
console.log(googleUser.getAuthResponse().id_token);
console.log(googleUser.getAuthResponse().uid);
console.log(auth2.currentUser.get().getId());
}
}
</script>
This works as expected, but I wanted to know if it would be possible to add this in my vue file instead of a native javascript way, since inside this function, I will be calling other vue methods.
Or is there a way where I could add the onSignIn(googleUser) function in vue and then call it when Google Auth finishes?
The solution here is to use gapi.signin2.render to render the sign-in button inside your component's mounted hook
<template>
<div id="google-signin-btn"></div>
</template>
<script>
export default {
methods: {
onSignIn (user) {
// do stuff, for example
const profile = user.getBasicProfile()
}
},
mounted() {
gapi.signin2.render('google-signin-btn', { // this is the button "id"
onsuccess: this.onSignIn // note, no "()" here
})
}
}
</script>
See https://developers.google.com/identity/sign-in/web/reference#gapisignin2renderid_options
I'm giving Vue.js a try and so far I'm loving it because it's much simpler than angular. I'm currently using vue-router and vue-resource in my single page app, which connects to an API on the back end. I think I've got things mostly working with a the primary app.js, which loads vue-router and vue-resource, and several separate components for each route.
Here's my question: How do I use props to pass global data to the child components when the data is fetched using an asynchronous AJAX call? For example, the list of users can be used in just about any child component, so I would like the primary app.js to fetch the list of users and then allow each child component to have access to that list of users. The reason I would like to have the app.js fetch the list of users is so I only have to make one AJAX call for the entire app. Is there something else I should be considering?
When I use the props in the child components right now, I only get the empty array that the users variable was initialized as, not the data that gets fetched after the AJAX call. Here is some sample code:
Simplified App.js
var Vue = require('vue');
var VueRouter = require('vue-router')
Vue.use(VueRouter);
var router = new VueRouter({
// Options
});
router.map({
'*': {
component: {
template: '<p>Not found!</p>'
}
},
'/' : require('./components/dashboard.js'),
});
Vue.use(require('vue-resource'));
var App = Vue.extend({
ready: function() {
this.fetchUsers();
},
data: function() {
return {
users: [],
};
},
methods: {
fetchUsers: function() {
this.$http.get('/api/v1/users/list', function(data, status, response) {
this.users = data;
}).error(function (data, status, request) {
// handle error
});
}
}
});
router.start(App, '#app')
Simplified app.html
<div id="app" v-cloak>
<router-view users = "{{ users }}">
</router-view>
</div>
Simplified dashboard.js
module.exports = {
component: {
ready: function() {
console.log(this.users);
},
props: ['users'],
},
};
When dashboard.js gets run, it prints an empty array to the console because that's what app.js initializes the users variable as. How can I allow dashboard.js to have access to the users variable from app.js? Thanks in advance for your help!
p.s. I don't want to use the inherit: true option because I don't want ALL the app.js variables to be made available in the child components.
I believe this is actually working and you are being misled by the asynchronous behavior of $http. Because your $http call does not complete immediately, your console.log is executing before the $http call is complete.
Try putting a watch on the component against users and put a console.log in that handler.
Like this:
module.exports = {
component: {
ready: function() {
console.log(this.users);
},
props: ['users'],
watch: {
users: {
handler: function (newValue, oldValue) {
console.log("users is now", this.users);
},
deep: true
}
}
}
};
In the new version of Vue 1.0.0+ you can simply do the following, users inside your component is automatically updated:
<div id="app" v-cloak>
<router-view :users="users"></router-view>
</div>