How to embed html/js widgets in Nuxt, specifically iFlyChat but broadly applicable - vue.js

I have a couple of plugin widgets that I'd like to integrate into my site but I'm wondering how best to do it to preserve the Nuxt functionality like code-splitting, etc. For example, the code below is for iFlyChat. When I first used the code in my appHeader, it worked, then was intermittent for a while but now doesn't show up at all:
<script>
var iflychat_app_id="xyzappidcode";
var iflychat_external_cdn_host="cdn.iflychat.com",iflychat_bundle=document.createElement("SCRIPT");iflychat_bundle.src="//"+iflychat_external_cdn_host+"/js/iflychat-v2.min.js?app_id="+iflychat_app_id,iflychat_bundle.async="async",document.body.appendChild(iflychat_bundle);var iflychat_popup=document.createElement("DIV");iflychat_popup.className="iflychat-popup",document.body.appendChild(iflychat_popup);
</script>
I've since tried a similar widget for edwid but that didn't show up on the page at all.

You have to create a nuxt plugin to init your code on each page.
Fist, create a file plugins/iflychat.js:
export default () => {
console.log("init iflychat plugin");
var iflychat_app_id = "xyzappidcode";
var iflychat_external_cdn_host="cdn.iflychat.com",iflychat_bundle=document.createElement("SCRIPT");iflychat_bundle.src="//"+iflychat_external_cdn_host+"/js/iflychat-v2.min.js?app_id="+iflychat_app_id,iflychat_bundle.async="async",document.body.appendChild(iflychat_bundle);var iflychat_popup=document.createElement("DIV");iflychat_popup.className="iflychat-popup",document.body.appendChild(iflychat_popup);
}
Then, configure Nuxt.js to import it:
//nuxt.config.js
export default {
plugins: [
{ src: '~plugins/iflychat.js', mode: 'client' }
]
}
That's it, you iFlatChat will run on every page view.

Related

Vue2Editor not working in Nuxt app when accessing page directly

I'm using Vue2Editor in my Nuxt app on a single page. Whenever I test the app and navigate to the page from another page on the app, it loads fine without any issues. But when I test and try to open that page directly, the app fails with the following error. I've tried to dynamically import the vue-editor package but that hasn't worked so far. Any ideas how I can make this import work on the page when trying to directly access it?
nuxt.config.js
plugins: [{src: './plugins/vue2-editor', ssr: true}]
plugins/vue2-editor.js
import Vue from 'vue'
if (process.BROWSER_BUILD) {
const VueEditor = require('vue2-editor')
Vue.use(VueEditor)
}
my_page.vue
<template><div><vue-editor></vue-editor></div></template>
<script>
...
import { VueEditor } from 'vue2-editor';
components: {
VueEditor
}
...
</script>
Can you try it wrapping the component to be client side only?
Problem comes that when you access directly to the page you are Server Side Rendering and document doesn't exists on server.
<client-only><vue-editor/></client-only>
If not working try setting the plugin as ssr: false
plugins: [{src: './plugins/vue2-editor', ssr: false}]
You can import locally, on the client with the following
export default {
components: {
[process.browser && 'VueEditor']: () => import('vue2-editor'),
}
}
Rather than having it defined globally (especially if you use it only in a few pages).
Otherwise, wrapping it in between <client-only> tags could be a good idea too.
More detailed answer available here.

How to make a dynamic import in Nuxt?

