How to fetch files from CDN with NuxtJS content module? - javascript

I want to fetch files from another server (e.g. a CDN) with the #nuxtjs/content module so that the .md files can be managed independently without Nuxt.js.
My current nuxt.config.js file looks like this:
export default {
...
content: {
dir: 'http://some-cdn.xyz/content/'
},
...
}
Now I want to load the content in the pages/_slug.vue file:
<template>
<div>
<NuxtContent :document="doc" />
</div>
</template>
<script>
export default {
async asyncData({ $content, params }) {
const doc = await $content(params.slug || 'index').fetch();
return { doc };
},
};
</script>
Now when I type in http://localhost:3000/some-page, I should get the corresponding file (some-page.md) from the CDN. Instead, I get this error from Nuxt.js: /some-page not found.
What should I do to get it working and is this even possible with my current setup?

As told here: https://github.com/nuxt/content/issues/237
This is not doable with the content module and is not planned to be done neither. A solution would be to manually fetch the files during the build time or alike.
You can maybe get some inspiration from this comment: https://github.com/nuxt/content/issues/37#issuecomment-664331854 or use another markdown parser.

Since #nuxtjs/content can't fetch files from another server, I used the marked.js library instead.

Related

How to import a file using ES6 modules, and read its content as a string?

In my standard React app, I need to achieve the following: import a file that sits within my src folder, in order to simply read its content as a string. For example, let's say I have the following code in a file:
alert('hey')
then in some other file, I would like to do something like this, in pseudo code:
import * as string from './someFile.js'
console.log(string)
The output of the console.log should be the JS code, as a string:
alert('hey')
If I could place the file within my public folder, I'd be able to perform an http request and read it as I wish. But the problem is of course, that the file is part of the build process(inside the src folder)
Can this be done?
i can think about:
define constants.js file with following code:
export default "alert('vasia')";
import this file from some react file:
import vasia from "./constants";
const App = () => {
console.log(eval(vasia));
}
is that what you r searching for?
But, must warn you: "eval" is evil!
I wanted to do the very same thing but unfortunately found out it is not possible in pure JS as at 2022.
There's a stage 3 TC39 proposal for Import Assertions which is available in Chromium-based browsers but only allows to import JSON files so it certainly would not help in this particular case.
I believe your best bet for now is to use Fetch API to get the content of your file asynchronously.
async function getSampleText() {
const response = await fetch('someFile.js');
console.log(
await response.text()
);
}

Vite glob import - accessing body / payload in SvelteKit?

I'm using the glob import functionality of Vite to bring in multiple markdown files to a SvelteKit page:
<script context="module">
export async function load() {
const employmentsMeta =
import.meta.globEager('./employments/*.md');
return {
props: {
employmentsMeta: employmentsMeta
}
};
}
</script>
<script>
export let employmentsMeta;
</script>
This works well for me to access the metadata via employmentsMeta[Object.keys(employmentsMeta)[0]]['metadata']. I'm having difficulty accessing the actual contents of the markdown file, however - no matter how I attempt to access it, it seems to be coming back as undefined.
For example, console.log(employmentsMeta[Object.keys(employmentsMeta)[0]['default']]) returns undefined, despite my understanding that there's a default export object in there, and the metadata access working as intended.
How do I access the payload / body of the imported markdown?
I have a similar use-case, with markdown files in SvelteKit as blog posts.
In your example, I'd get the file contents as html like this:
const contents = employmentsMeta[Object.keys(employmentsMeta)[0]].default.render();
contents here is an object like this:
{
html: '/* a string with the markdown content parsed as html */',
css: { code: '', map: null },
head: '',
}
I guess you have to use mdsvex for the md->html conversion to happen.
EDIT: I should mention that I do this in an endpoint ([slug].json.js), return the results, and get them in the route, as doing it in the load function was throwing an error (...default.render is not a function) client-side.

Stencil custom page javascript not firing

