Is it possible to use inline an $emitted parameter? - vue.js

In the following code, clicking on the component emits a signal to the parent, who modifies its state inline (in the sense - not via a handler):
Vue.component('my-component', {
template: '<div v-on:click="emitit">click on the component</div>',
methods: {
emitit: function() {
this.$emit('mysignal', 7)
}
}
})
new Vue({
el: "#root",
data: {
from: 0
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="root">
<my-component v-on:mysignal="from=5"></my-component>
from component: {{ from }}
</div>
Is it possible to access the parameter provided via the $emit directly in v-on:mysignal="..."?
I know that I can use a handler defined in the main Vue component but I would like to simplify my code and avoid to have several handlers in methods.

Yes, like this:
<my-component v-on:mysignal="value => from = value"></my-component>
Vue.component('my-component', {
template: '<div v-on:click="emitit">click on the component</div>',
methods: {
emitit: function() {
this.$emit('mysignal', 7)
}
}
})
new Vue({
el: "#root",
data: {
from: 0
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="root">
<my-component v-on:mysignal="value => from = value"></my-component>
from component: {{ from }}
</div>

#click="val => $emit('mysignal', val)"
<div v-on:click="val => $emit('mysignal', val)">click the component</div>'
or even
#click.prevent="e => $emit('mysignal', e.target.value)"

Related

How can I select element Id with a function in Vue.js?

I want to use the Id of the element in which I use the function as a parameter. Example:
<div
class="col-2 column-center"
id="myId"
#mouseover="aFunction(id)"
>
methods: {
aFunction(id){
alert(id);
}
}
But it doesn't work. How can I do it?
In another way, you can make your id attribute bind with the data property, like this :
<template>
<div class="col-2 column-center" v-bind:id="id" #mouseover="aFunction(id)">
test
</div>
</template>
<script>
export default {
name: 'Test',
data() {
return {
id: 'myId',
};
},
methods: {
aFunction(id) {
alert(id);
},
},
};
</script>
You can pass the event to the method and then get the id from event.target.id.
https://jsfiddle.net/tr0f2jpw/
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
},
methods: {
aFunction(event){
alert(event.target.id);
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<div
class="col-2 column-center"
id="myId"
#mouseover="aFunction($event)"
>
some stuff
</div>
</div>

use instance property as v-model

I use a Form object to handle all my input fields and validation/errors etc.
new Vue({
el: '#root',
data: {
form: { trainer: '' }
}
And I can use it as v-model like this:
<input v-model='form.trainer' name='trainer'>
I would like to make the form an instance property, because I have to pass it to all my components at the moment.
Vue.prototype.$form = {trainer: ''}
new Vue({ el: '#root' });
However, its not reactive anymore:
<input v-model="$form.trainer">
<span v-text="$form.trainer"></span>
Changes to the input are not reflected on the span.
Is there a solution to use a instance property as v-model?
Using Global Mixin:
You can use Global Mixin and then add form: { trainer: '' } to it and then this option will be available in every Vue instance created afterwards like:
// inject a handler for `myOption` custom option
Vue.mixin({
data: function () {
return {
form: { trainer: '' }
}
}
})
and then you can use it like normal component data option:
<input v-model="form.trainer">
<span v-text="form.trainer"></span>
Demo:
// inject a handler for `myOption` custom option
Vue.mixin({
data: function() {
return {
form: { trainer: '' }
}
}
})
// Define a new component called trainer
Vue.component('trainer', {
template: `<div>
<input v-model="form.trainer">
<span v-text="form.trainer"></span>
</div>
`
})
new Vue({
el: "#myApp",
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet">
<div id="myApp">
<trainer></trainer>
<trainer></trainer>
</div>
Demo #2:
var trainer2Data = {
form: { trainer: '' }
}
Vue.mixin({
data: function() {
return trainer2Data;
}
})
// Define a new component called trainer2
Vue.component('trainer2', {
template: `<div>
<input v-model="form.trainer">
<span v-text="form.trainer"></span>
</div>
`
})
new Vue({
el: "#myApp",
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet">
<div id="myApp">
<trainer2></trainer2>
<trainer2></trainer2>
</div>

vue.js – get new data information

I'm building a chrome extension using vue.js. In one of my vue components I get tab informations of the current tab and wanna display this information in my template. This is my code:
<template>
<div>
<p>{{ tab.url }}</p>
</div>
</template>
<script>
export default {
data() {
return {
tab: {},
};
},
created: function() {
chrome.tabs.query({ active: true, windowId: chrome.windows.WINDOW_ID_CURRENT }, function(tabs) {
this.tab = tabs[0];
});
},
};
</script>
The Problem is, that the template gets the data before it's filled through the function. What is the best solution for this problem, when the tab data doesn't change after it is set once.
Do I have to use the watched property, although the data is only changed once?
// EDITED:
I've implemented the solution, but it still doesn't work. Here is my code:
<template>
<div>
<div v-if="tabInfo">
<p>set time limit for:</p>
<p>{{ tabInfo.url }}</p>
</div>
<div v-else> loading... </div>
</div>
</template>
<script>
export default {
data() {
return {
tabInfo: null,
};
},
mounted() {
this.getData();
},
methods: {
getData() {
chrome.tabs.query({ active: true, windowId: chrome.windows.WINDOW_ID_CURRENT }, function(tabs) {
console.log(tabs[0]);
this.tabInfo = tabs[0];
});
},
},
};
</script>
The console.log statement in my getData function writes the correct object in the console. But the template only shows the else case (loading...).
// EDIT EDIT
Found the error: I used 'this' in the callback function to reference my data but the context of this inside the callback function is an other one.
So the solution is to use
let self = this;
before the callback function and reference the data with
self.tab
You could initialize tab to null (instead of {}) and use v-if="tabs" in your template, similar to this:
// template
<template>
<div v-if="tab">
{{ tab.label }}
<p>{{ tab.body }}</p>
</div>
</template>
// script
data() {
return {
tab: null,
}
}
new Vue({
el: '#app',
data() {
return {
tab: null,
}
},
mounted() {
this.getData();
},
methods: {
getData() {
fetch('https://reqres.in/api/users/2?delay=1')
.then(resp => resp.json())
.then(user => this.tab = user.data)
.catch(err => console.error(err));
}
}
})
<script src="https://unpkg.com/vue#2.5.17"></script>
<div id="app">
<div v-if="tab">
<img :src="tab.avatar" width="200">
<p>{{tab.first_name}} {{tab.last_name}}</p>
</div>
<div v-else>Loading...</div>
</div>

VueJS process component or HTML using a function

I understand how this works from the VueJS Documentation:
<div id="components-demo">
<button-counter></button-counter>
</div>
And also this:
<div id="app">
<a v-on:click="loadElement(request, etc)">Load</a>
</div>
Is there any way to write the equivalent from a function and have Vue pick it up? For example as this:
<div id="app">
{{ writeComponent('component-name', etc) }}
or
{{ writeElement('loadElement') }} <!-- which then writes the component above -->
</div>
The reason for this is that in this context quite a few components might need to be written and writing it out in HTML would be cumbersome.
You could use the render function like below:
Vue.component('button-counter', {
template: '<span class="bc">bc</span>'
})
new Vue({
el: '#app',
data: {
request: 'req!',
etc: 'etc!',
buttonCounters: []
},
methods: {
loadElement(r, e) {
console.log('loadElement', r, e);
this.buttonCounters.push(r);
}
},
render(h) {
let bcs = this.buttonCounters.map(bc => h('button-counter'));
let loadLink = h('a', {on: {"click": ($event) => { this.loadElement(this.request, this.etc)}}}, ["Load"]);
return h('div', {attrs: {"id": "app"}}, [loadLink, " ", ...bcs])
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app"></div>
Though the same can be achieved via regular template and v-for:
Vue.component('button-counter', {
template: '<span class="bc">bc</span>'
})
new Vue({
el: '#app',
data: {
request: 'req!',
etc: 'etc!',
buttonCounters: []
},
methods: {
loadElement(r, e) {
console.log('loadElement', r, e);
this.buttonCounters.push(r);
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<a v-on:click="loadElement(request, etc)">Load</a>
<button-counter v-for="(bc, index) in buttonCounters" :key="index"></button-counter>
</div>

How to handle getting new data even?

I have got Vue-JS app. After use click variable rasters_previews_list get new data. I would like to generate list with them. I can't understand which directive I should use to handle this even.
Here is my code:
var userContent = Vue.extend({
template: `
<div class="LayersMenuSectionContent" v-if="rasters_previews_list.length">
<ul v-for="img in rasters_previews_list">
<li>{{img.id}}</li> // generate list here
<ul>
</div>
`,
data: function () {
return {
rasters_previews_list: [] // when come new data I should generate li with then
}
},
ready: function()
{
},
methods:
{
}
});
Should I use v-on or v-if?
When rasters_previews_list is changed, list is autorendered in v-for.
new Vue({
el: '#app',
template: `
<div class="LayersMenuSectionContent" v-show="rasters_previews_list.length">
<ul v-for="img in rasters_previews_list">
<li>{{img.id}}</li>
<ul>
</div>
<button #click="add">Add</button>
`,
data: function () {
return {
rasters_previews_list: [] // when come new data I should generate li with then
}
},
ready: function(){ },
methods: {
add(){
this.rasters_previews_list.push({id: 'hello'},{id: 'world'});
}
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<div id="app"></div>
Extra: You must use v-show instead of v-if for this case