Vue.js method is not defined - javascript

Here is some problem with methods,
my component can't access to methods. May i need to pass methods like prop to component ?
here is my html:
<guests v-bind="guests"></guests>
here is component in my js file
var guestsComponent = Vue.component("guests", {
props: ['adultCount', 'childCount'],
template: `
<div class="guests-total">
<div>
<a #click="decrementAdult"></a>
<a #click="incrementAdult"></a>
<input type="text" placeholder="adults"/> {{adultCount}}
</div>
</div>
`
});
and here in the same js file my vue init and methods
var app = new Vue({
el: "#search",
components: {
"guests": guestsComponent
},
data() {
return {
guests: {
adultCount: 0,
childCount: 0
}
};
},
methods: {
decrementAdult() {
this.guests.adultCount++
},
incrementAdult() {
this.guests.adultCount--
}
}
});
I can access to data without problem when i use the props but i don't know how i can pass methods like props or this is needed?
here is error on console:
ReferenceError: decrementAdult is not defined
at o.eval (eval at xa (vue.min.js:NaN), <anonymous>:3:88)
at o.fn._render (vue.min.js?f6b5:6)
at o.eval (vue.min.js?f6b5:6)
at St.get (vue.min.js?f6b5:6)
at new St (vue.min.js?f6b5:6)
at o.hn.$mount (vue.min.js?f6b5:6)
at o.hn.$mount (vue.min.js?f6b5:6)
at init (vue.min.js?f6b5:6)
at eval (vue.min.js?f6b5:6)
at b (vue.min.js?f6b5:6)

Since the click events are done in the child component guests you should emit an event to the parent component and handle it there like :
....
<a #click="$emit('decrement-adult')"></a>
...
in the parent component do :
<guests v-bind="guests" #decrement-adult="decrementAdult"></guests>

I ran into a similar 'method1' is not defined error from the following code section:
methods: {
method1(){
console.log("running method1()");
},
method2(){
method1();
},
...
The problem was that the reference to method1() should have included the this keyword like so:
export default {
name: 'TuringMachine',
props: {
msg: String,
},
data() {
},
methods: {
method1(){
console.log("running method1()");
},
method2(){
this.method1();
}
}
}
Hope this helps anyone with the same issue.

Related

Vue render button

My question is about render a button on vue instance, to click in a button and then it render another button with event click, If I simple mount the button it dont get the function tes.
const Hello = {
props: ['text'],
template: '<button v-on:click="tes"> </button> ',
};
new Vue({
el: '#app',
data: {
message: 'Click me'
},
methods:{
alertar: function(event){
const HelloCtor = Vue.extend(Hello);
var instance = new HelloCtor({
propsData: {
text: 'HI :)'
}
})
instance.$mount() // pass nothing
this.appendChild(instance.$el)
},
tes: function(){
alert('Teste');
}
}
})
Erro :
vue.js:597 [Vue warn]: Invalid handler for event "click": got undefined
(found in <Root>)
warn # vue.js:597
(index):52 Uncaught TypeError: this.appendChild is not a function
at Vue.alertar ((index):52)
at invoker (vue.js:2029)
at HTMLParagraphElement.fn._withTask.fn._withTas
The problem is that you create a child component inside of your parent Vue that contains the template with the binding to the tes function. That means that the child will look in its own methods for tes, however it is a property of your parent, not of the child itself so it will never be able to find it in its own scope. You have to add the function to the child component instead:
const Hello = {
props: ['text'],
template: '<button v-on:click="tes"> </button> ',
methods: {
tes: function(){
alert('Teste');
}
}
};
Just expanding #Philip answer
Basically you can't access parent methods in programatically created components.
You need to specify the methods inside the child components.
const Hello = {
props: ['text'],
template: '<button v-on:click="this.tes"> Vue Generated</button> ',
methods: {
tes: function(){
alert('Teste');
}}
};
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
},
mounted(){
this.alertar()
},
methods:{
alertar: function(event){
const HelloCtor = Vue.extend(Hello);
var instance = new HelloCtor({
propsData: {
text: 'HI :)'
}
})
instance.$mount() // pass nothing
this.$refs.container.appendChild(instance.$el)
},
tes: function(){
alert('Teste');
}
}
})
Check this fiddle here
https://jsfiddle.net/50wL7mdz/370645/
However in some cases you may be able to access the parent components methods using
$parent directive which I believe will not work when components is created programatically.

