Nuxt.js: Conditional client/server side rendering - vue.js

The goal:
Use vue.js to serve a website with many routes and dynamic content which has open graph and twitter rich cards meta data markup.
The idea:
Requests with user agent containing Twitterbot, facebookexternalhit and so on get redirected to a Server Side Rendering website. All the other users get Client Side Rendering version.
The question:
Is it possible in Nuxt.js to make this conditional rendering? If yes, how? If not, any solutions to the above problem?
Credits to the idea

Nuxt can conditionally render a single page (CSR) or not (SSR) after the client request was received:
module.exports = {
path: '/public',
handler(req, res, next) {
res.spa = req.headers['user-agent'] !== 'Twitterbot'
next()
}
}

Related

Unable to make Navigation (polaris) component work with new shopify session system

I've been trying to rework an app to use the new session auth system.
Everything seems to work fine, however I am not able to use the Navigation (polaris) component successfully.
Let's say I have something like this :
<Navigation.Section
items={[
{
url : '/faq',
label : translations.faq,
icon : HintMajor
}
]}
/>
If I only put /faq I am unable to access the route. I am redirected to /auth and get obviously an error.
The only way I manage to make my routes work is to :
Add the shop query in each url like this :
// ...
url : '/faq?shop=${shop}',
// ...
Add every needed routes manually in my server.js WITHOUT the verifyRequest middleware (which does not seem like a good option to me)
// ...
router.get('/faq', handleRequest)
// ...
Can someone explain if I am doing anything wrong ?
(I am managing my SESSION_STORAGE with the custom redis storage shown in the repo doc https://github.com/Shopify/shopify-node-api/blob/main/docs/usage/customsessions.md)
Are you not supposed to have a wildcard in your App routes so that when you deliver backed end code it is doing just that, and leaving authentication to having the token in your header? No token and a request for anything authenticated and you then direct to login?

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

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!

Load different page template for mobile in nuxt

I'm working on a larger project and need to create a separate UX for mobile users on some pages. Using a responsive layout with CSS won't cut it, and dynamic component rendering with v-if results in a horrifying template.
This answer is the closest that I have come to, but I want to avoid manually defining routes as there are a ton of pages.
I am currently using a middleware to redirect based on a user agent check:
export default function(context) {
if (context.isMobile) {
if (context.route.fullPath.indexOf('/m') !== 0) {
return context.redirect('/m' + context.route.fullPath)
}
}
if (context.isDesktop) {
if (context.route.fullPath.indexOf('/m') === 0) {
return context.redirect(context.route.fullPath.substring(2))
}
}
}
but I don't have a way of telling whether there is a mobile version or not, so if there isn't, the error page will be displayed.
I also tried working with this answer but using nuxt-device-detect instead of breakpoints, but since the router is configured before getting in the browser, the check function will return the fallback option, so it didn't work well for me. Also since I'll be using SSR I'm avoiding things like document.documentElement.clientWidth.
I guess in short, my question is: what is the best practice for serving separate pages to mobile users?

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.

Node.js Response From Image Upload Without Refreshing Client Page

Problem Set: Client .posts image from form action='/pages/contact/image/something' to node.js and I .putItem to AWS S3. On the success response I would like to send the image url back to the client without refreshing the screen and update the location they wanted to add the image with the new src url.
If the page refreshes I lose the location where they wanted to upload the image. Open to any suggestions, I have looked at res.send, res.end, res.json, res.jsonp, res.send(callback): all of which overwrite(refresh) the client webpage with the array, text or context in general I am passing back to the client . Code below:
myrouter.route('/Pages/:Page/Image/:Purpose')
.post(function (req, res) {
controller.addImageToS3(req, res)
.then(function(imgurl){
//res.json({imgurl : imgurl});
//res.send(imgurl);
//res.end(imgurl);
//res.send(req.query.callback(imgUploadResponse(imgurl)))
<response mechanism here>
console.log('Image Upload Complete');
}, function (err){
res.render('Admin/EditPages', {
apiData : apiData,
PageId : PageId
});
});
});
Ideally there could be a passed parameter to a javascript function that I could then use: Example:
function imgUploadResponse(imgurl){
// Do something with the url
}
You, as a developer, have full control over the s3 url format. It follows a straightforward convention:
s3-region.amazonaws.com/your-bucket-name/your-object-name.
For example:
https://s3-us-west-2.amazonaws.com/some-random-bucket-name/image.jpg
While I would recommend keeping those details in the back-end, if you really want to avoid using res.send, you can basically make the front-end aware of the url formatting convention and present the url to the user, even before the image was actually uploaded (just need to append the name of the image to the s3-region.amazonaws.com/your-bucket-name)
Also, I'm not sure why your page would refresh. There are ways to refresh content of your page without refreshing the whole page, the most basic being AJAX. Frameworks like angular provide you with promises that allow you to do back-end calls from the front-end.
Hope this helps!