How to use vue component across multiple node projects? - vue.js

I'm trying to build a website builder within the drag-and-drop abilities via using Vue3. So, the user will be playing with the canvas and generate a config structure that going to post the backend. Furthermore, the server-side will generate static HTML according to this config.
Eventually, the config will be like the below and it works perfectly. The config only can have HTML tags and attributes currently. Backend uses h() function to generate dom tree.
My question is: can I use .vue component that will generate on the server side as well? For example, the client-side has a Container.vue file that includes some interactions, styles, etc. How can the backend recognize/resolve this vue file?
UPDATE:
Basically, I want to use the Vue component that exists on the Client side on the backend side to generate HTML strings same as exactly client side. (including styles, interactions etc).
Currently, I'm able to generate HTML string via the below config but want to extend/support Vue component itself.
Note: client and server are completely different projects. Currently, server takes config and runs createSSRApp, renderToString methods.
Here is the gist of how server would handle the API:
https://gist.github.com/yulafezmesi/162eafcf7f0dcb3cb83fb822568a6126
{
id: "1",
tagName: "main",
root: true,
type: "container",
properties: {
class: "h-full",
style: {
width: "800px",
transform: "translateZ(0)",
},
},
children: [
{
id: "9",
type: "image",
tagName: "figure",
interactive: true,
properties: {
class: "absolute w-28",
style: {
translate: "63px 132px",
},
},
},
],
}

This might get you started: https://vuejs.org/guide/scaling-up/ssr.html#rendering-an-app
From the docs:
// this runs in Node.js on the server.
import { createSSRApp } from 'vue'
// Vue's server-rendering API is exposed under `vue/server-renderer`.
import { renderToString } from 'vue/server-renderer'
const app = createSSRApp({
data: () => ({ count: 1 }),
template: `<button #click="count++">{{ count }}</button>`
})
renderToString(app).then((html) => {
console.log(html)
})
I guess extract the template from request or by reading the submitted Vue file and use that as the template parameter value

Related

Call context in nuxt not working in v-bind

I have question related to using context or prototype in Nuxt
I create a constant for 'modal' name like this:
export default Object.freeze({
MODAL_SHOWPRO: "MODAL_SHOWPRO",
})
I also created constant.js in plugin folder and already added to nuxt config.
import modals from '#/constants/modal';
export default ({ app }, inject) => {
inject('modalName', modals)
}
In component I can't call value from v-bind, it said : undefined MODAL_SHOWPRO
<Popup :id="$modalName.MODAL_SHOWPRO" />
but I can call it from $emit function something like this:
#click="$nuxt.$emit('showModal', {id: $modalName.MODAL_SHOWPRO})"
Can you let me know why and how to fix it?
Notice: It will work if:
I make data
{
modal: ''
}
and add to created:
async created() {
this.modalName = await this.$modalName
}
Nuxt is a meta-framework aimed at providing an universal app (server then client side). So, you need to think about both server and client.
In your code, you specified ssr: false, this is outdated and should rather be mode: 'client'. But setting so is still false because it means that the ENUM will not be available on the server (hence the error).
Setting it like this is more appropriate (regarding the nature of the plugin) and also fixes the issue
plugins: ['~/plugins/constant.js'],
More on Nuxt plugins: https://nuxtjs.org/docs/directory-structure/plugins#plugins-directory

How do I properly import multiple components dynamically and use them in Nuxt?

