Vue dynamic components - watch for mounted - vue.js

I'm using Webpack dynamic imports and Vue dynamic components to lazy-load a rather large Vue markdown-parsing component.
Now, I want to add syntax highlighting with Prism.js. I'm currently using the mounted() lifecycle hook of the parent component to install syntax highlighting, but this is only working some of the time, since the syntax highlighting depends on the Markdown component to be loaded first (when I manually execute Prism.highlightAll() from the console after page load, it works every time).
Relevant source code:
<template>
<vue-markdown>
# Hello
```javascript
import { hello } from "world"
```
</vue-markdown>
</template>
<script>
export default {
components: {
"vue-markdown": () => import("vue-markdown/src/VueMarkdown"),
},
mounted() {
import("prismjs/themes/prism-tomorrow.css")
.then(() => import("prismjs").then(p => Prism.highlightAll()))
}
}
</script>
So how do I wait for a dynamic component to load? I almost want something like this:
<vue-markdown v-on:mounted="syntaxHighlighting()"></vue-markdown>

I solved the problem by creating my own component which extends the VueMarkdown component, but with a mounted() hook that activates syntax highlighting. It looks like this:
<script>
import VueMarkdown from "vue-markdown/src/VueMarkdown"
import "prismjs/themes/prism-tomorrow.css"
import Prism from "prismjs"
export default {
extends: VueMarkdown,
mounted() {
Prism.highlightAll()
}
}
</script>
Then, I dynamically import this component into the parent component.
Not sure if this is the best solution, though...

Related

Vue3: Is it possible to register single file components using app.component(...)?

Diving into vue 3, trying to add Vue to an existing asp.net core project. Since the frontend is mostly razor pages, the app isn't being mounted with a templated component that has a hierarchy of components.
const vueApp = createApp({});
What I'm trying to do:
vueApp.component('MyComponent', require('./components/MyComponent').default);
vueApp.mount('#app');
I've also tried it this way, as described in the docs:
import { createApp } from 'vue/dist/vue.esm-browser'
import MyComponent from './components/MyComponent.vue'
const vueApp = createApp({
components: {
MyComponent
}
});
vueApp.mount('#app');
I've tried every version of this. requiring MyComponent.vue, with and without the default, importing MyComponent and using it that way (instead of require), none of them work. I just continue getting [Vue warn]: Failed to resolve component 'mycomponent' (Yes I did check the html coming back from the server, It's properly capitalized...not sure why the error is lower case).
MyComponent.vue looks like this:
<template>
<lots of vanilla html>
</template>
<script>
export default {
name: 'MyComponent',
data() {
return { some: "data" }
},
methods: { ... },
mounted() { ...}
}
</script>
//no component styling
Am I missing something here? Or is this no longer possible? I'm using the default vue-cli webpack config, if that matters.
Thanks
So, after rereading the docs (for what feels like the 10th time), I think I figured out the problem. It's actually not a Vue issue at all, it's my use of the Vue component.
In my asp.net core cshtml, I was referencing the component in PascalCase, like this:
<MyComponent />
Turns out this is a no no. By convention (enforced by the browser I guess), custom elements can only be referenced in the DOM using kebab-case, like this:
<my-component />
My vue app is still defining the component in PascalCase.
My main.js file is importing MyComponent, then passing it into the createApp options.components object.
const vueApp = createApp({
components: {
MyComponent
}
});
The more you know, I guess.

Vue: lazy loading from library (Bootstrap-Vue) inside a component

I am exploring the lazy-loading feature and I'm trying to use it for Bootstrap-Vue component, but it does not work.
If I import the b-card "normally", it gets renderred correctly:
import { BCard } from 'bootstrap-vue';
export default {
components: {
BCard
}
};
But when I'm attempting the 'lazy-load' syntax it does not work:
export default {
components: {
BCard: () => import('bootstrap-vue').BCard
}
};
The b-card component is not renderred, but no error is thrown and in Chrome's DOM inspection tools I can see that the placeholder <!----> is placed by Vue where the b-card component should be. I suspect that the library object that is loaded does not have the BCard property, but I don't know how else to access the library component with the 'lazy' syntax.
Is it possible to lazy-load a module from a library? How to do it?
When dynamically importing a module, you use the import keyword as a function and it returns a promise. So, to access the module component, you can use this syntax:
export default {
components: {
BCard: () => import('bootstrap-vue').then(module => module.BCard)
}
}