VueJs 2 emit custom event firing, but not being "heard"

Probably not possible, but I have an object that extends Vue/ VueComponent (tried both) that $emits a custom event that would normally be caught on its parent.
Please see this pen: https://codepen.io/anon/pen/MvmeQp?editors=0011 and watch the console.
class nonVueComponent extends Vue {
constructor(age,...args){
super(args)
console.log('new Blank Obj')
setTimeout(() => {
console.log('customEvent event does fire, but nothing hears it. Probably because it isnt in the DOM?', age)
this.$emit('customEvent', `custom event from nonVueComponent...${age}`)
},500)
}
}
Vue.component('test', {
template: `<div>
{{content}}
<child :childAge="age" #customEvent="customEvent"></child>
<child-secondary #secondaryEvent="customEvent"></child-secondary>
</div>`,
props: {},
data () {
return {
content: 'hello from component!',
age : 20
}
},
methods : {
customEvent(data){
console.log('PARENT: custom event triggered!', data)
this.content = data
},
secondaryEvent(data){
console.log('PARENT: !!secondary custom event triggered', data)
this.content = data
}
}
})
Vue.component('child',{
template: `<div>+- child {{childAge}}</div>`,
props: ['childAge'],
data () {
outsideOfVue: new nonVueComponent(this.childAge)
}
})
Vue.component('child-secondary',{
template: `<div>+- secondary event</div>`,
mounted(){
setTimeout( ()=>{
this.$emit('secondaryEvent', 'from secondary event....')
},125 )
}
})
let vm = new Vue({ el: '#app'})
Aside from using an eventBus, is there any other way to get the event up and out from the <child> ? Maybe make the nonVueComponent a mixin?
Thanks.
code:https://codepen.io/anon/pen/EvmmKa?editors=0011
The object who emits the event should be the instace of child-secondary.
Try to convey the instance to the nonVueComponent's constructor.
class nonVueComponent extends Vue {
constructor(age,comp,...args){
super(args)
console.log('new Blank Obj')
setTimeout(() => {
console.log('customEvent event does fire, but nothing hears it. Probably because it isnt in the DOM?', age)
comp.$emit('customEvent', `custom event from nonVueComponent...${age}`)
},500)
}
}

Vue.js component model update

