I am quite new to Vue.js and got stuck at a problem that shouldn't be too hard to solve: I have a single file component (.vue) that is supposed to view/manage a dynamic list of another single file component via JS.
My approach is this:
<script>
import Event from './DayView/Event'
export default {
components: {
Event
},
props: ['day']
}
const $ = window.$ = require('jquery')
$(document).ready(function () {
$('#day-view').append(new Event())
})
</script>
This results in the following error:
Uncaught TypeError: __WEBPACK_IMPORTED_MODULE_0__DayView_Event___default.a is not a constructor
Thanks in advance.
I found a solution for my problem (which isn't neccessarily mounting new objects). As DayView is supposed to view a list of Events, using a list of objects combined with v-for did the trick for me:
<template>
<div id="day-view">
[...]
<event v-for="event in events" :event="event"></event>
</div>
</template>
<script>
import Event from './DayView/Event'
let events = []
export default {
components: {
Event
},
data () {
return {
events: events
}
}
}
const $ = window.$ = require('jquery')
$(document).ready(function () {
events.push({start: '540', end: '630'})
})
</script>
See https://v2.vuejs.org/v2/api/
<template><div><event /></div></template>
<script>
import Event from './DayView/Event'
export default {
components: {
Event
},
props: ['day']
}
</script>
Related
Can someone help me out configuring a global function that I can call in all my Vue files?
When there is this in my Vue file:
#click="ModalShow.show('my-create')"
In the app.js I defined this constant:
const Modals = {
show(screen) {
alert(screen);
// other logic that i implement that should be triggered
},
};
But I keep getting:
TypeError: undefined is not an object (evaluating '_ctx.Modals.show')
What am I missing? It's a Vue project with the composition API
You can use provide/inject, first provide your function to your child components from the app (or parent component)
const Modal = {...}
const app = createApp({})
app.provide('Modal', Modal)
Then inject it into your component
import { inject } from 'vue'
export default {
setup() {
const Modal = inject('Modal')
return { Modal }
}
}
Or via script setup:
<script setup>
import { inject } from "vue";
const Modal = inject("Modal");
</script>
I'm trying to use createEventDispatcher to catch the child component's event from the parent's component but seems like doesn't work. If I remove the custom element, the dispatcher event works. Am I doing something wrong or svelte custom element doesn't support the dispatcher event?
child component Inner.svelte
<svelte:options tag="my-inner"/>
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function sayHello() {
dispatch('message', {
text: 'Hello!'
});
}
</script>
<button on:click={sayHello}>
Click to say hello
</button>
parent component App.svelte
<svelte:options tag="my-app" />
<script>
import {} from './Inner.svelte';
function handleMessage(event) {
alert(event.detail.text);
}
</script>
<my-inner on:message={handleMessage}></my-inner>
My rollup.config.js settings
plugins: [
svelte({
compilerOptions: {
customElement: true,
tag: null
},
}),
This is known issue. At least in 3.32 and before, events are not emitted from svelte component compiled into custom-element.
See https://github.com/sveltejs/svelte/issues/3119
This thread discuss about various workaround, but it depends of your usecase. The simple one seem to emit yourself an event :
import { createEventDispatcher } from 'svelte';
import { get_current_component } from 'svelte/internal';
const component = get_current_component();
const svelteDispatch = createEventDispatcher();
const dispatch = (name, detail) => {
svelteDispatch(name, detail);
component.dispatchEvent && component.dispatchEvent(new CustomEvent(name, { detail }));
// or use optional chaining (?.)
// component?.dispatchEvent(new CustomEvent(name, { detail }));
};
I'm building a project using Laravel and vue js and I needed to import a js package name ( photojshop ) ", which enable to edit an image filter or color " in my vue component.
I tried this way :
<template>
<img src=".." id="myImg" >
</templete>
<script>
import $ from 'jquery'
export default {enter code here
data: () => ({
......data of your component
}),
mounted() {
let recaptchaScript = document.createElement('script')
recaptchaScript.setAttribute('src', 'my local js file')
document.head.appendChild(recaptchaScript)
},
methods: {
EditImage(){
$('myImg').photoJshop({ .... }) (error)
}
}
}
</script>
but when I call the function $('..').photoJshop({}) an error occur function is not defined (photoJshop)
My problem is solved i just needed to write my code in
this.$nextTick(function () {
....
})
so that
I have the following parent component which has to render a list of dynamic children components:
<template>
<div>
<div v-for="(componentName, index) in supportedComponents" :key="index">
<component v-bind:is="componentName"></component>
</div>
</div>
</template>
<script>
const Component1 = () => import("/components/Component1.vue");
const Component2 = () => import("/components/Component2.vue");
export default {
name: "parentComponent",
components: {
Component1,
Component2
},
props: {
supportedComponents: {
type: Array,
required: true
}
}
};
</script>
The supportedComponents property is a list of component names which I want to render in the parent conponent.
In order to use the children components in the parent I have to import them and register them.
But the only way to do this is to hard code the import paths of the components:
const Component1 = () => import("/components/Component1.vue");
const Component2 = () => import("/components/Component2.vue");
And then register them like this:
components: {
Component1,
Component2
}
I want to keep my parentComponent as generic as possible. This means I have to find a way to avoid hard coded components paths on import statements and registering. I want to inject into the parentComponent what children components it should import and render.
Is this possible in Vue? If yes, then how?
You can load the components inside the created lifecycle and register them according to your array property:
<template>
<div>
<div v-for="(componentName, index) in supportedComponents" :key="index">
<component :is="componentName"></component>
</div>
</div>
</template>
<script>
export default {
name: "parentComponent",
components: {},
props: {
supportedComponents: {
type: Array,
required: true
}
},
created () {
for(let c=0; c<this.supportedComponents.length; c++) {
let componentName = this.supportedComponents[c];
this.$options.components[componentName] = () => import('./' + componentName + '.vue');
}
}
};
</script>
Works pretty well
Here's a working code, just make sure you have some string inside your dynamic import otherwise you'll get "module not found"
<component :is="current" />
export default { data () {
return {
componentToDisplay: null
}
},
computed: {
current () {
if (this.componentToDisplay) {
return () => import('#/components/notices/' + this.componentToDisplay)
}
return () => import('#/components/notices/LoadingNotice.vue')
}
},
mounted () {
this.componentToDisplay = 'Notice' + this.$route.query.id + '.vue'
}
}
Resolving dynamic webpack import() at runtime
You can dynamically set the path of your import() function to load different components depending on component state.
<template>
<component :is="myComponent" />
</template>
<script>
export default {
props: {
component: String,
},
data() {
return {
myComponent: '',
};
},
computed: {
loader() {
return () => import(`../components/${this.component}`);
},
},
created() {
this.loader().then(res => {
// components can be defined as a function that returns a promise;
this.myComponent = () => this.loader();
},
},
}
</script>
Note: JavaScript is compiled by your browser right before it runs. This has nothing to do with how webpack imports are resolved.
I think we need some plugin that can have code and every time it should load automatically. This solution is working for me.
import { App, defineAsyncComponent } from 'vue'
const componentList = ['Button', 'Card']
export const registerComponents = async (app: App): void => {
// import.meta.globEager('../components/Base/*.vue')
componentList.forEach(async (component) => {
const asyncComponent = defineAsyncComponent(
() => import(`../components/Base/${component}.vue`)
)
app.component(component, asyncComponent)
})
}
you can also try glob that also work pretty well but I have checked it for this solution but check this out worth reading
Dynamic import
[Update]
I tried same with import.meta.globEage and it works only issue its little bit lazy loaded you may feel it loading slow but isn't noticeable much.
import { App, defineAsyncComponent } from 'vue'
export const registerComponents = async (app: App): void => {
Object.keys(import.meta.globEager('../components/Base/*.vue')).forEach(
async (component) => {
const asyncComponent = defineAsyncComponent(
() => import(/* #vite-ignore */ component)
)
app.component(
(component && component.split('/').pop()?.split('.')[0]) || '',asyncComponent
)
})
}
I tried with props and PropsData but it is not working :/
import Vue from 'vue'
import Hello from '#/components/Hello.vue'
export function createComponent(selector, params) {
const test = {name: 'Test'};
new Vue({
el: selector,
render: app => app(selector, {
propsData: test
})
})
}
I can't find why this is not possible even with some console.log here and there, i'm not really confident with VueJS so is there a way to do that ?
Edit:
After some testings, props was correctly passed by what I wrote, It seems that I am not correctly passing the component name to the Vue instance:
Here is how my code works (or how i want it to works at least...):
Random php file:
<?php
$selector = "Hello";
$data = json_encode(['name' => 'from PHP'])
?>
<div id="<?= $selector; ?>-component"></div>
<script>
createComponent(<?= $selector; ?>, <?php return $data; ?>) // I tried "$selector" format, but doesn't work either.
</script>
My main js file
import Vue from 'vue'
import Hello from '#/components/Hello'
export function createComponent(selector, params) {
new Vue({
el: '#' + selector + '-component',
render: app => app(selector, { // If I replace selector by Hello, it works, but that's not what I want.
props: params
})
})
}
In this file I tried some tricks to call the component from 'selector' but the console tells me: ReferenceError: Hello is not defined
But as we can see Hello is clearly define at the top of the file.
My single file vue component:
<template>
<h1>
Hello {{ name }}!!
</h1>
</template>
<script>
module.exports = {
props: {
name: {
type: String,
default: 'World'
}
}
}
</script>
I simplified my php code to show you the case so there may have some errors, I haven't tried it.
The mistake must come from the syntax, or else I get it wrong.
PS: I hate JS syntax :)
import Vue from 'vue'
export function createComponent(selector, propsData) {
let instance = new Vue({
el: document.createElement('div'),
render(createElement) {
return createElement(
'h' + this.level, // tag name
this.$slots.default // array of children
)
},
propsData
})
document.body.querySelector(selector).appendChild(instance.$el);
}
Assuming that you want to create dynamically VueJS instance and append it to dom. Also propsData object you need to move out from render function. Render function is optional, you can always use template string...