problem with Javascript dynamic module import containing variables - javascript

Okay, so I've been bumping my head on the walls trying to figure out what the hell's going on here. See, I've been trying to load a module dynamically in Vue.js. What confuses me here is that the following actually works (i.e. when I hardcode the path to my module):
currentModal(){
return () => System.import(`../../ride/modals/boarding.vue`);
},
However, if I do this:
currentModal(){
let path = "../../ride/modals/";
return () => System.import(`${path}boarding.vue`);
},
I get the following error:
Cannot find module '../../ride/modals/boarding.vue'.
Then, if I go the other way around and do:
currentModal(){
let content = "boarding.vue";
return () => System.import(`../../ride/modals/${content}`);
},
The error becomes:
Error in render: "TypeError: undefined is not a function"
Obviously, something's different when I use a variable instead of hardcoding the path... but for the life of me, I can't figure out what. Anyone has any clue?

As Bergur mentioned in his comment, the problem is indeed that webpack cannot resolve a string that doesn't yet exist (issue explained here). What I did instead is add a property to my component that I called "content-list" and in the main page, I fill it with my desired components like so:
parent component:
<template>
.....
<modal-window
:init-visible="modalVisible"
:content-list="contentList"
#modalClosed="modalClosed">
</modal-window>
.....
</template>
<script>
import mainContent from './modals/main.vue'; //I doubt this is still useful
import otherContent from './modals/other.vue'; //I doubt this is still useful
export default{
data(){
return {
modalVisible: false,
contentList: {
main: () => System.import("./modals/main.vue"),
other: () => System.import("./modals/other.vue")
},
}
},
}
</script>
Inside the modal component:
<template>
...
<div ref="scrolling-pane" class="scrolling-pane" :class="{ 'scrolling-pane-normal': !isDirty, 'scrolling-pane-dirty': isDirty }" key="b">
<transition name="content-slide"
:enter-active-class="enterClassName"
:leave-active-class="leaveClassName"
#before-enter="beforeEnter"
#before-leave="beforeLeave"
#after-enter="afterEnter"
#after-leave="afterLeave">
<keep-alive>
<component :is="currentModal" #switchContent="onSwitchContent"></component>
</keep-alive>
</transition>
</div>
...
</template>
<script>
export default{
components: {
},
props: {
initVisible: Boolean,
contentList: Object
},
data(){
return {
currentContent: this.contentList['main']
}
}
...
</script>

Related

Why am I getting "$attrs is readonly" and "$listeners is readonly" errors in the child component of the external library?

Why am I getting "$attrs is readonly" and "$listeners is readonly" errors in the child component of the external library every time something is updated in the parent component inside the Nuxt application?
This is how the whole bunch of errors looks like, caused by one problem:
[Vue warn]: $attrs is readonly.
found in
---> <MyComponent>
// ...
[Vue warn]: $listeners is readonly.
found in
---> <MyComponent>
// ...
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "data"
found in
---> <MyComponent>
// ...
About the mutation of the props. I don’t do it! How can I update props from MyComponent while in the parent component? MyComponent is a library that is imported into the parent component. It is not possible to update props inside MyComponent, I only pass props to MyComponent...
Sometimes the prop mutation error occurs only once during the initial page load.
I have an external library. Its essence is to provide access to its internal component. This is how main.ts looks like:
import MainComponent from './main.vue';
export default MainComponent;
The main.vue file is a regular vue component wrapped in Vue.extend for TypeScript.
I have reduced all the code as much as possible in order to make it as easy as possible to understand the problem:
<template>
<div>test</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
props: {
data: {
type: Object,
required: true,
},
},
data(): { sourceData: Record<string, any> } {
return {
sourceData: {},
};
},
watch: {
sourceData: {
handler(newData: Record<string, any>): void {
this.$emit('update', newData);
},
deep: true,
},
},
created(): void {
this.sourceData = { ...this.data };
},
});
</script>
Problems in this piece of code:
watch: {
sourceData: {
handler(newData: Record<string, any>): void {
this.$emit('update', newData);
},
deep: true,
},
},
On the Nuxt side of the application, it looks like this:
<MyComponent
#update="updateData($event)"
:data="sourceData"
/>
export default Vue.extend({
layout: 'profile',
data (): Record<string, any> {
return {
sourceData: {
// some source data
},
updatedSourceData: {},
}
},
methods: {
updateData(newSourceData: Record<string, any>): void {
this.updatedSourceData = { ...newSourceData }
}
},
// ...
In other words, I can say that this code is the problem:
this.updatedSourceData = { ...newSourceData }
I want to put new data in a separate object to render it (for example, in pre code). But this is what causes the error.
I have been trying to solve this problem for several hours, tried everything I could find and understand. Help me please.
UPD
I rewrote the component using a different approach. Also I gave up on building the library. Now I just import the component file from the package.
This is how the component code looks now:
<template>
<div>
{{ syncedData }}
</div>
</template>
<script lang="ts">
import { Component, PropSync, Vue } from 'vue-property-decorator';
#Component
export default class MyComponent extends Vue {
#PropSync('data', { type: Object }) syncedData!: Record<string, unknown>;
}
</script>
Inside the application, I use it like this:
<MyComponent
:data.sync="data"
/>
The problem is identical, it does not change throughout all my changes. Whatever I do with this component inside the package (library) - nothing helps.
And the craziest thing:
import MyComponent from '/Users/Colibri/projects/my-lib/src/components/my_component.vue'
That is, it's not even a library already, I just import it from another directory. I’m going crazy because I don’t understand why this is happening.
If I copy this component into a Nuxt project, then the errors disappear. The problem is somewhere here, but I absolutely do not understand it.

v-runtime-template vuejs cannot access root property

This is my page.vue
<template>
<div>
<v-runtime-template :template="template"></v-runtime-template>
</div>
</template>
<script>
import VRuntimeTemplate from "v-runtime-template";
export default {
data: () => ({
template: `
<span>{{sample}}</span>
`
}),
components: {
VRuntimeTemplate
},
computed:{
sample(){ return "sampledata" }
}
};
My package.json:
"vue": "^2.6.11",
"v-runtime-template": "^1.10.0"
Problem : I got this error on console:
vue.esm.js?a026:628 [Vue warn]: Property or method "sample" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property..
I have read some github issues as like this told me similar problem.
Maybe someone can help me how to resolve this of my simple implementation of using this v-runtime-template library? Many thanks.
I don't know how this trick can solve my problem, but this really save my life.
Original source
by H.Eden
So, I solve my problem by creating new vue file as v-runtime-template pivot.
RuntimePivot:vue:
<template>
<v-runtime-template :template="`<div>${template}</div>`"></v-runtime-template>
</template>
<script>
import VRuntimeTemplate from "v-runtime-template";
export default {
props:['template','props','listener'],
components:{
VRuntimeTemplate
},
name:"RuntimePivot",
data(){
return {
myname:'everything'
}
},
}
</script>
Then I import the runtimePivot.vue template to my Page.vue
<template>
<div>
<RuntimePivot :template="template" :props="properties" />
</div>
</template>
<script>
import RuntimePivot from "./RuntimePivot";
export default {
components:{
RuntimePivot
},
name:"Page",
data(){
return {
template:`<span>{{key}}</span>`,
properties:{key:"value"}
}
},
}
</script>
Better solution is still opened to you guys.

Expected T, got Object in Vue-Prop of custom Object-Type

Description
Hello there,
I would like to share my Vue-components using bit.dev.
I got a Vue-component like this:
<template>
...
</template>
<script>
import CustomItem from "../../../../objects/CustomItem";
export default {
name: "Test",
props: {
item: {
type: CustomItem,
},
},
};
</script>
As you can see, this component requires the prop to be a specific object.
This is the CustomObject
export default class CustomItem {
constructor ({id, name}) {
this.id = id;
this.name = name;
}
// provide cool functions here
}
This works fine in my project, but not if I include this this way:
<template>
<div v-if="!$wait.is('item.loading')">
<MyComponent :item="item"/>
</div>
</template>
<script>
import MyComponent from '#bit/myproject.my-component'
import CustomItem from '#bit/myproject.custom-item';
export default {
name: 'Home',
components: {MyComponent},
data () {
return {
item: {}
};
},
beforeRouteEnter (to, _from, next) {
const promises = [
axios.get (`/api/item/1`)
];
next (vm => {
vm.$wait.start ('item.loading');
axios.all (promises)
.then (([itemRes]) => {
vm.item = new CustomItem(itemRes.data.data);
}).finally(()=>{
vm.$wait.end ('item.loading');
});
});
},
};
</script>
In this case I get this error:
[Vue warn]: Invalid prop: type check failed for prop "item". Expected T, got Object
found in
---> <MyComponent> at resources/js/components/Items/MyComponent.vue
What did I miss here?
Edit
As I can see, the component #bit/myproject.my-component which has been provided by bit.dev, provides a packed and minified version of my component. In there, the prop looks like this:
props:{item:{type:function t(e){var n=e.id,r=e.name})...
So I guess this is why this happens.
Basically it seems that in your project actually there are 2 classes CustomItem:
the one you imported relatively:
import CustomItem from "../../../../objects/CustomItem";
and the one you imported as an external package:
import CustomItem from '#bit/myproject.custom-item';
So I would try first to check if it works when you unify your imports to one form:
import CustomItem from '#bit/myproject.custom-item';
But is JS world things are not simple sometimes and even this may not help you - sometimes even referring to CustomItem in this way does not guarantee that there won't be more than one CustomItem in your production codebase. The solution I would suggest is to enforce 'duck-typing' in a custom props validator, if it's really important to check the type of the prop. You still cannot use JS instanceof as it won't work, even checking item.prototype.name === 'CustomItem' is not a good idea, as class names are changed during code minimalization, so duck-typing seems to be the only reasonable solution for you.

problem with using vue.js routing and mixin

Vue developers.
Now I'm trying vuebnb tutorial and have a problem with routing and mixins.
I tried to assign data before entering the router using beforeRouteEnter guard, but it seems like my template is rendered before data assign.
Following is the code I tried.
ListingPage.vue
<template>
<div>
<img :src="'/' + images[1].img_path" />
</div>
</template>
<script>
import { populateAmenitiesAndPrices } from "../js/helpers";
import routeMixin from '../js/route-mixin';
export default {
mixins: [ routeMixin ],
data() {
return {
title: null,
about: null,
address: null,
amenities: [],
prices: [],
images:[],
}
},
methods: {
assignData({ listing, images }) {
console.log('inside_component_before_assign');
this.images = [...images];
Object.assign(this.$data, populateAmenitiesAndPrices(listing));
console.log('inside_component_after_assign');
}
},
components: {
}
};
</script>
route-mixin.js
import axios from 'axios';
function getData(to) {
return new Promise((resolve) => {
let serverData = JSON.parse(window.vuebnb_data);
if (!serverData.path || to.path !== serverData.path) {
axios.get(`/api${to.path}`).then(({ data }) => {
resolve(data);
});
} else {
resolve(serverData);
}
});
}
export default {
beforeRouteEnter: (to, from, next) => {
console.log('before_next');
getData(to).then((data) => {
next(component => {
component.assignData(data);
});
});
console.log('after_next');
}
};
Here data is a object and it is like {listing: {...}, images: Array(5), path: "/listing/1}.
Data fetch from the server is checked.
But when I try to render ListingPage.vue, error is logged in console like this:
*
[Vue warn]: Error in render: "TypeError: Cannot read property 'img_path' of undefined"
found in
---> <ListingPage> at resources/assets/components/ListingPage.vue
<App> at resources/assets/components/App.vue
<Root>
Regardless of the error, the page is displayed successfully. Please help me to get rid of this error:
From the reply on your comment I conclude your problem is solved by adding a v-if to your html tag?
Changing your code to
<img v-if="images[1]" :src="'/' + images[1].img_path" />
should work, as the async. request is probably not resolved by the time, the page is compiled by vue. For that have a look at the vue component lifecycle.
As the data attributes are reactive, your component will be visible and the value within will be updated couple ms later than it is compiled.
I'd be glad if somebody could suggest another approach as do not if this is the best practice myself.

Vuejs -- Computed property not being defined on the instance

I'm fairly new to VueJS and I am having difficulties getting a child component to work properly.
So first off, I had some code in a "view" that I realized I wanted to use more than once, so I began to factor that code out into a separate component, but I ran into this problem:
[Vue warn]: Property or method "<feedbackCallback|stateCallback|submitCallback>" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
My setup is the following:
Vue 2.4.2
Webpack 3.5.5
Bootstrap-Vue 1.0.0
Vue-Router 2.7.0
I'm also using Single File Components
I'm going to call the file for the "view" ViewA and the file for the "component" "CompA"
ViewA with the parts removed that weren't going into a separate component:
<template>
[...]
<b-form #submit="submitCallback">
<b-form-group
id="groupID"
description=""
label="Set Thing Here" :feedback="feedbackCallback"
:state="stateCallback">
<b-form-input
id="inputID" :state="stateCallback"
v-model.trim="thing">
</b-form-input>
</b-form/group>
<b-button type="submit" variant="primary">Submit</b-button>
</b-form>
[...]
</template>
<script>
export default {
[...]
data () {
return {
thing: '',
[...]
}
},
computed: {
stateCallback () {
return 'invalid'
},
feedbackCallback () {
return 'Please enter a valid thing'
}
},
methods: {
submitCallback (event) {
[...]
}
},
[...]
</script>
Now, I moved these guys into CompA.
This is the code now where I'm getting the error:
ViewA:
<template>
[...]
<comp-a v-model.trim="thing" :thingvalue="thing"></comp-a>
[...]
</template>
<script>
import CompA from '../components/CompA'
export default {
name: 'view-a'
components: {
CompA
},
data () {
return {
thing: ''
}
}
}
</script>
CompA:
<template>
<b-form #submit="submitCallback">
<b-form-group
id="groupID"
description=""
label="Set Thing Here" :feedback="feedbackCallback"
:state="stateCallback">
<b-form-input
id="inputID" :state="stateCallback"
:value="thingvalue">
</b-form-input>
</b-form/group>
<b-button type="submit" variant="primary">Submit</b-button>
</b-form>
</template>
<script>
export default {
props: {
thingvalue: {
type: String
required: true
}
},
computed: {
stateCallback () {
return 'invalid'
},
feedbackCallback () {
return 'Please enter a valid thing'
}
},
methods: {
submitCallback (event) {
[...]
}
}
}
</script>
You might notice when I moved the code over, I changed the v-model.trim="thing" to :value="thing". I was getting the same error with thing until I did this.
Now is there something I'm missing? I've been digging a lot to try and understand. I even looked at some of bootstrap-vue's code to see if they do anything funky. But it turns out they have some computed properties being used in a very similar fashion. So I'm not understanding where the problem is happening. Let me know if you need more information.
Update
I'm more convinced that there is something going on with WebPack and VueJS as I'm not finding any definition of these properties/methods in the bundled up js file.
Well turns out it was simply a dumb mistake on my part. Did not have a closing script tag. Eslint wasn't catching either (maybe there is a setting to make sure it does), so it compiled with webpack just fine. Lesson: Always remember your closing tag!

Categories

Resources