I need to implement dynamic pages in a Nuxt + CMS bundle.
I send the URL to the server and if such a page exists I receive the data.
The data contains a list of components that I need to use, the number of components can be different.
I need to dynamically import these components and use them on the page.
I don't fully understand how I can properly import these components and use them.
I know that I can use the global registration of components, but in this case I am interested in dynamic imports.
Here is a demo that describes the approximate logic of my application.
https://codesandbox.io/s/dank-water-zvwmu?file=%2Fpages%2F_.vue
Here is a github issue that may be useful for you: https://github.com/nuxt/components/issues/227#issuecomment-902013353
I've used something like this before
<nuxt-dynamic :name="icon"></nuxt-dynamic>
to load dynamic SVG depending of the icon prop thanks to dynamic.
Since now, it is baked-in you should be able to do
<component :is="componentId" />
but it looks like it is costly in terms of performance.
This is of course based on Nuxt components and auto-importing them.
Also, if you want to import those from anywhere you wish, you can follow my answer here.
I used this solution. I get all the necessary data in the asyncData hook and then import the components in the created () hook
https://codesandbox.io/s/codesandbox-nuxt-uidc7?file=/pages/index.vue
asyncData({ route, redirect }) {
const dataFromServer = [
{
path: "/about",
componentName: 'myComponent'
},
];
const componentData = dataFromServer.find(
(data) => data.path === route.path
);
return { componentData };
},
data() {
return {
selectedRouteData: null,
componentData: {},
importedComponents: []
};
},
created() {
this.importComponent();
},
methods: {
async importComponent() {
const comp = await import(`~/folder/${this.componentData.componentName}.vue`);
this.importedComponents.push(comp.default);
}

How do I translate Quasar component?

I have a Vue/Quasar application in which I use i18n but now I'm facing a problem: table footer doesn't get translated. In the table headers, for example, I do something like this to translate column names:
{
align: 'left',
field: (val) => val,
label: this.$t('locale.column'),
name: 'column',
required: true,
sortable: true,
},
where $t is 18n function, locale is my component and the column is actually's column's name. I don't have direct access to the footer of the table (where the pagination an the total number of the elements are) and it doesn't get translated. I also use Quasar language packs, quasar.js goes like this:
import en from 'quasar/lang/en-us.js'
/*
import other languages
*/
import {
state,
} from '#state/modules/i18n';
const locale = state.locale;
const langDictionary = {
// all the imported langs go here
};
Vue.use(Quasar, {
config: {},
components: { /* not needed if importStrategy is not 'manual' */ },
directives: { /* not needed if importStrategy is not 'manual' */ },
plugins: {
Cookies,
Dialog,
Loading,
Notify,
},
lang: langDictionary[locale]
});
export {langDictionary};
You are probably setting the language at startup correctly as lang: langDictionary[locale]. If you change the language state in your app later, you need to also inform Quasar if you want Quasar components and plugins to get correctly translated. See the docs, Change Quasar Language Pack at Runtime for how to achieve that, the docs about language pack also includes tips for dynamically loading the language pack.
You didn't specify how you change the language in your app, so I can't give an example built upon that.

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

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.

Implementing Emojis in a VueJs App

I am looking to setup emojis to my chat-app project.
I really liked the emojis in slack / tweeter and I would like to have something similar.
I found the following libraries: (if anyone can suggest a better library I would love to hear)
emojionearea
wdt-emoji-bundle
I am not sure how to load these libraries and use them in a VueJS app.
Could anyone please assist with how to load and use them?
I would like to mention I tried to use emoji-mart-vue
but not sure how to add the the component to the template at run time.
Thanks a lot
tldr; Checkout demo and code on JSFiddle
This answer is divided into two parts. First part deals with how to import all the stuff into the environment while the second part deals with how to use it Vue.
This solution uses EmojiOne as the emoji provider and EmojioneArea to provide emoji autocomplete behavior.
Part 1: Adding the libraries
There are three ways in which you can do this.
Using Global <script> tag (not recommended for anything serious): You can add the <script> tags for all the dependencies in order and simply start using the Global objects.
Add these <script> in order:
https://code.jquery.com/jquery-3.3.1.min.js
https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.min.js
https://cdn.jsdelivr.net/npm/emojione#3.1.6/lib/js/emojione.min.js
https://cdnjs.cloudflare.com/ajax/libs/jquery.textcomplete/1.8.4/jquery.textcomplete.min.js
https://cdnjs.cloudflare.com/ajax/libs/emojionearea/3.4.1/emojionearea.min.js
Using AMD modules (using require.js): You can use the following configuration to load these modules asynchronously using require.js
require.config({
paths: {
'jquery': 'https://code.jquery.com/jquery-3.3.1',
'Vue': 'https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue',
'emojione': 'https://cdn.jsdelivr.net/npm/emojione#3.1.6/lib/js/emojione',
'jquery.textcomplete': 'https://cdnjs.cloudflare.com/ajax/libs/jquery.textcomplete/1.8.4/jquery.textcomplete',
'emojionearea': 'https://cdnjs.cloudflare.com/ajax/libs/emojionearea/3.4.1/emojionearea'
},
shim: {
'emojione': {
'exports': 'emojione'
}
}
})
You can then require these modules in your entry point as
require(['jquery', 'Vue', 'emojione'], function($, Vue, emojione){ /* ... */ })
Using webpack: All the above libraries are available via npm and you install and use them just as you would do with any other library. No special config required.
BONUS: Using webpack with require.js and offloading network and caching loads to CDN!
I often use this setup for my projects and this improves reliability and performance of your site. You can use webpack.externals to instruct webpack to not bundle any vendor dependencies and instead you provide them yourself, either by manually adding <script> tags or using require.js.
Start by adding this to your webpack.<whatever>.js
//...
output: {
libraryTarget: 'umd' // export app as a library
},
//...
externals: {
'jquery': 'jquery',
'vue': 'Vue',
'emojione': 'emojione'
},
//...
Then, in your require.js entry, add this
//...
map: {
// any module requesting jquery should get shield
"*": {
"jquery": "jquery-shield"
},
// shield should get original jquery
"jquery-shield": {
"jquery": "jquery"
},
// patch plugins
"jquery.textcomplete": {
"jquery": "jquery"
},
"emojionearea": {
"jquery": "jquery"
}
}
//...
// define shield, require all the plugins here
define('jquery-shield', ['jquery', 'jquery-textcomplete', 'emojionearea'], function($){ return $ })
and then add require(...) your webpack bundle
Part 2: Using EmojiOne with Vue
As the OP mentioned his/her case is to use emojis in a chat app, I would also explain the solution with that case, though this can (and should) be modified for other use cases too!
The solution focuses on two aspects. First is to simply display the emoji in a message component, i.e., no need to display an emoji picker dialog and second is to display a emoji picker dialog whilst the user is typing into (say) a textarea.
To achieve the first goal, you can use a message component like,
Vue.component('message', {
props: ['content'],
render: function(h){
return h('div', {
class: { 'message': true }
}, [
h('div', {
class: { 'bubble': true },
domProps: {
innerHTML: emojione.toImage(this.content)
}
})
])
}
})
This would create a <message /> component which you can use as
<message content="Hey :hugging:!!" />
which will render
<div class="message">
<div class="bubble">
Hey <img class="emojione" alt="🤗" title=":hugging:" src="...">!!
</div>
</div>
Now, to create a <message-box /> component that will display an emoji picker to assist in autocomplete, do as follows
Vue.component('message-box', {
template: `<div class="message-box"><textarea></textarea></div>`,
mounted(){
// find the input
$(this.$el).find('textarea').emojioneArea()
}
})
And that's it! Although it may seem like a lot, the crux of the solution is quiet simple! Just use emojione.toImage(str) to get a DOM string and apply it to the Vue component (you can also use v-html to do this but IMO render() is a bit more sleek!). And for the displaying the picker, you just call $(...).emojioneArea() on the <textarea /> once the component is mounted.
Make sure to checkout full code example at https://jsfiddle.net/riyaz_ali/5Lhex13n