Nuxt.js & contentful website not generating dynamic routes properly - vue.js

I've just finished building a nuxt.js & contentful website. There are several routes that need to be generated when people hit the website but it doesn't seem to generate all the routes or not recognise some pages unless I refresh. Example - I upload a blog post to contentful and it doesn't appear in the list of blog posts but when I change the text of a blog that is appearing with no issue, I have attached my config generate below
generate: {
routes () {
return Promise.all([
client.getEntries({
'content_type': 'product'
}),
client.getEntries({
'content_type': 'kebaProduct'
}),
client.getEntries({
'content_type': 'blogPost'
}),
])
.then(([productEntries, kebaEntries, blogEntries]) => {
return [
...blogEntries.items.map(entry => `/blog/${entry.fields.slug}`),
...productEntries.items.map(entry => `/products/${entry.fields.slug}`),
...kebaEntries.items.map(entry => `/products/ev-charging/${entry.fields.slug}`),
]
})
}
It works fine when I am on localhost and all the product routes are being generated and updated fine, only some of the 'kebaProduct' routes are being created when I run npm run generate. Not sure what I am missing
Note when I do generate although I have 5 'kebaProducts on contentful' it only generates one .html file not sure what the expected behaviour is.

Figured it out. If some content has been specified and it isn't present in the contentful code then the page will fail to be generated as it will throw an error. You can do checks with v-if for content and conditionally render it that way or make sure all fields are 'required' in the Contentful validations

Related

Cypress - run test in iframe

I'm trying to find elements in iframe but it doesn't work.
Is there anyone who have some system to run tests with Cypress in iframe? Some way to get in iframe and work in there.
It's a known issue mentioned here. You can create your own custom cypress command which mocks the iframe feature. Add following function to your cypress/support/commands.js
Cypress.Commands.add('iframe', { prevSubject: 'element' }, ($iframe, selector) => {
Cypress.log({
name: 'iframe',
consoleProps() {
return {
iframe: $iframe,
};
},
});
return new Cypress.Promise(resolve => {
resolve($iframe.contents().find(selector));
});
});
Then you can use it like this:
cy.get('#iframe-id')
.iframe('body #elementToFind')
.should('exist')
Also, because of CORS/same-origin policy reasons, you might have to set chromeWebSecurity to false in cypress.json (Setting chromeWebSecurity to false allows you to access cross-origin iframes that are embedded in your application and also navigate to any superdomain without cross-origin errors).
This is a workaround though, it worked for me locally but not during CI runs.
This works for me locally and via CI. Credit: Gleb Bahmutov iframes blog post
export const getIframeBody = (locator) => {
// get the iframe > document > body
// and retry until the body element is not empty
return cy
.get(locator)
.its('0.contentDocument.body').should('not.be.empty')
// wraps "body" DOM element to allow
// chaining more Cypress commands, like ".find(...)"
// https://on.cypress.io/wrap
.then(cy.wrap)
}
spec file:
let iframeStripe = 'iframe[name="stripe_checkout_app"]'
getIframeBody(iframeStripe).find('button[type="submit"] .Button-content > span').should('have.text', `Buy me`)
that is correct. Cypress doesn't support Iframes. It is simple not possible at the moment. You can follow (and upvote) this ticket: https://github.com/cypress-io/cypress/issues/136

Image require() in nuxt with hot reload by HRM webpack

I use the dynamic source for vue-webpack images in nuxt :src="require('path/to/image' + dynamic.variable)" in my project navbar. If the users substitute their image through a form which refetches their information and deletes their previous image I get a webpack error module (img) not found (it does not find the new one): is there a way to solve this, like wait for webpack HRM to finish?
I tried setting up a setTimeout() of one second before user re-fetch and it works, but I don't like a random waiting, I'd use a promise or a sync dynamic, the point is webpack hot reload is not controlled by my functions.. I also tried with setting the dynamic path as a computed: but it doesn't fix.
My image tag:
<img v-if="this.$auth.user.image" class="userlogo m-2 rounded-circle" :src="require('#assets/images/users/' + this.$auth.user.image)" alt="usrimg">
My Useredit page methods:
...
methods: {
userEdit() {
//uploads the image
if (this.formImageFilename.name) {
let formImageData = new FormData()
formImageData.append('file', this.formImageFilename)
axios.post('/db/userimage', formImageData, { headers: { 'Content-Type': 'multipart/form-data' } })
// once it has uploaded the new image, it deletes the old one
.then(res=>{this.deleteOldImage()})
.catch(err=>{console.log(err)})
}else{
this.userUpdate() //if no new image has to be inserted, it proceeds to update the user information
}
},
deleteOldImage(){
if(this.$auth.user.image){axios.delete('/db/userimage', {data: {delimage: this.$auth.user.image}} )}
console.log(this.$auth.user.image + ' deleted')
this.userUpdate() // it has deleted the old image so it proceeds to update the user information
},
userUpdate(){
axios.put(
'/db/user', {
id: this.id,
name: this.formName,
surname: this.formSurname,
email: this.formEmail,
password: this.formPassword,
image: this.formImageFilename.name,
})
.then(() => { console.log('User updated'); this.userReload()}) // reloads the updated user information
.catch(err => {console.log(err)} )
},
userReload(){
console.log('User reloading..')
this.$auth.fetchUser()
.then(() => { console.log('User reloaded')})
.catch(err => {console.log(err)} )
},
}
...
the problem happens after "console.log('User reloading..')" and before "console.log('User reloaded');", it is not related to the file upload nor the server response. I broke a single function in many little ones just to check the function progression and its asynchronous dynamics but the only one that is not manageable is the webpack hot reload :/
I'd like the users to upload their images and see their logo in the Navbar appear updated after submitting the form.
First of all, as somebody told you in the comments, webpack hmr shouldn't be used for production.
In Nuxt, everything that you reference from the assets folder will be optimized and bundled into the project package. So the ideal use case for this folder is all assets that can be packaged and optimized, and most likely won't change like fonts, css, background images, icons, etc.
Then, require is called only once by webpack when it is either building the site for local development or building the site for generating a production package. The problem in your case is that you delete the original file while you're in development and webpack tries to read it and fails.
In the case of these images that the user uploads, I think you should use the static folder instead and instead of using require you'll have to change the :src with
:src="'/images/users/' + this.$auth.user.image"
Let me know if this helps.
Okay, I probably solved it.
HMR: you are of course right. Thank you for pointing out, I am sorry, I am a beginner and I try to understand stuff along the way.
Aldarund, thank you, your idea of not changing the path and cache it client side.. I am too noob to understand how I could implement it ( :) ) but it gave me a good hint: the solution was to keep the image name as the user id + the '.png' extension and to manage the image with jimp so that the image name, extension and file type are always the same, and with or without webpack compiling the new path, I always have the correct require().
Jair, thank you for the help, I didn't follow that road, but I will keep it as a second chance if my way creates errors. Just to be specific: the error comes when it does not find -and asks for the name of- the NEW image, not the OLD one, as I wrote in my question: it happens because the fetchUser() functions reloads the user information including the new image name.
Do you guys see any future problems in my methodology?
Really thank you for your answers. I am learning alone and it's great to receive support.

