I have code like this :
Vue.component("sample", {
inherit: true,
template: "#sample",
props: {
active : "active",
...
},
methods: {
forceChangeActive: function(){
var activeItem = this.$parent._data.active;
var modalSetup = this.$parent._data.showModal;
console.log("1.before function", this.$parent._data);
this.$parent.$options.methods.baseAction(activeItem, modalSetup);
console.log("2.before function", this.$parent._data);
});
}
});
new Vue({
el: "#app",
data: {
active: 0,
showModal: false,
...
},
methods: {
baseAction: function(activeItem, modalSetup){
console.log("3.modal", modalSetup, "activeItem": activeItem);
modalSetup = true;
activeItem = 2;
console.log("4.modal", modalSetup, "activeItem": activeItem);
}
}
});
There are active and showModal data variables in new Vue. Now I'm getting undefined for both if I use this.active or this.showModal.
And console output is:
before function Object { active=0, showModal=false }
modal false, activeItem 0
modal true, activeItem 2
before function Object { active=0, showModal=false }
How I can change variable`s values in new Vue or in component?
inherit:true is deprecated.
You need to explicitly pass active and showModal to the child component using properties. You can use .sync to make sure they are shared back to the parent as well:
<sample :active.sync="active" :show-modal.sync="showModal"></sample>
Related
App.vue
<HeaderPart></HeaderPart>
<router-view />
<PlayerBar v-if="audio"></PlayerBar>
I want to Hide/Show this PlayerBar using functions, In main.js i have created all the function to alter the value of audio.
Vue.mixin({
data: function() {
return {
baseURL: "https://sampleurl",
authToken: "sampleauth",
watch: false,
audio: true
};
},
methods: {
playaudio(item) {
window.Amplitude.playNow(item);
},
playvideo() {
this.audio = false;
this.watch = true;
console.log(this.audio);
},
stopvideo() {
this.watch = false;
this.audio = true;
console.log(this.audio);
}
},
computed: {
...mapGetters({
cur_user: "user"
})
}
});
So Whenever playvideo, the Playerbar will be hidden, and when he close video, playerbar will be shown. in Console.log, Values are coming fine, Audio is changing to true/false but playerbar not getting hidden on frontend
try v-show instead of v-if
<PlayerBar v-show="audio"></PlayerBar>
It might the naming conflict with the Vue component "watch" option (Vue watch option)
I have an app which calls a web service in the created() function and populates a property of the root data object. The property is passed via a prop to a child component and using the Chrome dev tools I can see that the prop data is available on the child component.
The problem I have is that I try to set data properties in the child component using values passed via the prop I end up with undefined property data. If I use the Chrome inspection tools and add a breakpoint I can see that the prop is an observable object in the form of {__ob__: Observer} and as such, I cannot directly access any of the data. My suspicion is that the child object sets it's data properties before the web service call has completed in the root.
How can I overcome this?
I've created a JsFiddle for this:
https://jsfiddle.net/ProNotion/a8c6nqsg/
Vue.component("mycomponent", {
template: '#my-component-template',
props: ["customer_data"],
data() {
return {
form_data: {
customerEmail: this.customer_data.customerEmail1
}
}
}
});
new Vue({
el: "#app",
data() {
return {
customer: {}
};
},
methods: {
init() {
var self = this;
axios.get("https://0bb1313e-b089-432e-b6bc-250f6162d7f0.mock.pstmn.io/GetCustomerData")
.then(response => {
self.customer = response.data;
}).catch(response => {
console.error(response);
});
}
},
created() {
this.init();
}
});
Here is my HTML markup:
<div id="app">
<mycomponent :customer_data="customer" />
</div>
<script type="x-template" id="my-component-template">
<div>
<p>{{form_data.customerEmail1}}</p>
</div>
</script>
Check response data type and format
console.log(typeof response.data) // string
{ "customerEmail1": "me#example.com", } // Remove `,`
You must parse to JSON type
axios.get(...).then(response => {
self.customer = JSON.parse(response.data.replace(',', ''))
})
Set property to watch with `deep` option
[Deep watching](https://v2.vuejs.org/v2/api/#vm-watch) will be detect nested value changes inside Objects
```
Vue.component("mycomponent", {
template: '#my-component-template',
props: ["customer_data"],
data() {
return {
form_data: {}
}
},
watch: {
customer_data: {
handler (val) {
this.form_data = val;
},
deep: true
}
}
});
```
Demo: https://jsfiddle.net/ghlee/f4gewvqn
I have child component and want to pass some data to it's parent.
My child component looks like:
// <button #click="sendClick($event)">Send</button>
// ...
data: function (){
return {
mycode: ""
}
},
methods: {
sendClick(e)
{
bus.$emit('change', this.mycode);
}
}
My parent component looks:
var app = new Vue({
el: '#app',
data: {
currentView: 'past-form',
mycode: ''
},
methods:
{
changeView()
{
this.currentView = 'past-form'
console.log(this.mycode);
},
},
created()
{
bus.$on('change', function(mycode){
this.mycode = mycode;
});
}
})
I haven't found a better place for placing bus.$on (bus is declared globally) than in created(), but the docs state that created() is for stuff that should be initialized after the page is loaded. The created() block works; I checked it by placing in it console.log(this.mycode), but should I move emit handler somewhere else?
It's look like my code does not execute mycode: '', because console.log(this.mycode); does not print anything.
As I mentioned in the comment, if your component is a direct child of your Vue, then there is no need for a bus.
That said, the created handler is fine for adding your bus event handler.
I expect the issue you have is a this issue. Try changing your handler to
bus.$on('change', mycode => this.mycode = mycode)
See How to access the correct this inside a callback?
Here is an example.
console.clear()
const bus = new Vue()
Vue.component("child", {
template: `<button #click="sendClick($event)">Send</button>`,
data: function() {
return {
mycode: "something"
}
},
methods: {
sendClick(e) {
bus.$emit('change', this.mycode);
}
}
})
var app = new Vue({
el: '#app',
data: {
currentView: 'past-form',
mycode: ''
},
methods: {
changeView() {
this.currentView = 'past-form'
console.log(this.mycode);
},
},
created() {
bus.$on('change', mycode => {
this.mycode = mycode
this.changeView()
})
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.js"></script>
<div id="app">
<child></child>
Parent mycode: {{mycode}}
</div>
Im trying to delegate an avent from one instance to another.
I have a toolbar on the top of the page with a button like this
<div id="toolbar">
<button v-on:click="add" class="btn btn-default" type="submit"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Ny</button>
</div>
This add event working in this vue instance
var toolbarApp = new Vue({
el: '#toolbar',
data: {
},
methods: {
add: function (event) {
alert('lol');
}
}
});
But now i want to attach this add event to another instance like this
var contactApp = new Vue({
mixins: [toolbarApp],
el: '#contact',
data: {
showGrid: true,
showForm: false,
editMode: false
},
created: function () {
this.getContacts();
},
methods: {
getContacts: function () {
$.getJSON(this.apiGrid, function (data) {
this.contacts = data;
}.bind(this));
},
add: function (event) {
alert('hej');
}
}
});
But i cant attach this because of diffrent instance. Is it any way to do this?
Have also tried with mixedin with no luck.
Thanks in advice
what you are trying to do is not unique, there's even a title for it "Event bus"
EventBus = new Vue();
var toolbarApp = new Vue({
el: '#toolbar',
data: {
},
methods: {
add: function (event) {
EventBus.$emit('add-something', this.somevar);
}
}
});
then in your other instance:
var contactApp = new Vue({
mixins: [toolbarApp],
el: '#contact',
data: {
showGrid: true,
showForm: false,
editMode: false
},
created: function () {
this.getContacts();
EventBus.$on('add-something', function(somevar) {
// do cool stuff, like this.getContacts...
});
},
methods: {
getContacts: function () {
$.getJSON(this.apiGrid, function (data) {
this.contacts = data;
}.bind(this));
},
add: function (event) {
alert('hej');
}
}
});
Definition:
Sometimes you need a quick and easy solution to pass data between Vue.js components.
For an application with simple architecture it’s enough to communicate between components using events. For this we can create a quick solution and implement EventBus. EventBus allows us to emit an event in one component and listen for that event in another.
https://medium.com/#andrejsabrickis/https-medium-com-andrejsabrickis-create-simple-eventbus-to-communicate-between-vue-js-components-cdc11cd59860
How is it possible to add elements dynamically to the content? Example below:
<template>
{{{ message | hashTags }}}
</template>
<script>
export default {
...
filters: {
hashTags: function(value) {
// Replace hash tags with links
return value.replace(/#(\S*)/g, '<a v-on:click="someAction()">#$1</a>')
}
}
}
</script>
Problem is that if I press the link no action will fire. Vue do not see new elements.
Update:
Based on this answer, you can do a similar dynamic-template component in Vue 2. You can actually set up the component spec in the computed section and bind it using :is
var v = new Vue({
el: '#vue',
data: {
message: 'hi #linky'
},
computed: {
dynamicComponent: function() {
return {
template: `<div>${this.hashTags(this.message)}</div>`,
methods: {
someAction() {
console.log("Action!");
}
}
}
}
},
methods: {
hashTags: function(value) {
// Replace hash tags with links
return value.replace(/#(\S*)/g, '<a v-on:click="someAction">#$1</a>')
}
}
});
setTimeout(() => {
v.message = 'another #thing';
}, 2000);
<script src="//unpkg.com/vue#latest/dist/vue.js"></script>
<div id="vue">
<component :is="dynamicComponent" />
</div>
Vue bindings don't happen on interpolated HTML. You need something Vue sees as a template, like a partial. However, Vue only applies bindings to a partial once; you can't go back and change the template text and have it re-bind. So each time the template text changes, you have to create a new partial.
There is a <partial> tag/element you can put in your HTML, and it accepts a variable name, so the procedure is:
the template HTML changes
register new partial name for the new template HTML
update name variable so the new partial is rendered
It's a little bit horrible to register something new every time there's a change, so it would be preferable to use a component with a more structured template if possible, but if you really need completely dynamic HTML with bindings, it works.
The example below starts out with one message, link-ified as per your filter, and after two seconds, changes message.
You can just use message as the name of the partial for registering, but you need a computed that returns that name after doing the registering, otherwise it would try to render before the name was registered.
var v = new Vue({
el: 'body',
data: {
message: 'hi #linky'
},
computed: {
partialName: function() {
Vue.partial(this.message, this.hashTags(this.message));
return this.message;
}
},
methods: {
someAction: function() {
console.log('Action!');
},
hashTags: function(value) {
// Replace hash tags with links
return value.replace(/#(\S*)/g, '<a v-on:click="someAction()">#$1</a>')
}
}
});
setTimeout(() => {
v.$set('message', 'another #thing');
}, 2000);
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<partial :name="partialName"></partial>
I just learned about $compile, and it seems to fit your need very nicely. A very simple directive using $compile avoids all the registrations.
Vue.directive('dynamic', function(newValue) {
this.el.innerHTML = newValue;
this.vm.$compile(this.el);
});
var v = new Vue({
el: 'body',
data: {
message: 'hi #linky'
},
computed: {
messageAsHtml: function() {
return this.message.replace(/#(\S*)/g, '<a v-on:click="someAction()">#$1</a>');
}
},
methods: {
someAction: function() {
console.log('Action!');
}
}
});
setTimeout(() => {
v.$set('message', 'another #thing');
}, 2000);
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<div v-dynamic="messageAsHtml"></div>
In Vue.js 2 it's easier:
new Vue({
...,
computed: {
inner_html() {
return ...; // any raw html
},
},
template: `<div v-html='inner_html'></div>`,
});
The best solution I found which works fine with custom html is looks like this, it's like you kind of create new component each times the html property changes. No actually one did this, we just use computed property for creating new component.
That is how it looks:
new Vue({
el: "#root",
data: {
value: '',
name: 'root',
htmlData: '<div><input #input="onInputProxy($event)" ' +
'v-model="value" ' +
'v-for="i in 3" ' +
':ref="`customInput${i}`"></div>'
},
computed: {
// our component is computed property which returns the dict
htmlDataComponent () {
return {
template: this.htmlData, // we use htmlData as template text
data() {
return {
name: 'component',
value: ''
}
},
created () {
// value of "this" is formComponent
console.log(this.name + ' created');
},
methods: {
// proxy components method to parent method,
// actually you done have to
onInputProxy: this.onInput
}
}
}
},
methods: {
onInput ($event) {
// while $event is proxied from dynamic formComponent
// value of "this" is parent component
console.log(this.name + ' onInput');
// use refs to refer to real components value
console.log(this.$refs.htmlDataComponent.value);
console.log(this.$refs.htmlDataComponent.$refs.customInput1);
console.log(this.$refs.htmlDataComponent.$refs.customInput2);
console.log(this.$refs.htmlDataComponent.$refs.customInput3);
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.min.js">
</script>
<div id="root">
<component ref="htmlDataComponent"
v-if="htmlData"
:is="htmlDataComponent"></component>
</div>
I did not check it for memory efficiency, but it looks like works just fine.
Modified version of #RoyJ's answer, works in Vue.js v2.6.10
new Vue({
...,
computed: {
inner_html() {
return ...; // any raw html
},
},
directives: {
dynamic: {
bind(el, binding) {
el.innerHTML = binding.value;
},
update(el, binding) {
el.innerHTML = binding.value;
},
},
},
template: `<div v-dynamic='inner_html'></div>`,
});
Since partial has been removed from VueJS 2 (https://v2.vuejs.org/v2/guide/migration.html#Vue-partial-removed)
A better way may be to create a component which processes its content and create appropriate DOM elements
The above component will replace hashtags by clickable links
<process-text>Hi #hashtag !</process-text>
Vue.component('process-text', {
render: function (createElement) {
var hashtagRegex = /(^|\W)(#[a-z\d][\w-]*)/ig
var text = this.$slots.default[0].text
var list = text.split(hashtagRegex)
var children = []
for (var i = 0; i < list.length; i++) {
var element = list[i]
if (element.match(hashtagRegex)) {
children.push(createElement('a', {
attrs: {
href: 'https://www.google.fr/search?q=' + element,
target: "_blank"
},
domProps: {
innerHTML: element
}
}))
} else {
children.push(element)
}
}
}
return createElement('p', {}, children) // VueJS expects root element
})