Property or method ... is not defined on the instance but referenced during render - vue.js

I am writing a sample Vue component that works with an Event.
I get this error by rendering the index.html: "Property or method "onCouponApplied" is not defined on the instance but referenced during render" in the console.
index.html
<div id="root">
<coupon #applied="onCouponApplied"></coupon>
</div>
and below is Vue codes
main.js
window.Event = new Vue();
Vue.component('coupon',{
template: `<input placeholder="Enter" #blur="onCouponApplied">`,
methods:{
onCouponApplied() {Event.$emit('applied');}
}
})
new Vue({
el:"#root",
created() {
Event.$on('applied', ()=>alert('Received'));
},
});

First, Vue template code doesn't belong in index.html: <div id="root"></div>
Then you're mixing up event-bus and normal custom Vue events.
It's either:
window.Event = new Vue();
Vue.component("coupon", {
template: `<input placeholder="Enter" #blur="onCouponApplied">`,
methods: {
onCouponApplied() {
Event.$emit("applied");
}
}
});
new Vue({
el: "#app",
template: `<coupon></coupon>`,
created() {
Event.$on("applied", () => alert("Received"));
},
});
or:
Vue.component("coupon", {
template: `<input placeholder="Enter" #blur="$emit('applied')">`,
});
new Vue({
el: "#app",
template: `<coupon #applied="onCouponApplied"></coupon>`,
methods: {
onCouponApplied() {
alert("Received");
}
}
});

Related

Get Vue.js instance-specific component data within x-template

