Vuejs detect the change and update the page without user's involvement - vuejs2

I've got the VueJS single page application (SPA) and when users successfully register in my app it redirects them to the Home page. This Home page shows a bar (alert) asking users to confirm the email they have provided during the registration. Note: this bar will we be constantly shown until they confirm it!
Currently when a user confirms the email it opens the confirmation page in a new tab saying that Email have been successfully confirmed! and the bar disappears but when user goes to the original tab (Home page) the bar is still shown.
Is there any way you could listen on the background for a change of the user's state (user.isVerified) and hide the bar without user's involvement (refresh the page/click on any links)?
To get the user's data I'm using an API call:
getUser({ commit }) {
return new Promise((resolve, reject) => {
axios
.get('http://localhost:8000/api/user')
.then(response => {
commit('setData', response.data.data);
resolve(response);
})
.catch(errors => {
reject(errors.response);
})
})
}
JSON response:
{
"data": {
"id": 2,
"name": "John Doe",
"email": "john#email.com",
"isVerified": true,
"createdAt": "2020-04-17T13:17:07.000000Z",
"updatedAt": "2020-04-26T23:15:24.000000Z"
}
}
Vue page:
<template>
<v-content>
<v-alert
v-if="user.isVerified === false"
tile
dense
color="warning"
dark
border="left"
>
<div class="text-center">
<span>
Hey {{ user.name }}! We need you to confirm your email address.
</span>
</div>
</v-alert>
<v-content>
<router-view></router-view>
</v-content>
</v-content>
</template>

To be clear, when you say "listen for state" - each Vue instance has its own state, so your website open in another tab does not naturally share state with the original tab. It sounds like you want your Vue app to listen for changes in your database, so that you can tell when your API has marked your user as 'confirmed'. This is a great opportunity for Websockets.
Essentially, your app has a 'listener' for a 'user confirmed' message. When your API confirms a user, it sends the message. When the listener receives that message, it updates the user confirmation in state and refreshes (or, better, re-requests the user from the API). This is the same technology that powers push notifications, chat apps, etc.

You have to set user.isVerified = true somewhere in the conformation component, e.g. in the mounted hook:
ConformationPage.vue
mounted () {
this.user.isVerified = true
},
If the "user" object is stored in Vuex store, then dispatch or commit change to the store instead:
ConformationPage.vue
mounted () {
this.$store.commit('userIsVerified', true)
}
store.js
mutations: {
userIsVerified (state, value) {
state.user.isVerified = value
}
}
Keep in mind, if that ConformationPage.vue component is a page (route) and not just a component, make sure that users cannot just verify themselves simply by visiting that page before even getting the email.
And also make sure you lazy-load that component, because otherwise it might be rendered when the user opens the website, which will immidiatelly verify them.

tldr;
This is the job for Websockets.
Long answer below
Why do you need Websockets?
The idea behind it is that your application needs to know whether the user has truly confirmed his email. And for it to be successfully confirmed it has to access the backend server which connects to your user database.
So now, you need your backend server to update the data in the frontend application right? Yup! If you are able to answer this, you already halfway through.
However, this is what your frontend application can't do. For the example in the question right now you used axios which uses HTTP Protocol to ask the backend server for data.
So what's up with the HTTP Protocol?
The problem with HTTP Protocol is that it only allows you to request one way to the back end server. So your interaction is only REQUEST and RESPONSE. Ok, so what? Isn't it fine? I can do some sort of setInterval until I get the response I wanted right? Well, that was my thought and technically you could.
However, it's a bad practice and you shouldn't even think about it.
Ok, so how do I know when the database is updated?
This is where the Websocket comes in.
MDN interpretation of Websocket
The WebSocket API is an advanced technology that makes it possible to open a two-way interactive communication session between the user's browser and a server. With this API, you can send messages to a server and receive event-driven responses without having to poll the server for a reply.
In a simple way, it allows your backend server to talk to your frontend application. Do I need to replace my HTTP backend server? No, WebSocket is actually made to be together with HTTP, act like it was some sort of an extension to HTTP Protocol.
Now I would say, this is where the job is hard. To learn about websocket, and create a backend server that supports your needs. Also, your frontend application has to adapt to use WebSocket as well.
If you're really interested in getting some live data and is not interested (or doesn't have time) to create a backend server, you should check out Firebase. It's free, and you can have fun with it.
I hope that can help you get started. Thank you!