TypeError: _vm.moment is not a function in Vuejs

I have a problem migrating to moment on Vuejs.
After running npm install vue-moment and adding to my script:
<script>
const moment = require('vue-moment');
...
</script>
I added this into my <template> :
<h1>{{moment('2017-12-20 11:00').fromNow()}}</h1>
And I get this error:
[Vue warn]: Error in render: "TypeError: _vm.moment is not a function"
You can use it globally as #red-X said, but you can add it only on your component:
import moment from 'moment'
export default {
data: () => ({
moment: moment
})
}
And then you can access it in your HTML template.
But i recommand you to use computed vars for using this kind of code, and to not have logic in your html template, just render computed vars inside your templates for readability.
And with this solution, you don't need to have moment library available globally or in your component, just the import.
Here it's an example :
import moment from 'moment'
export default {
computed: {
distanceFromNow() {
return moment('2017-12-20 11:00').fromNow()
}
}
}
And in your template :
<template>
<div>
{{ distanceFromNow }}
</div>
</template>
import * as momentTemp from 'moment';
const moment = momentTemp["default"];
Did you add the 'moment' attribute to the lifecycle 'methods',forExample:
methods:{
moment,
function a(){},
}
Did you add moment to the global Vue object like this:
const moment = require('vue-moment');
Vue.use(moment)
Just adding it to the local scope of a component will not make it available for use in the template. Everything referenced in the template is taken from the component itself.

Import multiple lazy-loaded vue component children into Parent

I have many components that use the same children components. I am trying to save myself some time and code by importing all of the components in a .js file, Similar to using mixins in vue. and then import that file into the parent component. Unfortunately the parent component does not recognize these imported components. Seems like a simple ask but having trouble implementing it.
When I log Children in the parent I get a components object with the two vue components I am just not sure how to utilize it in the parent component. I would import them globally however not every component need them so it wouldn't be very efficient.
I also feel like I am importing Components twice into the parent but again am unsure of how to accomplish this so though I would post what I have so far.
thanks for your help
**Children**
export default {
components: {
Popover: () => import('#/components/inline-components/popover'),
Button: () => import('#/components/inline-components/button')
}
}
**Parent**
<template>
<Button>I am the Button</Button>
</template>
import Children from 'utilities/children'
export default {
components: {
Children
}
}
Posting the answer from Husam Ibrahim comment.
**Children**
export default {
components: {
Popover: () => import('#/components/inline-components/popover'),
Button: () => import('#/components/inline-components/button')
}
}
**Parent**
<template>
<Button>I am the Button</Button>
</template>
import Children from 'utilities/children'
export default {
// Add them as mixins instead of components
mixins: [Children]
}

How to create a reusable component in VueJs?

I would like to create Side Panel as a reusable component in Framework7 with VueJS. Here is my code..
Card.vue
<f7-card>
<f7-card-header>Card header content</f7-card-header>
<f7-card-content><img src="https://wiki.videolan.org/images/Ubuntu-logo.png"></img></f7-card-content>
<f7-card-footer>Card footer content</f7-card-footer>
</f7-card>
Now i registered as a component like below,
import Vue from 'vue'
export default [
{
path: '/card/',
component: require('./Card')
},
]
In later vues i imported as,
import Card from './Card.vue'
and i try to access in another vues like,
Now i'm getting an error like
[Vue warn]: Unknown custom element: - did you register the
component correctly? For recursive components, make sure to provide
the "name" option.
Can anyone help me where have i mistaken?
Thanks,
It's not really clear from your code exactly what you are doing, but the error you are getting happens when you try to use a component as a child of another component without registering it in the parent's components setting like this:
<script>
import Card from './Card.vue'
export default {
data () {
return {
somedata: 'some value'
}
},
components: {Card: Card}, // <- you're missing this
// etc...
}
</script>
You can also register components globally. More here: https://v2.vuejs.org/v2/guide/components.html#Local-Registration
Are you showing us all of Card.vue? For a valid single-file vue component, I would expect to see <template>, <script> and <style> elements. The render function will be generated from whatever you put in the <template> element.
Make sure that the component that you want to reuse is wrapped inside a template tag
As follows
<template>
<div>
<component data/>
<div/>
<template/>
Then register it inside the parent
Like so
export default {
name: "Card",
components: {
Card
},
};