In my nuxt component I want to use the ace editor:
import Ace from "ace-builds/src-noconflict/ace"
when the component is mounted I am doing the following:
this.editor = Ace.edit...
Obviously the window is not defined on the server on page reload. But unfortunately I just can't find a solution to fix this issue.
Is there a way to import a package on the mounted() hook?
I already tried
const Ace = require("ace-builds/src-noconflict/ace")
But that doesn't quite seem to work. Do you have any ideas to solve this issue?
I already tried to register a plugin plugins/ace.js:
import Vue from "vue"
import Ace from "ace-builds/src-noconflict/ace"
Vue.use(Ace)
registered it in nuxt.config.js:
plugins: [
{ src: "~/plugins/ace", mode: "client" }
],
But how do I use Ace in my component now? It is still undefined...
Since the error was thrown during the import statement, I'd recommended using dynamic imports as explained in my other answer here.
async mounted() {
if (process.client) {
const Ace = await import('ace-builds/src-noconflict/ace')
Ace.edit...
}
},
From the official documentation: https://nuxtjs.org/docs/2.x/internals-glossary/context
EDIT: I'm not sure about Ace and it's maybe a drastic change but you may also give a look to vue-monaco which is elbow-to-elbow popularity wise (vanilla Monaco editor).
EDIT2: mounted actually only runs on the client so you could strip the process.client conditional. Meanwhile, I do let it here in case you want to run some logic in other hooks like created (which are run on both server + client). More info here.
EDIT3: not directly related to the question, but some packages expose a component which is only available on the client-side (no SSR support), in those cases you could import the component only on the client side and easily prevent any other errors.
Nuxt Plugin
IMHO you were on the right track with the "plugin" solution. Only mistake was the
Vue.use(Ace) part. This only works for vue plugins.
The plugin file could look somewhat like that:
import Ace from 'ace-builds/src-noconflict/ace'
import Theme from 'ace-builds/src-noconflict/theme-monokai'
export default ({ app }, inject) => {
inject('ace', {
editor: Ace,
theme: Theme
})
}
Then you could use this plugin and initiate the editor in a component this way:
<template>
<div id="editor">
function foo(items) {
var x = "All this is syntax highlighted";
return x;
}
</div>
</template>
<script>
export default {
data () {
return {
editor: {}
}
},
mounted () {
this.editor = this.$ace.editor.edit('editor')
this.editor.setTheme(this.$ace.theme)
}
}
</script>

“window is not defined” in Nuxt.js

