How to correctly put google sign-in code in a nuxt component - google-oauth

Google provides a friendly .html code in https://developers.google.com/identity/sign-in/web
I tried this code in a .html file and it works totally fine.
After the user is signed in, onSignin would be revoked every time the page is loaded, which means user info will be logged in console
But if I use this code in a nuxt component, only the sign-in process would be run, onSignIn function is not revoked, and no errors were shown.
Here's my code:
<template>
<div class="g-signin2" data-onsuccess="onSignIn"></div>
</template>
<script>
/* eslint-disable */
function onSignIn(googleUser) {
// Useful data for your client-side scripts:
var profile = googleUser.getBasicProfile()
console.log('ID: ' + profile.getId()) // Don't send this directly to your server!
console.log('Full Name: ' + profile.getName())
console.log('Given Name: ' + profile.getGivenName())
console.log('Family Name: ' + profile.getFamilyName())
console.log('Image URL: ' + profile.getImageUrl())
console.log('Email: ' + profile.getEmail())
// The ID token you need to pass to your backend:
var id_token = googleUser.getAuthResponse().id_token
console.log('ID Token: ' + id_token)
}
export default {
head() {
return {
meta: [
{
name: 'google-signin-scope',
content: 'profile email'
},
{
name: 'google-signin-client_id',
content: process.env.GOOGLE_CLIENT_ID
}
],
script: [
{
src: 'https://apis.google.com/js/platform.js',
defer: true,
async: true
}
]
}
}
}
</script>
I'm sure process.env.GOOGLE_CLIENT_ID is correct since I've checked dom tree when running on browser. If you know where the bug is please let me know, thank you.

Vue SFC's script is isolated, and you have declared onSignIn just in this scope. Yeah, this is not going to fly.
Luckily, you can simply add onSignIn function in window scope:
window.onSignIn = onSignIn
Mostly, you've got it all right. But there is even dedicated package ready for this use case: google-signin-vue. I would not recommend blindly using it though; instead look at source code and find features you'd like to have, if needed.

I solved this problem by myself. I checked: how to implement Google Login API in VueJS?
and use the second method the answer provided.
I moved properties in head() to nuxt.config.js like this:
// put this in script array
{
src: 'https://apis.google.com/js/platform.js?onload=renderButton',
defer: true,
async: true
}
// ...
// put this in meta array
{
name: 'google-signin-scope',
content: 'profile email'
},
{
name: 'google-signin-client_id',
content: process.env.GOOGLE_CLIENT_ID
}
in component
// template
<div id="google-signin-button"></div>
// methods
onSignIn(user) {
console.log('invoking onSignin...')
const profile = user.getBasicProfile()
console.log('ID: ' + profile.getId()) // Don't send this directly to your server!
console.log('Full Name: ' + profile.getName())
console.log('Given Name: ' + profile.getGivenName())
console.log('Family Name: ' + profile.getFamilyName())
console.log('Image URL: ' + profile.getImageUrl())
console.log('Email: ' + profile.getEmail())
// The ID token you need to pass to your backend:
var id_token = user.getAuthResponse().id_token
console.log('ID Token: ' + id_token)
}
// mounted lifecycle
mounted() {
setTimeout(() => {
gapi.signin2.render('google-signin-button', {
onsuccess: this.onSignIn
})
})
},
And it works finally. Hope this will help someone.

Related

How to initiate refresh of Vuejs page elements w/ new data?

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

vue-meta => How to change the header information