Related

In Nuxt/VueJs, how can I get SendInBlue Chat Widget to see route changes?

I'm using SendInBlue chat widget on a Nuxt/VueJs website. Through the
SendInBlue Conversations UI,
the widget reports the visitor's page current location (url and title).
However, the location does not change when the internal route changes (no hard browser location change).
I want to see the location changes.
How can I notify the SendInBlue Chat Widget that the route is changing?
The
SendInBlue Chat Widget API
includes a pageView method that can be called to alert the widget to a route change.
SibConversations('pageView')
This Answer about watching a route
provides the basic framework. Note that in my case I added the code to the /layouts/default.vue since it renders most of my pages. (Add to all necessary layouts.)
<script>
export default {
watch: {
$route() {
// on route change, notify SendInBlue Chat Widget so it updates visitor location
if (process.client) {
// delay 0.5 seconds to be sure route has actually changed ($nexttick does show correct page title)
window.setTimeout(() => {
window.SibConversations('pageView')
}, 500)
}
},
},
// ... and data, methods, etc., as needed
}
</script>
So now when the route changes, we notify the widget.
Note: If we trigger window.SibConversations('pageView') without a delay, I found that the title is not correct. I'm assuming the page is not yet rendered. I tried using this.$nexttick, but the title is still not present. So, I use window.setTimeout() to provide a short delay. I found that 100ms works for me, but I used a longer time in case a slower computer took longer. (And we aren't worried about half-second accuracy.)
I also wrapped the call to ensure it's only called on client side. No nasty "windows undefined" errors.

Vue Router Redirect to Login page

I am trying to test my Vue.js app authentication.
Assume, a user log in and to go to his profile the link would look like this.
https://website.com/profile/1
He can get to his page but what if he is a hacker and if he tried to type
https://website.com/profile/2
which will go to the profile of another person.
How can I redirect this hacker to the login page if he tried to go to a URL that does not belong to his profile?
Right now, I can stop him from viewing another people profile but still, he is still in the profile link even if he typed
https://website.com/profile/2
What I am trying to accomplish is that if users type a link that does not belong to his or her but which is valuable to other people it will redirect to the login page.
I am not storing User Data on the client side.
Firstly, you have to assume that the user is able to manipulate the webpage in any way and access all data stored on the client side. So you should not be storing sensitive data on the client that you do not want the user to access. (You may not be doing this, but I thought I should point it out.)
I assume you have a Profile component. You can use Vue Router navigation guards to authorize the transition and abort or redirect it if it is disallowed.
If the user is logged in then you are probably storing information about the logged in user on the client. You can use this to check if the profile page being visited belongs to the logged in user.
In your Profile component:
beforeRouteEnter(to, from, next) {
if (!loggedInUser) {
// User not logged in
next('/')
} else if (loggedInUser.id !== to.params.userId) {
// User is accessing profile page of another user, redirect
// to their profile page instead
next({ params: { userId: loggedInUser.id } })
} else {
next()
}
}
This is just an example of what you can do, I can't give a specific answer without knowing exactly how your app works.

How to refetch an apollo vuejs after an external oauth2 completion