Im absolutely new in Vue framework and I need create reusable component with live BTC/LTC/XRP price
For live prices Im using Bitstamp websockets API. Here is example usage with jQuery - run this snippet, is really live.
var bitstamp = new Pusher('de504dc5763aeef9ff52')
var channel = bitstamp.subscribe('live_trades')
channel.bind('trade', function (lastTrade) {
$('p').text(lastTrade.price)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pusher/4.1.0/pusher.min.js"></script>
<h3>BTC/USD price</h3>
<p>loading...</p>
As you can see, its really simple. But, I need to use Vue.js component. So I created this, and its also fully functional:
var bitstamp = new Pusher('de504dc5763aeef9ff52')
Vue.component('live-price', {
template: '<div>{{price}}</div>',
data: function () {
return {
price: 'loading...'
}
},
created: function () {
this.update(this)
},
methods: {
update: function (current) {
var pair = current.$attrs.pair === 'btcusd'
? 'live_trades'
: 'live_trades_' + current.$attrs.pair
var channel = bitstamp.subscribe(pair)
channel.bind('trade', function (lastTrade) {
current.price = lastTrade.price
})
}
}
})
new Vue({
el: '.prices'
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/pusher/4.1.0/pusher.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.1/vue.min.js"></script>
<section class="prices">
<live-price pair="btcusd"></live-price>
<live-price pair="ltcusd"></live-price>
<live-price pair="xrpusd"></live-price>
</section>
But, there is big BUT. Am I using Vue right way? WHERE IS IDEAL PLACE to run Pusher? In "created" or "mounted" method? In "computed"? In "watch"? Or where? Am i doing it right? I really dont known, I started with Vue ... today :(
Looks pretty good for your first day using Vue! I would just make a few changes.
The component is reaching out and using a global, bitstamp. Generally with components, you want them to be independent, and not reaching out of themselves to get values. To that end, declare the socket as a property that can be passed in to the component.
Likewise, the pair is passed in as a property, but you do not declare it and instead, use current.$attrs.pair to get the pair. But that's not very declarative and makes it harder for anyone else to use the component. Moreover, by making it a property, you can reference it using this.pair.
When using something like a socket, you should always remember to clean up when you are done using it. In the code below, I added the unsubscribe method to do so. beforeDestroy is a typical lifecycle hook to handle these kinds of things.
Computed properties are useful for calculating values that are derived from your components data: the channel you are subscribing to is a computed property. You don't really need to do this, but its generally good practice.
A Vue can only bind to a single DOM element. You are using a class .prices which works in this case because there is only one element with that class, but could be misleading down the road.
Finally, created is an excellent place to initiate your subscription.
console.clear()
var bitstamp = new Pusher('de504dc5763aeef9ff52')
Vue.component('live-price', {
props:["pair", "socket"],
template: '<div>{{price}}</div>',
data() {
return {
price: 'loading...',
subscription: null
}
},
created() {
this.subscribe()
},
beforeDestroy(){
this.unsubscribe()
},
computed:{
channel(){
if (this.pair === 'btcusd')
return 'live_trades'
else
return 'live_trades_' + this.pair
}
},
methods: {
onTrade(lastTrade){
this.price = lastTrade.price
},
subscribe() {
this.subscription = this.socket.subscribe(this.channel)
this.subscription.bind('trade', this.onTrade)
},
unsubscribe(){
this.subscription.unbind('trade', this.onTrade)
this.socket.unsubscribe(this.channel)
}
}
})
new Vue({
el: '#prices',
data:{
socket: bitstamp
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/pusher/4.1.0/pusher.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.1/vue.min.js"></script>
<section id="prices">
<live-price pair="btcusd" :socket="bitstamp"></live-price>
<live-price pair="ltcusd" :socket="bitstamp"></live-price>
<live-price pair="xrpusd" :socket="bitstamp"></live-price>
</section>
Rewrited - is it ok now?
var config = {
key: 'de504dc5763aeef9ff52'
}
var store = new Vuex.Store({
state: {
pusher: null
},
mutations: {
initPusher (state, payload) {
state.pusher = new Pusher(payload.key)
}
}
})
var livePrice = {
template: '#live-price',
props: ['pair'],
data () {
return {
price: 'loading...',
subscription: null
}
},
computed: {
channel () {
return this.pair === 'btcusd'
? 'live_trades'
: 'live_trades_' + this.pair
}
},
methods: {
onTrade (lastTrade) {
this.price = lastTrade.price
},
subscribe () {
this.subscription = this.$store.state.pusher.subscribe(this.channel)
this.subscription.bind('trade', this.onTrade)
},
unsubscribe () {
this.subscription.unbind('trade', this.onTrade)
this.$store.state.pusher.unsubscribe(this.channel)
}
},
created () {
this.subscribe()
},
beforeDestroy () {
this.unsubscribe()
}
}
new Vue({
el: '#prices',
store,
components: {
'live-price': livePrice
},
created () {
store.commit({
type: 'initPusher',
key: config.key
})
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/pusher/4.1.0/pusher.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.1/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/2.3.1/vuex.min.js"></script>
<section id="prices">
<live-price pair="btcusd"></live-price>
<live-price pair="ltcusd"></live-price>
<live-price pair="xrpusd"></live-price>
</section>
<template id="live-price">
<div>
{{price}}
</div>
</template>

Vue.js - "SyntaxError: Unexpected token <" when testing component

I am trying to create a component with Vue.js. My component is currently defined like this:
MyComponent.vue
<template id="my-template">
<div>
<button class="btn" v-on:click="increment">increment</button>
</div>
</template>
<script type="text/javascript">
Vue.component('incrementer', {
template: '#my-template',
props: {
i: {
type: Number,
default: 1,
}
},
data: function() {
return {
count: 0
}
},
methods: {
increment: function() {
this.count = this.count + this.i;
}
}
});
</script>
I am trying to create some automated tests for this component. In an attempt to do this, I have the following:
my-component.spec.js
const MyComponent = require('../src/MyComponent.vue');
describe('my-component', function() {
// Inspect the raw component options
it('has a created hook', () => {
expect(typeof MyComponent .created).toBe('function')
});
});
I am trying to run this test via Jasmine through Gulp. In Gulp, my test task looks like this:
gulpfile.js
gulp.task('test', ['build'], function() {
return gulp.src(['test/**/*spec.js'])
.pipe(jasmine({
timeout: 10000,
includeStackTrace: false,
color: false
}))
;
});
When this task gets executed, I receive the following error:
(function (exports, require, module, __filename, __dirname) { <template id="my-template">
^
SyntaxError: Unexpected token <
I don't understand why I'm receiving this error. What do I need to do to test a component in Vue.js via Jasmine?
Thank you!
According to Vue Docs:
In terms of code structure for testing, you don’t have to do anything special in your components to make them testable. Just export the raw options
When you test that component, all you have to do is import the object along with Vue to make many common assertions
So in you MyComponent.vue file:
<template>
<div>
<button class="btn" v-on:click="increment">increment</button>
</div>
</template>
<script type="text/javascript">
export default {
props: {
i: {
type: Number,
default: 1,
}
},
data: function() {
return {
count: 0
}
},
methods: {
increment: function() {
this.count = this.count + this.i;
}
}
}
</script>
Then in your my-component.spec.js:
const Vue = reuqire("vue")
const MyComponent = require('../src/MyComponent.vue');
describe('my-component', function() {
// Inspect the raw component options
it('has a created hook', () => {
expect(typeof MyComponent.created).toBe('function')
});
});

Vue.js passing functions to props not working

I have a problem that passing functions to components is not working the way it's specified in the documentation.
This is in my app.js
methods: {
updateAnswer: function(question) {
console.log('question: '+question);
}
}
This is in my html-file:
<multiplechoice class="question counterIncrement counterShow active" id="q2" whenanswered="{{ updateAnswer('1') }}"></multiplechoice>
This is in my components.js file:
props: [
'whenanswered'
],
ready: function() {
this.whenanswered();
},
I have already tried this:
props: [
{ name: 'whenanswered', type: Function}
];
but still no luck.
This is in my console when I load the page:
Uncaught TypeError: this.whenanswered is not a function
Any help would be very much appreciated :)
You could do this:
<multiplechoice class="question counterIncrement counterShow active" id="q2" :whenanswered="updateAnswer('1')"></multiplechoice>
Without the ':'(same as v-bind) like you did will only send a string and not evaluate. Even with those {{ }}.
But remember that your updateAnswerfunction should return a function. Since your prop will execute updateAnswer('1') and your multiplechoiceactually expects a function that will be executed when it wants.
methods: {
whenanswered: function(question) { // or whenanswered (question) { for ES6
return function () { ... } // or () => {...} for ES6
}
}
A fiddle would help, but basically, you need:
methods: {
whenanswered: function(question) {
...
}
}
if you wanna call that function. A prop is just a string, not a function.
Example:
<div id="app">
Loading...
<data-table on-load="{{onChildLoaded}}"></data-table>
</div>
new Vue({
el: "#app",
methods: {
onChildLoaded: function (msg) {
console.log(msg);
}
},
components: {
'data-table': {
template: 'Loaded',
props: ['onLoad'],
ready: function () {
this.onLoad('the child has now finished loading!')
}
}
}
});

Categories

Resources