One of my root Vue.js components is using an x-template, displayed in 3 different parts of my app (3 separate Vue instances).
Everything is working if I put the data directly into the component, but I can’t figure out how to pass in data to each individual vm instance. Here’s what I’m using at present:
Vue.component('some-table', {
template: '#some-template',
data() {
return { someArray: [] }
},
methods: {
}
});
let someVm = new Vue({
el: '#some-div',
});
The template:
<script id="some-template" type="text/x-template">
<table v-for="(item, id) in someArray" v-bind:key="id">
<tr>
<td>{{item.someKey}}</td>
</tr>
</table>
</div>
</script>
Within another JavaScript class, I’m attempting to push data to someArray:
class SomeClass {
constructor() {
}
someMethod() {
let newData = [1,2,3];
someVm.someArray = newData; // doesn't work
someVm.someArray.push(...newData); // doesn't work
someVm.someArray.concat(newData); // doesn't work
}
}
Using any of the various lines in someMethod() above, I can see someVm contains someArray, but with no observers.
I can’t seem to figure out how to push new data to each instance, then be accessible within some-template. I’m obviously missing something really simple, but I’ve been stuck on this for a while now, so any pointers would be amazing!
Thanks!
Do not set data attr at component registration (Vue.component(...)). Instead, declare the props that component uses and pass it through markup or js.
Vue.config.productionTip = false;
Vue.config.devtools = false;
Vue.component('some-component', {
template: '#some-component',
props: {
message: {
type: String, // declare the props: https://vuejs.org/v2/api/#props
}
}
});
const cp1 = new Vue({
el: '#cp1',
data() {
return {
msg: "using data"
};
}
});
const cp2 = new Vue({
el: '#cp2'
});
const cp3 = new Vue({
el: '#cp3'
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script id="some-component" type="text/x-template">
<p>{{message}}</p>
</script>
<div id="cp1">
<some-component :message="msg"></some-component>
</div>
<div id="cp2">
<some-component message="using props"></some-component>
</div>
<div id="cp3" is="some-component" message="using props and is"></div>

How to load external Vue components when I not use npm

How to load external Vue components when I not use npm? I got this error message:
ReferenceError: VueButtonSpinner is not defined
Vue.use(VueButtonSpinner);
var vm = new Vue({
delimiters: ['[[', ']]'],
el: '#test',
data: {
isLoading: false,
status: ''
},
components: {
VueButtonSpinner
},
methods: {
send() {
console.log("ypyoyoyoy");
this.isLoading = true;
}
},
});
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js"></script>
<script src="https://unpkg.com/vue-button-spinner#2.2.0/dist/vue-button-spinner.js"></script>
<div id="test">
<button-spinner
#click="send"
:isLoading="isLoading"
:disabled="isLoading"
:status="status">
<span>Submit</span>
</button-spinner>
</div>
This should work. The trick is that the script gets imported, but the code defaults to the default attribute inside of window['vue-button-spinner']:
Vue.component('button-spinner', window['vue-button-spinner'].default);
var vx = new Vue({
// delimiters: ['[[', ']]'],
el: '#test',
data() {
return {
isLoading: false,
status: ''
};
},
methods: {
send() {
console.log("ypyoyoyoy");
this.isLoading = true;
}
},
});
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js"></script>
<script src="https://unpkg.com/vue-button-spinner#2.2.0/dist/vue-button-spinner.js"></script>
<div id="test">
<button-spinner
#click.native="send"
:is-loading="isLoading"
:disabled="isLoading"
:status="status">
<span>Submit</span>
</button-spinner>
</div>
You can use the module loader system, so use <script type="module" src="whatever.js"></script> and import in whatever.js:
From MDN <script>
module: HTML5 For HTML5-compliant browsers the code is treated as a
JavaScript module. The processing of the script contents is not
affected by the charset and defer attributes. For information on using
module, see ES6 in Depth: Modules. Code may behave differently when
the module keyword is used.
whatever.js
import Vue from "https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js";
import {
VueButtonSpinner
} from "https://unpkg.com/vue-button-spinner#2.2.0/dist/vue-button-spinner.js";
const app = new Vue({
el: "#app",
components: {
"button-spinner": VueButtonSpinner
}
});

Get Vue component object (class) from component instance

Question
Given I am in component context, how do I get the component object? By component object I mean the object you get when you import Component from 'Component.vue'.
Current progress
Here's one possibility I found.
const component = {
methods: {
getComponent: () => this,
displayItem () {
console.log('this.getComponent()', this.getComponent()) // undefined
console.log('this', this) // component instance
console.log('component', component) // what I need (component object)
},
},
}
export default component
The downside though is that it kills IDE support.
I also checked this manually.
Ideal solution
The approximation to syntax I'd like to see: this.$component.
What's the point?
Instantiate components via :is="component".
Perform instance of check.
The closer you got is vm.$options:
Vue.component('some-comp', {
template: '<p>{{ message }}</p>',
props: {name: String},
data() {
return {
message: 'Open the console!'
}
},
computed: {
example() {
return this.message.toUpperCase();
}
},
watch: {
message() {
console.log('watcher triggered');
}
},
mounted() {
console.log(this.$options);
console.dir(this.$options.__proto__);
}
});
new Vue({
el: '#app'
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<some-comp :name="'Alice'"></some-comp>
</div>
But it seems what you want is constructor:
Vue.component('aaa-aaa', {
template: '<div>AAA component</div>'
})
Vue.component('bbb-bbb', {
template: '<div>BBB component</div>'
})
new Vue({
el: '#app',
mounted() {
console.log(this.$refs.a1);
console.log(this.$refs.a1.constructor);
console.log(this.$refs.b1);
console.log(this.$refs.b1.constructor);
console.log('a1 a2', this.$refs.a1.constructor === this.$refs.a2.constructor)
console.log('a1 b1', this.$refs.a1.constructor === this.$refs.b1.constructor)
console.log('b1 b2', this.$refs.b1.constructor === this.$refs.b2.constructor)
console.log('b2 a2', this.$refs.b2.constructor === this.$refs.a2.constructor)
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<aaa-aaa ref="a1"></aaa-aaa>
<aaa-aaa ref="a2"></aaa-aaa>
<bbb-bbb ref="b1"></bbb-bbb>
<bbb-bbb ref="b2"></bbb-bbb>
</div>

Vue Component props value is does not seem on template

Everything is fine in my main.js
import User from './components/User.vue'
new Vue({
el: '#user',
render: h => h(User),
data() {
return {
groups: []
}
},
mounted() {
this.getGroups();
},
methods: {
getGroups: function () {
axios.get('http://vagrant.dev/groups')
.then(response => this.groups = response.data)
.catch(error => console.log(error));
}
}
});
User.vue
<template>
<div id="user">
{{groups}} <!-- must be printed but it doesn't seem -->
</div>
</template>
<script>
export default {
name: 'user',
props: ['groups']
}
</script>
and index.html
<div id="user">
<user :groups="groups"></user>
</div>
Where is the mistake?
I do not get any errors, but I didn't understand why the value was not printed on the screen
Seems like you are putting 2 Vue instances on the same template since in your main file you are defining the template as el: "#user" which is already controlled by User.vue.
In you index.html add a div with id="app" and in main.js set el: "#app"
Your main.js and index.html shouldn't mix template in HTML and render function. So your main.js should be:
```new Vue({
el: '#user',
components: { User },
data() {
return {
groups: []
}
},
...```
You shouldn't directly reassign data value because Vue cannot detect property additions and rerender view. You should do it via Vue.set or this.$set like this:
this.$set(this, 'groups', response.data)
You can read more about how vue reactive in https://v2.vuejs.org/v2/guide/reactivity.html and Vue.set API: https://v2.vuejs.org/v2/api/#Vue-set

Vue.js 2.0 this.$compile

How do can you compile a HTML string to template within a component method?
This was possible in Vue 1 like in this jsfiddle
new Vue({
el: '#app',
data: {
sampleElement: '<button v-on="click: test()">Test</button>'
},
methods:{
addNewElement: function(){
var element = $('#app').append(this.sampleElement);
/* compile the new content so that vue can read it */
this.$compile(element.get(0));
},
test: function(){
alert('Test');
}
}
});
How can you do this in Vue 2?
This is no longer possible, however, Vue 2 is data driven, so you shouldn't be trying to affect the DOM manually at all, instead you should bind elements to the underlying data in your view model. With that in mind your example will need to be re-written. Firstly, start by making your element a component:
Vue.component('my-button', {
template: '<button v-on:click="test()">{{text}}</button>',
props: ['text'],
methods: {
test() {
alert('Test');
}
}
});
Then you can create your main view model and add your button component using a v-for:
Markup:
<button v-on:click="addNewElement()">Add Element</button>
<my-button v-for="button in buttons" :text="button"></my-button>
View model:
new Vue({
el: '#app',
methods: {
addNewElement: function() {
this.buttons.push('Test');
}
},
data: {
buttons: []
}
});
In this example we are pushing buttons to an array that will then be displayed on the page, rather than manually appending them to the template.
Here's the JSFiddle: http://jsfiddle.net/10q9je5a/
If you want something more generic, then you can simply create an array of different components and use :is to let Vue know which component to render:
Markup:
<div id="app">
<button v-on:click="addNewElement()">Add Element</button>
<div :is="element.component" v-for="element in elements" v-bind="element.props"></div>
</div>
View Model:
new Vue({
el: '#app',
methods: {
addNewElement: function() {
this.elements.push({component: 'my-button', props: {text: 'Test'}});
}
},
data: {
elements: []
}
});
Here's the JSFiddle for that: http://jsfiddle.net/cxo5eto0/