I have a vuejs/apollo grapqhl Single page app. At one point there is an option to connect it another service (in this case vimeo). The flow is
click to start - this goes to the graphql server and collects a
redirect link to the vimeo oauth2 server with some state attached,
then does the redirect
the consent dialog happens on the vimeo oauth2 server, which
redirects to a route on the graphql server - this updates the back
end database with the vimeo user profile, and then returns to the
main front end app where it left off.
at this point I need to refetch the data updated at step 2, but of course I can't detect when this is complete.
Any suggestions as to how to co-erce the front end to refetch when step 3 is complete ?
Actually I figured out how to do this. Just check for the tab coming back into focus using ifvisible, and force a refetch when it does.
mounted () {
const that = this
ifvisible.on('focus', () => that.$apollo.queries.UserProfile.refetch())
}

node express multer fast-csv pug file upload

I trying to upload a file using pug, multer and express.
The pug form looks like this
form(method='POST' enctype="multipart/form-data")
div.form-group
input#uploaddata.form-control(type='file', name='uploaddata' )
br
button.btn.btn-primary(type='submit' name='uploaddata') Upload
The server code looks like this (taken out of context)
.post('/uploaddata', function(req, res, next) {
upload.single('uploaddata',function(err) {
if(err){
throw err;
} else {
res.json({success : "File upload sucessfully.", status : 200});
}
});
})
My issue is that while the file uploads successfully, the success message is not shown on the same page, ie: a new page is loaded showing
{success : "File upload sucessfully.", status : 200}
As an example for other elements (link clicks) the message is displayed via such javascript:
$("#importdata").on('click', function(){
$.get( "/import", function( data ) {
$("#message").show().html(data['success']);
});
});
I tried doing a pure javascript in order to workaround the default form behaviour but no luck.
Your issue has to do with mixing form submissions and AJAX concepts. To be specific, you are submitting a form then returning a value appropriate to an AJAX API. You need to choose one or the other for this to work properly.
If you choose to submit this as a form you can't use res.json, you need to switch to res.render or res.redirect instead to render the page again. You are seeing exactly what you are telling node/express to do with res.json - JSON output. Rendering or redirecting is what you want to do here.
Here is the MDN primer on forms and also a tutorial specific to express.js.
Alternatively, if you choose to handle this with an AJAX API, you need to use jquery, fetch, axios, or similar in the browser to send the request and handle the response. This won't cause the page to reload, but you do need to handle the response somehow and modify the page, otherwise the user will just sit there wondering what has happened.
MDN has a great primer on AJAX that will help you get started there. If you are going down this path also make sure you read up on restful API design.
Neither one is inherently a better strategy, both methods are used in large-scale production applications. However, you do need to choose one or the other and not mix them as you have above.

SignalR inside _Layout.cshtml - persist connection

I decided to use SignalR for chat on my page. Chat page is opened when user clicks on "Enter Chat" link which is placed inside _Layout.cshtml. This works fine. However, what I would like to achieve is the following functionality:
On the left side of the page I would like to have some kind of
"online users" area and when one user logins, other users whose are
already logged in will be able to see that a new user just enters the
page.
Users who are online can chat with each other by simply
clicking on their names
I am using the following code to connect to the chat application:
$(function () {
//declare a proxy to reference the hub
var chatHub = $.connection.chatHub;
registerClientMethods(chatHub);
//Start Hub
$.connection.hub.start().done(function () {
registerEvents(chatHub);
chatHub.server.connect(#User.Identity.Name);
});
});
However when I place this code inside my _Layout.cshtml page, users are permanently logged off and connected again each time they navigate through pages (they are intended to be opened inside _Layout.cshtml).
Is there any way for persisting connection with the hub when navigating through page? What is the best practices when using this kind of functionality?
Whenever you navigate away from a page or in any way refresh the contents of the page you will need to start a fresh SignalR connection. There are two ways to handle this behavior when navigating through pages:
Create a single page application.
Handle users connecting/disconnecting on the server in such a way that they're not truly logged out until they leave the site.
Now to dive into a little more detail on #2. Users on your site may disconnect/connect every time they transition to a new page but you can control how they log out or appear disconnected via your server side logic. You can get this functionality by keeping a set of "online" users in your server side code and then only considering them offline after a specified timeout value.