I have a nuxtjs project with a page opening on a url like server\posts\id. On this page I have added head information to influence the meta tags. However, some tags are post specific and need to be filled dynamically. This only seems possible after you have data loaded in mounted. How can I add the meta maniplulation to mounted?
It seems you need an extra 'data' property. If you use this in the header, and update it later it will change the meta information.
Right way to get meta from api is: using fetch method
async fetch({ store, params }) {
await store.dispatch('modules/item/get_item', params.article)
},
Use Computed :
computed: {
...mapState('modules/item', {
Item: (state) => state.data
})
},
and use nuxt head (vue-meta)
head() {
return {
title:
this.$store.state.modules.general.info.name + ' / ' + this.Item.title,
meta: [
{
hid: 'description',
name: 'description',
content:
this.$store.state.modules.general.info.name +
' / ' +
this.Item.seo_description
},
}

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

How do I use the vue-resource plugin to delete an object on demand?

Using the vue-resource plugin, it has an example like this:
new Vue({
ready: function() {
var resource = this.$resource('someItem{/id}');
// delete item
resource.delete({id: 1}).then(function (response) {
// handle success
}, function (response) {
// handle error
});
}
})
I am somewhat confused that this is under the ready property since it suggests it runs and deletes something as soon as the component loads. How do I actually use that resource.delete function from clicking an element? For example, I have this:
<div #click="deleteReward(reward)" class="glyphicon glyphicon-trash pull-right"></div>
which is currently calling this:
deleteReward(reward) {
this.rewards.$remove(reward);
this.$http.delete('api/reward/' + reward.id).then(function (response) {
console.log('deleted ' + reward.name);
});
},
But my understanding is that I should somehow be able to call resource.delete instead and not have to specify the URL and method explicitly. Am I wrong in that assumption?
What I want is to do something like #click="reward.delete()" and have it know to call the resource.delete automatically and have resource.delete accept the reward object as a parameter.
You could try something like this:
new Vue({
data: {
resource: null
},
ready: function() {
this.resource = this.$resource('api/reward{/id}')
},
methods: {
deleteReward(reward) {
this.resource.delete({id:reward.id}).then(function (response) {
console.log('deleted ' + reward.name)
})
}
}
})
And then:
<div #click="deleteReward(reward)"></div>

"Route Not Found" in console window when Durandal is trying to load first page of app

I'm getting "Route not found" in the console window on trying to load an app converted from 1.2 to 2.0. Is there any way I can debug what route it's trying to find at the point of failure please? It would be handy if it said, "cannot find route:/viewmodels/wrongfolder/startup" or something!
Please be aware that ALL of this was working perfectly prior to upgrading from 1.2 to 2.0, so it's differences in the Durandal settings that I need to address. No files have been removed or lost or moved, so it's not that things have changed in the app outside of the new versions of scripts being updated by nuget.
main.js and config.js live in root of "app" folder. Shell.js is in app/viewmodels and shell.html is in app/views. All views/viewmodels are in the relevant folders below the main /app folder.
I have a "config.js" file with routes returned:
var routes = [{
route: 'home',
moduleId: 'home',
title: 'Home',
nav: true
}, {
route: 'labTool',
moduleId: 'labTool',
title: 'Lab Tool',
nav: true
}];
var startModule = 'labTool';
main.js:
//specify which plugins to install and their configuration
app.configurePlugins({
router: true,
dialog: true,
widget: false
});
app.start().then(function () {
viewLocator.useConvention();
router.makeRelative({ moduleId: 'viewmodels' });
app.setRoot('viewmodels/shell');
router.handleInvalidRoute = function (route, params) {
logger.logError('No route found', route, 'main', true);
};
});
Shell.js:
var inEditMode = ko.observable(false); //set edit mode to false at start
var shell = {
activate: activate,
router: router,
inEditMode: inEditMode
};
return shell;
function activate() {
return datacontext.primeData()
.then(boot)
.fail(failedInitialization);
}
function boot() {
logger.log('Application Loaded!', null, system.getModuleId(shell), true);
router.map(config.routes).buildNavigationModel();
return router.activate(config.startModule);
}
function failedInitialization(error) {
var msg = 'App initialization failed: ' + error.message;
logger.logError(msg, error, system.getModuleId(shell), true);
}
Some of the code may still need editing to handle the change from 1.2 to 2.0 but I think I have most of it now.
I had a similar problem after the upgrade and creating a default route with a route property of '' sorted it for me.
So instead of using your startModule property try setting you labTool route to have a route property of ''.
In case anyone else runs into this, this error can also occur if you have non-ascii characters in the route name.
Not working:
{ route: 'Møøse', ... }
Working:
{ route: 'Moose', title: 'Møøse', ... }