I'm trying to create a custom web page in stencil website and trying to add custom javascript module.
This is the html file named '/templates/pages/custom/page/customz.html'
{{~inject 'template' template}}
<h2>Hello World!</h2>
<body>
Some custom content!
<body>
<script>window.__webpack_public_path__ = "{{cdn 'assets/dist/'}}";</script>
<script src="{{cdn 'assets/dist/theme-bundle.main.js'}}"></script>
<script>window.stencilBootstrap("{{page_type}}", {{jsContext}}).load();</script>
This is the javascript file named '/asset/js/theme/customz.js'
import PageManager from './page-manager';
export default class Customz extends PageManager {
onReady() {
console.log('onReady');
}
constructor(context) {
super(context);
console.log(context);
}
}
then i added this in app.js file
const customClasses = {
'pages/custom/page/customz': () => import('./theme/customz')
};
and also added it .stencil file to test it locally
I also created the web page in bigcommerce dashboard.
The problem i have is that the HTML is loaded but the Javascript file is not injected (i cannot see the console log strings in console or other js logic i insert).
Where can be the problem?
The place I usually start when troubleshooting a custom template is the related section on the BigCommerce Dev Center here: https://developer.bigcommerce.com/stencil-docs/storefront-customization/custom-templates#troubleshooting-template-authoring
If you've verified the version of the Stencil CLI and URL you're using, try using this same code with the base Cornerstone theme on the latest version.
you need add link for Windows too:
Look like:
const customClasses = {
'pages/custom/page/customz': () => import('./theme/customz'),
'pages\\custom\\page\\customz': () => import('./theme/customz')
};
And your custom page must contains:
`{{~inject 'template' template}}
<script>window.__webpack_public_path__ = "{{cdn 'assets/dist/'}}";</script>
<script src="{{cdn 'assets/dist/theme-bundle.main.js'}}"></script>
<script>window.stencilBootstrap("{{page_type}}", {{jsContext}}).load();</script>`
If don`t connection on base file from layout.

"document is not defined" in Nuxt.js

I am trying to use Choices.js within a Vue component. The component compiles successfully, but then an error is triggered:
[vue-router] Failed to resolve async component default:
ReferenceError: document is not defined
In the browser I see:
ReferenceError document is not defined
I think this has something to do with the SSR in Nuxt.js? I only need Choices.js to run on the client, because it's a client only aspect I guess.
nuxt.config.js
build: {
vendor: ['choices.js']
}
AppCountrySelect.vue
<script>
import Choices from 'choices.js'
export default {
name: 'CountrySelect',
created () {
console.log(this.$refs, Choices)
const choices = new Choices(this.$refs.select)
console.log(choices)
}
}
</script>
In classic Vue, this would work fine, so I'm very much still getting to grips with how I can get Nuxt.js to work this way.
Any ideas at all where I'm going wrong?
Thanks.
It's a common error when you start a Nuxt project ;-)
The Choices.js lib is available only for client-side! So Nuxt tried to renderer from server-side, but from Node.js window.document doesn't exist, then you have an error.
nb: window.document is only available from the browser renderer.
Since Nuxt 1.0.0 RC7, you can use <no-ssr> element to allow your component only for client-side.
<template>
<div>
<no-ssr placeholder="loading...">
<your-component>
</no-ssr>
</div>
</template>
take a look at the official example here: https://github.com/nuxt/nuxt.js/blob/dev/examples/no-ssr/pages/index.vue
Update:
Since Nuxt >= 2.9.0, you have to use the <client-only> element instead of <no-ssr>:
<template>
<div>
<client-only placeholder="loading...">
<your-component>
</client-only>
</div>
</template>
To know more, see nuxt docs: https://nuxtjs.org/docs/2.x/features/nuxt-components#the-client-only-component
The accepted answer (while correct) was too short for me to understand it and use it correctly, so I wrote a more detailed version. I was looking for a way to use plotly.js + nuxt.js, but it should be the same as the OP's problem of Choice.js + nuxt.js.
MyComponent.vue
<template>
<div>
<client-only>
<my-chart></my-chart>
</client-only>
</div>
</template>
<script>
export default {
components: {
// this different (webpack) import did the trick together with <no-ssr>:
'my-chart': () => import('#/components/MyChart.vue')
}
}
</script>
MyChart.vue
<template>
<div>
</div>
</template>
<script>
import Plotly from 'plotly.js/dist/plotly'
export default {
mounted () {
// exists only on client:
console.log(Plotly)
},
components: {
Plotly
}
}
</script>
Update: There is <client-only> tag instead of <<no-ssr> in Nuxt v>2.9.0, see #Kaz's comment.
You need to add it as a plugin and then disable SSR for it.
As the document and window are not defined on the server-side.
Your nuxt.config.js should look like below
plugins: [
{ src: '~/plugins/choices.js' } // both sides
{ src: '~/plugins/client-only.js', mode: 'client' }, // only on client side
{ src: '~/plugins/server-only.js', mode: 'server' } // only on server side
],
I found that now the no-ssr is replace by , i am using echart and have the same problem but now it´s working!
<client-only>
<chart-component></chart-component>
</client-only>
I had this error with lightgallery.js adding mode: 'client'
seems helped
nuxt.config.js
plugins: [
{ src: '~/plugins/lightgallery.js', mode: 'client' }
],
plugins/lightgallery.js
import Vue from 'vue'
import lightGallery from 'lightgallery.js/dist/js/lightgallery.min.js'
import 'lightgallery.js/dist/css/lightgallery.min.css'
Vue.use(lightGallery)
ImageGallery.vue
<template>
<section class="image-gallery-container">
<div class="image-gallery-row">
<div
ref="lightgallery"
class="image-gallery"
>
<a
v-for="image in group.images"
:key="image.mediaItemUrl"
:href="image.mediaItemUrl"
class="image-gallery__link"
>
<img
:src="image.sourceUrl"
:alt="image.altText"
class="image-gallery__image"
>
</a>
</div>
</div>
</section>
</template>
<script>
export default {
name: 'ImageGallery',
props: {
group: {
type: Object,
required: true
}
},
mounted() {
let vm = this;
if (this.group && vm.$refs.lightgallery !== 'undefined') {
window.lightGallery(this.$refs.lightgallery, {
cssEasing: 'cubic-bezier(0.680, -0.550, 0.265, 1.550)'
});
}
}
}
</script>
<script>
import Choices from 'choices.js'
export default {
name: 'CountrySelect',
created () {
if(process.client) {
console.log(this.$refs, Choices)
const choices = new Choices(this.$refs.select)
console.log(choices)
}
}
}
</script>
I guess this should help, nuxt will touch insides of computed after it renders on server and window will be defined
This thread is a bit old, but I will leave my solution here so maybe someone finds it useful.
I had similar issue with vue-star-rating and few other plugins recently.
Below steps can be followed and adjusted depending on the plugin name, import / usage settings:
Go to your plugins folder and create new js file, in this case vue-star-rating.js, then edit it to setup the plugin:
import Vue from 'vue'
import VueStarRating from 'vue-star-rating'
Vue.component('vue-star-rating', VueStarRating); //<--- the name you used to register the plugin will be the same to use when in the component (vue-star-rating)
Go to your nuxt.config.js file and add plugin:
plugins: [{
src: '~/plugins/vue-star-rating', // <--- file name
mode: 'client'
},
//you can simply keep adding plugins like this:
{
src: '~/plugins/vue-slider-component',
mode: 'client'
}]
Now you are ready to use the plugin anywhere in the application. However, to do that you will need to wrap it in the container <client-only>. Example:
<client-only placeholder="loading...">
<vue-star-rating />
</client-only>
Notes:
You do not need to import anything locally to the component, simply using it like above should fix the problem.
Please make sure you are naming the plugin the same way in both places, step 1 and step 3. In this case it would be vue-star-rating.
if you still want to do it, document object can be taken this way:
const d = typeof document === 'undefined' ? null : document
For completeness, it's worth mentioning that instead of the object syntax in Yusuf Adeyemo answer (which I prefer as it separates out the file from how it is used), you can also set plugins to operate in client or server side only by naming the files like so:
export default {
plugins: [
'~/plugins/foo.client.js', // only in client side
'~/plugins/bar.server.js', // only in server side
'~/plugins/baz.js' // both client & server
]
}
src: https://nuxtjs.org/docs/directory-structure/plugins/#client-or-server-side-only
On top of all the answers here, you can also face some other packages that are not compatible with SSR out of the box (like in your case) and that will require some hacks to work properly. Here is my answer in details.
The TLDR is that you'll sometimes need to:
use process.client
use the <client-only> tag (be careful, it will not render but still execute the code inside)
use a dynamic import if needed later on, like const Ace = await import('ace-builds/src-noconflict/ace')
load a component conditionally components: { [process.client && 'VueEditor']: () => import('vue2-editor') }
With all of this, you're pretty much covered for every possible case.
I was trying to access document in created hook so when I moved the logic from created hook to mounted hook, my problem was solved.

I am using Vue js and python flask as my backend. I want to have some local variable set. How can it be done?

My Vue js file. Here I am using two urls from localhost. I want to make a configuration file such that if I make changes in config file, I will be able to get the same changes.
<template>
<div>
<div class="global-buttons">
<button>See Previously Sent Mail</button>
<button #click="saveData()">Save Current Changes</button>
<button #click="loadData(savedUrl)">Load Previously Saved Data</button>
<button>Send Mail ✈</button>
</div>
</template>
In Python we can easily import from one file to another, but I dont know how to set these configs in Vue js. I have 3 components of Vue and at 6 different places I am using urls consisting of localhost. When I will host my project this all urls needs to be changed. and I have to go each lines to change . So I am searching for a config file where I can make changes and from there the urls will be imported.
I want like
config.js
URL = http://127.0.0.1:5000/
AND in my template
<a href="config.URL + sent_mail/book"
As I mentioned in comment - you can create some external object, something like this, let's call it config.js
export default {
config: {
url: 'myurl'
}
}
Then import it in your component file
import ConfigFile from '../config'
Then in your data you can use it like this
data() {
return {
url: ConfigFile.config.url
}
}
And then you can use url it in your template.
<p>{{ url }}</p>
Since you use flask as a backend, you can just include config.js as a script before you include the vue scripts
const URL = http://127.0.0.1:5000/
Inside your vue files, you can then have access to the URL variable

Categories