Webpack imported module - constructor error - javascript

In my Vue application I habe main.js which inside a Vue instace. THis Vue instance has one mixin with handled the create hook.
...
import Engine from './engine/EngineCore'
...
let mixin = {
created: function () {
this.$engine = window.CC_ENGINE_SHADOW = new Engine();
this.$utils = {
CommonUtils,
ImagesUtils
}
}
};
new Vue({
el: 'app',
mixins: [mixin],
components: {App},
store
});
Engine it's a simple javascript file which incapsulate some global functionalities for the whole application.
Running npm run dev the application starts but gives me this error:
TypeError: __WEBPACK_IMPORTED_MODULE_5__engine_EngineCore___default.a is not a constructor
During this I did not touched anithing about Webpack and I'd like to understand deeply this error that I cannot figure it out.

Related

How can I access/mutate Vue component properties from vanilla JS

I have a Vue 2 project made with Vue CLI, and I plan to distribute it as a library, ideally with the dependencies and Vue syntax stuff abstracted away by some kind of wrapper script. I would like to allow this kind of interaction:
// mount the component on a plain JS webpage
const myComponent = new MyComponent('#my-component');
// handle events from the component in vanilla JS
myComponent.on('load', someHandler);
// (A.) call a component method and get a return value
const processedData = myComponent.process(123);
// (B.) access/mutate reactive component data properties
myComponent.setMessage('Hello world!');
I have tried changing the "build target" to build a Libary or a Web Component as mentioned in the Vue documentation. I can mount the library component just fine, and handle events, but it doesn't mention how I might interact with the component data from outside the Vue VM (see comments A and B).
How can I access Vue component methods and data properties from outside the Vue VM, in vanilla JS?
To access the Vue component properties (and methods) outside of the VM, you can mount it with a "template ref" like this:
const vm = new Vue({
components: {
MyComponent,
},
template: `
<my-component
ref="myComponent"
/>
`,
}).$mount('#mount-element");
and then you can call its methods like this:
vm.$refs.myComponent.someFunction();
You'll get the returned values and it will access/mutate reactive properties inside the VM as expected.
To use the class syntax described in the original question, we can create a simple class to wrap the vue component:
// import the component built by Vue CLI with the "library" build target
// (puts `MyComponent` in the global namespace)
import './MyComponent.umd.min.js';
import Vue from 'https://unpkg.com/vue#2/dist/vue.esm.browser.min.js';
export default class {
constructor(mountElement) {
// mount vue VM with a template ref to access its properties
const thisClass = this;
this.vm = new Vue({
components: {
MyComponent,
},
template: `
<my-component
ref="myComponent"
/>
`,
}).$mount(mountElement);
this.component = this.vm.$refs.myComponent;
}
// define methods that could call this.component's functions
someFunction() {
// do stuff
return this.component.someFunction()
}
}
It seems to work pretty well. A possible improvement would be to build the component library with a different tool, since Vue CLI v3 (with Vue v2 projects) can't output ESM module files, so the best we can do is a UMD modle that gets defined globally.

Vue.js 2 + Typescript - global variables becomes undefined after build

I have a Vue.js 2 project with Typescript. In the main.ts file, I've declared 2 variables, that I've wanted to access globally in my project:
// ...
Vue.prototype.$http = http; // this is the library imported from another file, contains various methods such as `get`, `post` etc.
Vue.prototype.$urls = urls; // this is JSON object, also imported from another file
new Vue({
store,
render: (h) => h(App),
}).$mount('#app');
In one of my components, let's call it User I have following mounted code block:
mounted(): void {
this.$http.get(`${this.$urls.getUser}/${this.userId}`);
}
Everything works fine when I'm running a local server (via npm run serve command), but when I create an app build (via npm run build command) and enter the app on the server (or the index.html file on my hdd) I receive following error:
TypeError: Cannot read property 'get' of undefined
at VueComponent.value (user.ts:62) // <-- this line is the one with $http.get from `mounted` hook
I'm not sure how to proceed with this, I've blindly tried to add those global values to various places e.g. in http.d.ts file I have the following:
import { KeyableInterface } from '#/interfaces/HelperInterfaces';
import Vue from 'vue';
declare module 'vue/types/vue' {
interface VueConstructor {
$http: KeyableInterface;
}
}
declare module 'vue/types/vue' {
interface Vue {
$http: KeyableInterface;
}
}
declare module 'vue/types/options' {
interface ComponentOptions<V extends Vue> {
http?: KeyableInterface
}
}
(I've also created urls.d.ts with similar code)
UPDATE #1:
I've tried also following approach - in my main.ts file:
const helperModules = {
/* eslint-disable-next-line #typescript-eslint/no-explicit-any */
install: (vueInstance: any) => {
vueInstance.prototype.$http = http;
vueInstance.prototype.$urls = urls;
},
};
Vue.use(helperModules);
But it still doesn't work (same error).
UPDATE #2:
I've also imported http utility into my user component, and added following console.log to existing mounted callback:
console.log(http, this.$http)
And while working on my localhost, it returns me twice the same value, but when I create a build it returns me:
ModuleĀ {__esModule: true, Symbol(Symbol.toStringTag): "Module"}, undefined
Similar thing happens, when I add console.log(urls, this.$urls) - imported module is being logged, while prototyped one returns undefined.
Any thoughts? Will appreciate any help.
Finally I've overcome the problem by moving the prototyping parts from main.ts to App.ts file.
I'm not 100% sure if it's a valid "the Vue.js way" of solving this, as I've always declared that in main.js file - but I was using then JavaScript & it was "just working" as expected.

Is it possible to use both Vue.js components as well as conventional Vue instances in an application?

I currently have a Laravel application that is using Vue.js as the frontend JavaScript framework. For the large scripts I am using components but for smaller scripts I wish to just use a Vue instance in the blade template.
The problem I have is that the app.js configuration I currently I have to make one way work breaks the other way and vice versa.
require('./bootstrap');
window.Vue = require('vue');
Vue.component('repairs', require('./components/repairs.vue'));
Vue.component('repair-show', require('./components/repair-show.vue'));
const app = new Vue({
el: '#app'
});
The above works components, the below works for creating an instance.
require('./bootstrap');
window.Vue = require('vue');
Vue.component('repairs', require('./components/repairs.vue'));
Vue.component('repair-show', require('./components/repair-show.vue'));
I understand why this wouldnt work for both with the components exporting to the global Vue instance I was just wondering if there was another way of configuring to make both ways work.
For your reference I have an example of one of the Vue scripts in the Vue component.
import axios from 'axios';
var csrf_token = $('meta[name="csrf-token"]').attr('content');
export default {
created() {
this.blocks = JSON.parse(this.b);
this.contractors = JSON.parse(this.c);
this.token = csrf_token;
},
props: ['b', 'c'],
data(){
return {
blocks: '',
blockSelected: '',
units: '',
token: '',
}
},
methods: {
getUnits: function(){
var self = this;
axios.post('/getrepairsUnit', {
blockSelected: this.blockSelected,
})
.then(function(response) {
self.units = response.data;
})
.catch(function(error) {
console.log(error);
});
},
}
}
And an example of my Vue instance...
<script>
var app = new Vue({
el: '#app',
data: {
deposit: 0
}
});
</script>
The error I get when trying to run the Vue instance is the following:
[Vue warn]: Property or method "deposit" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
(found in root instance)
I am able to console log out so is working somewhat but the view doesnt pick up the data. This however does work if I remove the global instance in the app.js file.
Thank you
You cannot have two view models in the same page element (i.e. two Vue instances), if you want to use different vue instance you will need to create different JavaScript bundles for each instance, or simply add a Vue instance in the blade template and don't include the app.js.
Vuejs will work at least one instance should be created and its should bound with one ID as you did as follows
require('./bootstrap');
window.Vue = require('vue');
Vue.component('repairs', require('./components/repairs.vue'));
Vue.component('repair-show', require('./components/repair-show.vue'));
const app = new Vue({
el: '#app'
});
Here
const app = new Vue({
el: '#app'
});
above code is the heart of vuejs application. This the point your vuejs application gets bootstrapped
Make sure you are doing the same while working with vuejs

Vue js - source available to component

I have created a source in my main app.js file
let source = {
selectedTalent: 0,
};
var vm = new Vue({
el: '#app',
components:{
Navigation:navigation,
I want the source to be available in my navigation component. I aways get a not defined error.
How do I get this to work. Do I have to pass as a prop? I though this would be globally available as 'source'
My component is imported like this as I am using webpack
import navigation from './components/nav.vue';
This is the only difference I can see from the example in the docs.
You should export and import that source object correctly, otherwise its scope is within the app.js script. That scoping is pretty much what Webpack does.
Example:
source.js
export default let source = {
selectedTalent: 0,
};
navigation.component.js
import source from '../path/to/source/'
/* your code for the component here */
And then you could avoid declaring source in your app.js as you seem not to be using it in there.

Instantiate standalone Vue.js app (2.0.0rc7) with properties

I have a Vue.js app (2.0.0rc7) that is built using single page components. The main component is called App. To render my application into a div with id app, I use the following script (I called it main.js):
import Vue from 'vue'
import App from './App.vue'
var vm = new Vue({
el: '#app',
render: h => h(App)
})
This approach works well. I am using webpack to resolve imports. However, I would like my application be be used as a "drop-in" that developers can easily use within their websites. Therefore, I wonder, how can I pass properties to the main App component?
For example, I would like to enable developers to load my (built) app using HTML script tags and then instantiate it using:
App(dataObj1, dataObj2, ..., '#id-of-div-element-to-mount')
I found a solution to my problem: I created an init function that takes as input the desired data and then renders the application to an HTML element. I make this init function available in the global namespace:
var init = function (data, el) {
const vm = new Vue({
el: el,
template: '<app :data="data"/>',
components: {
App
},
data () {
return {
data: data
}
}
})
}
// grab existing namespace object, or create a blank object
// if it doesn't exist
var APP = window.APP || {}
APP = {
init: init
}
window.APP = APP
This way, others can use my built application with their data in their HTML pages via APP.init(data, '#some-el-id').
For this to work, Vue needs to be built in the standalone mode, which requires to add this to the webpack config:
// ...
resolve: {
alias: {vue: 'vue/dist/vue.js'}
},
// ...
See details here: https://github.com/vuejs/vue/wiki/Vue-2.0-RC-Starter-Resources#standalone-vs-runtime-builds

Categories

Resources