I get an error porting from Vue.js to Nuxt.js.
I am trying to use vue-session in node_modules. It compiles successfully, but in the browser I see the error:
ReferenceError window is not defined
node_modules\vue-session\index.js:
VueSession.install = function(Vue, options) {
if (options && 'persist' in options && options.persist) STORAGE = window.localStorage;
else STORAGE = window.sessionStorage;
Vue.prototype.$session = {
flash: {
parent: function() {
return Vue.prototype.$session;
},
so, I followed this documentation:
rewardadd.vue:
import VueSession from 'vue-session';
Vue.use(VueSession);
if (process.client) {
require('vue-session');
}
nuxt.config.js:
build: {
vendor: ['vue-session'],
But I still cannot solve this problem.
UPDATED AUGUST 2021
The Window is not defined error results from nodejs server side scripts not recognising the window object which is native to browsers only.
As of nuxt v2.4 you don't need to add the process.client or process.browser object.
Typically your nuxt plugin directory is structured as below:
~/plugins/myplugin.js
import Vue from 'vue';
// your imported custom plugin or in this scenario the 'vue-session' plugin
import VueSession from 'vue-session';
Vue.use(VueSession);
And then in your nuxt.config.js you can now add plugins to your project using the two methods below:
METHOD 1:
Add the mode property with the value 'client' to your plugin
plugins: [
{ src: '~/plugins/myplugin.js', mode: 'client' }
]
METHOD 2: (Simpler in my opinion)
Rename your plugin with the extension .client.js and then add it to your plugins in the nuxt.config.js plugins. Nuxt 2.4.x will recognize the plugin extension as to be rendered on the server side .server.js or the client side .client.js depending on the extension used.
NOTE: Adding the file without either the .client.js or .server.js extensions will render the plugin on both the client side and the server side. Read more here.
plugins: ['~/plugins/myplugin.client.js']
There is no window object on the server side rendering side. But the quick fix is to check process.browser.
created(){
if (process.browser){
console.log(window.innerWidth, window.innerHeight);
}
}
This is a little bit sloppy but it works. Here's a good writeup about how to use plugins to do it better.
Its all covered in nuxt docs and in faq. First you need to make it a plugin. Second you need to make your plugin client side only
plugins: [
{ src: '~/plugins/vue-notifications', mode: 'client' }
]
Also vendor is not used in nuxt 2.x and your process.client not needed if its in plugin with ssr false
In Nuxt 3 you use process.client like so:
if (process.client) {
alert(window);
}
If you've tried most of the answers here and it isn't working for you, check this out, I also had the same problem when using Paystack, a payment package. I will use the OP's instances
Create a plugin with .client.js as extension so that it can be rendered on client side only. So in plugins folder,
create a file 'vue-session.client.js' which is the plugin and put in the code below
import Vue from 'vue'
import VueSession from 'vue-session'
//depending on what you need it for
Vue.use(VueSession)
// I needed mine as a component so I did something like this
Vue.component('vue-session', VueSession)
so in nuxt.config.js, Register the plugin depending on your plugin path
plugins:[
...
{ src: '~/plugins/vue-session.client.js'},
...
]
In index.vue or whatever page you want to use the package... import the package on mounted so it is available when the client page mounts...
export default {
...
mounted() {
if (process.client) {
const VueSession = () => import('vue-session')
}
}
...
}
You can check if you're running with client side or with the browser. window is not defined from the SSR
const isClientSide: boolean = typeof window !== 'undefined'
Lazy loading worked for me. Lazy loading a component in Vue is as easy as importing the component using dynamic import wrapped in a function. We can lazy load the StepProgress component as follows:
export default {
components: {
StepProgress: () => import('vue-step-progress')
}
};
On top of all the answers here, you can also face some other packages that are not compatible with SSR out of the box 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
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') }
For me it was the case of using apex-charts in Nuxt, so I had to add ssr: false to nuxt.config.js.

How can I add vue-router link as ag-grid-vue column?

ag-grid-vue documentation from ag-grid website clearly says:
You can provide Vue Router links within the Grid, but you need to
ensure that you provide a Router to the Grid Component being created.
with sample code:
// create a new VueRouter, or make the "root" Router available
import VueRouter from "vue-router";
const router = new VueRouter();
// pass a valid Router object to the Vue grid components to be used within the grid
components: {
'ag-grid-vue': AgGridVue,
'link-component': {
router,
template: '<router-link to="/master-detail">Jump to Master/Detail</router-link>'
}
},
// You can now use Vue Router links within you Vue Components within the Grid
{
headerName: "Link Example",
cellRendererFramework: 'link-component',
width: 200
}
What's missing here is how to make the "root" Router available. I've been looking into various sources and see many people have the same problem, but none got a clear answer.
https://github.com/ag-grid/ag-grid-vue/issues/1
https://github.com/ag-grid/ag-grid-vue/issues/23
https://github.com/ag-grid/ag-grid-vue-example/issues/3
https://forum.vuejs.org/t/vue-cant-find-a-simple-inline-component-in-ag-grid-vue/21788/10
Does ag-grid-vue still work with vue-router, then how, or is this just outdated documentation? Some people claim it worked for them so I assume it worked at one point.
I am not looking for cool answer at this point. I just want to know if it is possible. I tried passing router using window or created() and none worked so far.
Thank you!
the approach suggested by #thirtydot works well. The only downside was the user cannot right-click, but I found you can just define href link. So when you left-click, event listener makes use of router. When you right-click and open in new tab, browser takes href link.
You still need to make your root router available. Below code sample assumes you have the code inside the vue-router-aware Vue component that consumes ag-grid, hence this.$router points to the root router.
{
headerName: 'ID',
field: 'id',
cellRenderer: (params) => {
const route = {
name: "route-name",
params: { id: params.value }
};
const link = document.createElement("a");
link.href = this.$router.resolve(route).href;
link.innerText = params.value;
link.addEventListener("click", e => {
e.preventDefault();
this.$router.push(route);
});
return link;
}
}

Nuxt - define a const once and which can use across all pages

I'm trying to implement Shopify JS SDK in Nuxt
So this is what I did, a plugin
// plugins/shopify.js
import Vue from 'vue'
import 'isomorphic-fetch'
import Shopify from 'shopify-buy'
export default ({ app }, inject) => {
app.shopify = Shopify.buildClient({
domain: 'aaa.myshopify.com',
storefrontAccessToken: 'aaa'
});
}
nuxt config
//nuxt.config.js
plugins : [{ src : '~/plugins/shopify', ssr: false}]
vendor : ['shopify-buy']
index
asyncData ({ app }) {
return app.shopify.product.fetchAll().then((products) => {
// Do something with the products
console.log(products);
return { products : products }
});
}
The result is
TypeError Cannot read property 'product' of undefined
But it works if I removed the asyncData, refresh my page, and add the code back without refreshing.
I believe this has something to do with the lifecycle.
Can anyone please tell me if I'm doing it the right way, or there's other proper way to define such const which can be use across pages, components etc
And if this is the right way, what I did wrong?
Thanks in advance.
My reference are Nuxt guides as well as examples.
I tried google around but can't locate what I need, or maybe I just didn't get the right keywords.
FROM DOCUMENTATION
Nuxt.js lets you create environment variables that will be shared for
the client and server-side.
To do this, you can use the env property:
nuxt.config.js:
module.exports = {
env: {
baseUrl: process.env.BASE_URL || 'http://localhost:3000'
}
}
Then to access it from anywhere, just use it like so:
process.env.baseEnv
For example, in an axios plugin:
import axios from 'axios'
export default axios.create({
baseURL: process.env.baseUrl
})