Headless CMS and static pages? Content updates?

I am trying to use my first Headless CMS and I've tried both Prismic.io and Contentful.
For instance, this is the code from Contentful guide:
asyncData({ env }) {
return Promise.all([
// fetch the owner of the blog
client.getEntries({
'sys.id': env.CTF_PERSON_ID
}),
// fetch all blog posts sorted by creation date
client.getEntries({
content_type: env.CTF_BLOG_POST_TYPE_ID,
order: '-sys.createdAt'
})
])
.then(([entries, posts]) => {
// return data that should be available
// in the template
return {
person: entries.items[0],
posts: posts.items
}
})
.catch(console.error)
}
This works fine and I am able to fetch my blog posts in
<article v-for="post in posts" :key="post">
<h2>{{ post.fields.title }}</h2>
<p>{{ post.fields.content }}</p>
</article>
However, if I generate static pages with Nuxt, I understood the page will still load the latest version of the content from Contentful when live, while instead it just keeps the static content fetched on the pages when generated.
Am I missing the main point here?
Thanks
What you discovered is correct. Nuxt in its current version makes requests to the contentful API when new navigations occur. Afaik there are plans to write the data to disk during build time (e.g. Gatsby does it like that) but these are not implemented yet.
Personally, I'm running my private blog on exactly this tech stack and there is a small time window where static pages and the dynamically loaded part are different. This wasn't a bit problem for me so far. I can understand though that this could cause troubles.

using vue-head with prerender-spa-plugin is causing title and meta tags to be displayed twice on netlify

This issue only happens when live on netlify ( despite their prerender option turned off ), not while being served locally.
the live site shows :
<title>about | anonplayer about | anonplayer</title>
title and meta tags are set using the vue-head package like so
head: {
title: {
inner: "about | anonplayer",
separator: ' ',
}, ...
and this happens for all routes of my single page app and also to meta tags where there are two sets of the tags I intended to have.
looks like this
I used the default prerender settings like so:
config.plugins.push(new PrerenderSPAPlugin({
// Required - The path to the webpack-outputted app to prerender.
staticDir: path.join(__dirname, 'dist'),
// Required - Routes to render.
routes: ['/', '/about'].concat(contracts.map(each => `/${each.abi}/${each.contract}`)),
}))
was the same, but with Angular
in my case helped replacing function this.meta.addTag() with this.meta.updateTag()
so think it's not hosting issue)

What is the correct place to set the Default Namespace for a Multiple Module project created with Phalcon DevTools?

I just started looking about Phalcon this week, I'm trying to create a multiple module application with the dev tools.
The result of running phalcon project <name> multiple only creates one module ('frontend') and it works fine. However, when I add a second module (by copying the frontend one and changing the namespace to \Backend , I couldn't get to the Backend\IndexController class.
After reading the doc page about mutiple module applications and looking at the samples (https://github.com/phalcon/mvc/tree/master/multiple and https://github.com/phalcon/mvc/tree/master/multiple-volt) and an old question on the Google group (sorry, can't post more than 2 links since I'm new on StackOverflow), I've ended commenting this this line on the services.php file:
$router->setDefaultNamespace("MyL\Frontend\Controllers"); //project name is MyL
and adding the following on the setServices of my backend/Module.php file:
$di->set('dispatcher', function() {
$dispatcher = new Dispatcher();
$dispatcher->setDefaultNamespace("MyL\Backend\Controllers");
return $dispatcher;
});
and the something similar on the frontend/Module.php
It works with these modifications, but my question is: is this the best way to do it, or is there a better way?
Thanks in advance!
You need to register your modules in your app like so:
$app = new \Phalcon\Mvc\Application();
$app->registerModules(
[
'frontend' => [
'className' => 'MyL\Frontend\Controllers',
'path' => '../apps/frontend/Module.php'
],
'backend' => [
'className' => 'MyL\Backend\Controllers',
'path' => '../apps/backend/Module.php'
],
]
);
Make sure that you have the Module.php ready also for each module
Here is a simple way to split frontend and backend in Phalcon project without modules:
https://github.com/borzov/phalcon-templates