VueJs 2: How to display prop in another component? - javascript

I have a simple question, but cannot find how to solve.
There are 2 Vue 2 Components. In Component 1, two props are passed, which therefore are used in Component 2.
// Component 1
Vue.component('component1', {
props: {
titleTest: {
type: String,
required: true
},
textTest: {
type: String,
required: true
}
},
template: `
<div>
<div :title="titleTest">{{ titleTest }}</div>
<div :data-test="textTest">{{ textTest }}</div>
</div>
`,
created() {
console.log('Created1');
this.$root.$refs.component1 = this;
},
methods: {
foo: function() {
alert('this is component1.foo');
}
}
});
// Component 2
Vue.component('component2', {
template: `
<div>
<div>Some text</div>
<ul>
<li>List Item1</li>
<li>List Item2</li>
</ul>
<div>
<button id='test' type="submit" #click="bar">Text</button>
<component1 ref="component1" :title="test1" :data-test="test2"></component1>
</div>
</div>
`,
data: function() {
return {
test1: 'testText1',
test2: 'testText2'
};
},
methods: {
bar: function() {
this.$root.$refs.component1.foo();
}
},
created: function() {
console.log('Created2');
}
});
new Vue({
el: '#plotlyExample',
});
My idea, when I use Component 1 in Component 2 HTML template and bind data variables, they should be displayed. However, Vue sets only "title" and "data-test" but {{ titleTest }}, {{ textTest }} are not displayed. Additionally Vue sets props in one div, instead of two separate.
My ideal result is:

You named your props titleTest and textTest, that means you need to pass title-test and text-test, NOT title and data-test.
The reason they end up in your main <div> is because when Vue doesn't recognise them as props (because you used different names), it falls back on assuming they're regular HTML attributes (like class, id, and style)
In order for it to work, you either need to rename your props to title and dataTest in Component1, or you should use the title-test and text-test names in Component2.

You just need pass props to the component1
<component1 ref="component1" :title-test="test1" :text-test="test2"></component1>

you are missnaming the props in the component1 ( child component ) , you used title and data-test but your props names are titleTest and textTest ! so you should use title-test and text-test instead .
:prop-name="propValue"

Related

Vue2Js: Is it possible to call a method with attributes from one component in another?

I have 3 Components: Component 1 is Top Menu, Component 2 is a kind of lib with functions, Component 3 is main code.
Ideally, I want to use in Component 1 a method from Component 2 with attributes from Component 3.
And then use Component 1 in Component 3.
// Component 1 Top Menu
Vue.component('component1', {
props: {
titleTest: {
type: String,
required: true
},
textTest: {
type: String,
required: true
}
},
template: `
<div>
<div :title="titleTest">{{ titleTest }}</div>
<div :data-test="textTest">{{ textTest }}</div>
<button id='test' type="submit" #click="bar">TestButton</button>
</div>
`,
created() {
console.log('Created1');
this.$root.$refs.component1 = this;
},
methods: {
//alertText should be passed from Component 3
bar: function(alertText) {
console.log(alertText);
this.$root.$refs.component2.foo(alertText);
}
}
});
// Component 2 Lib
Vue.component('component2', {
created() {
this.$root.$refs.component2 = this;
},
methods: {
foo: function(alertText) {
alert('this is component2.foo' + alertText);
}
}
});
// Component 3 Main Code
Vue.component('component3', {
template: `
<div>
<div>Some text</div>
<ul>
<li>List Item1</li>
<li>List Item2</li>
</ul>
<div>
<component1 ref="component1" :title-test="test1" :text-test="test2"></component1>
</div>
</div>
`,
data: function() {
return {
test1: 'testText1',
test2: 'testText2',
alertText: 'alertText'
};
},
created: function() {
}
});
new Vue({
el: '#plotlyExample',
});
I am not really sure is it possible, tried with "emits" but did not work.
When you call Vue.component('component2', {...}), it just tels Vue about named component, and that does not make component instance, for using methods of component2 it should be rendered/mounteded. But i think, you wrong using component2, for same, will be better using mixins or provide/reject or vuex.

Why not my vue component not re-rendering?

I have a question why not this component, not re-rendering after changing value so what I'm trying to do is a dynamic filter like amazon using the only checkboxes so let's see
I have 4 components [ App.vue, test-filter.vue, filtersInputs.vue, checkboxs.vue]
Here is code sandbox for my example please check the console you will see the value changing https://codesandbox.io/s/thirsty-varahamihira-nhgex?file=/src/test-filter/index.vue
the first component is App.vue;
<template>
<div id="app">
<h1>Filter</h1>
{{ test }}
<test-filter :filters="filters" :value="test"></test-filter>
</div>
</template>
<script>
import testFilter from "./test-filter";
import filters from "./filters";
export default {
name: "App",
components: {
testFilter,
},
data() {
return {
filters: filters,
test: {},
};
},
};
</script>
so App.vue that holds the filter component and the test value that I want to display and the filters data is dummy data that hold array of objects.
in my test-filter component, I loop throw the filters props and the filterInputs component output the input I want in this case the checkboxes.
test-filter.vue
<template>
<div class="test-filter">
<div
class="test-filter__filter-holder"
v-for="filter in filters"
:key="filter.id"
>
<p class="test-filter__title">
{{ filter.title }}
</p>
<filter-inputs
:value="value"
:filterType="filter.filter_type"
:options="filter.options"
#checkboxChanged="checkboxChanged"
></filter-inputs>
</div>
</div>
<template>
<script>
import filterInputs from "./filterInputs";
export default {
name: "test-filter",
components: {
filterInputs,
},
props:{
filters: {
type: Array,
default: () => [],
},
value: {
type: Array,
default: () => ({}),
},
},
methods:{
checkboxChanged(value){
// Check if there is a array in checkbox key if not asssign an new array.
if (!this.value.checkbox) {
this.value.checkbox = [];
}
this.value.checkbox.push(value)
}
};
</script>
so I need to understand why changing the props value also change to the parent component and in this case the App.vue and I tried to emit the value to the App.vue also the component didn't re-render but if I check the vue dev tool I see the value changed but not in the DOM in {{ test }}.
so I will not be boring you with more code the filterInputs.vue holds child component called checkboxes and from that, I emit the value of selected checkbox from the checkboxes.vue to the filterInputs.vue to the test-filter.vue and every component has the value as props and that it if you want to take a look the rest of components I will be glad if you Did.
filterInpust.vue
<template>
<div>
<check-box
v-if="filterType == checkboxName"
:options="options"
:value="value"
#checkboxChanged="checkboxChanged"
></check-box>
</div>
</template>
<script>
export default {
props: {
value: {
type: Object,
default: () => ({}),
},
options: {
type: Array,
default: () => [],
},
methods: {
checkboxChanged(value) {
this.$emit("checkboxChanged", value);
},
},
}
</script>
checkboxes.vue
<template>
<div>
<div
v-for="checkbox in options"
:key="checkbox.id"
>
<input
type="checkbox"
:id="`id_${_uid}${checkbox.id}`"
#change="checkboxChange"
:value="checkbox"
/>
<label
:for="`id_${_uid}${checkbox.id}`"
>
{{ checkbox.title }}
</label>
</div>
</div>
<template>
<script>
export default {
props: {
value: {
type: Object,
default: () => ({}),
},
options: {
type: Array,
default: () => [],
},
}
methods: {
checkboxChange(event) {
this.$emit("checkboxChanged", event.target.value);
},
},
};
</script>
I found the solution As I said in the comments the problem was that I'm not using v-model in my checkbox input Vue is a really great framework the problem wasn't in the depth, I test the v-model in my checkbox input and I found it re-render the component after I select any checkbox so I search more and found this article and inside of it explained how we can implement v-model in the custom component so that was the solution to my problem and also I update my codeSandbox Example if you want to check it out.
Big Thaks to all who supported me to found the solution: sarkiroka, Jakub A Suplick

Can you pass an external function to a Vue component as a prop?

I have a legacy codebase mostly built in PHP. I'm researching how to turn commonly used parts of the code into re-usable Vue components that can be plugged in as needed.
In one case, I have an onclick event in the html which will need to be individually passed to a child component. onclick="func()"
I want to be able to pass that func to the component from the markup, without defining this one-time use function as a property method either on the component or its parents.
I can't find anything in the Vue docs or elsewhere on how to do that. Every attempt I make gives an error:
Property or method "hi" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
Is there a way to pass externally-defined functions in the global scope to a Vue instance?
Vue tabs:
Vue.config.devtools = true;
Vue.component('tabs', {
template: `
<div class="qv-tabs">
<div class="tab">
<ul>
<li v-for="tab in tabs"
:class="{'is-active' : tab.isActive}"
#click="tab.callHandler"
>
<a :href="tab.href" #click="selectTab(tab)">{{tab.name}}</a>
</li>
</ul>
</div>
<div class="tab-content">
<slot></slot>
</div>
</div>
`,
data(){
return{
tabs: []
};
},
created(){
this.tabs = this.$children;
},
methods:{
selectTab(selectedTab){
this.tabs.forEach(tab => {
tab.isActive = (tab.name == selectedTab.name);
});
},
otherHi() {
alert('other hi');
}
}
});
Vue.component('tab', {
template: `
<div v-show="isActive">
<slot></slot>
</div>
`,
props: {
name: {required: true},
selected: {default: false},
callHandler: Function,
clickHandler: {
type: Function,
default: function() { console.log('default click handler') }
}
},
data(){
return{
isActive: false
}
},
methods: {
callHandler() {
console.log('call handler called');
this.clickHandler();
}
},
computed:{
href(){
return '#' + this.name.toLowerCase().replace(/ /g, '-');
}
},
mounted(){
this.isActive = this.selected;
}
});
new Vue({
el: '.app',
methods: {
vueHi() { alert('hi from vue'); }
}
});
function hi() {
alert('hi!');
}
Markup:
<div class="app">
<tabs>
<tab name="Tab 1" :selected="true" v-bind:call-handler="hi">
<p>Tab content</p>
</tab>
<tab name="Tab 2">
<p>Different content for Tab 2</p>
</tab>
</tabs>
</div>
You could define your methods like this in your component :
...
methods: {
hi
}
...
But you will have to define it in every component where you need this function. Maybe you can use a mixin that define the methods you want to access from yours components, and use this mixins in these components ? https://v2.vuejs.org/v2/guide/mixins.html
Anoter solution ( depending on what you try to achieve ) is to add your method to the Vue prototype like explained here :
https://v2.vuejs.org/v2/cookbook/adding-instance-properties.html
Vue.prototype.$reverseText = function(string) {
return string.split('')
.reverse()
.join('')
}
With this method defined in the Vue prototype, you can use the reverseText method like this in all of your components template :
...
<div> {{ $reverseText('hello') }} </div>
...
Or from yours script with this :
methods: {
sayReverseHello() {
this.$reverseText('hello')
}
}

Passing custom emited events as props to a new created component in VueJs

My app consists of:
A component named
<consl :output="output" #submit-to-vue><consl>
which contains an input that calls a submit() method when enter key is pressed.
<div>
<output v-html="output"></output>
<div id="input-line" class="input-line">
<div class="prompt">{{ prompt }}</div>
<div>
<input class="cmdline" autofocus
v-model.trim="command"
#keyup.enter="submit"
:readonly="submited" />
</div>
</div>
Then the method submit() emits an event #submit-to-vue to parent method submitv() that create an instance of the same component and adds it to the DOM.
//........
methods: {
submit: function () {
this.$emit('submit-to-vue')
this.submited = true
}
},
and
//......
methods: {
submitv: function () {
var ComponentClass = Vue.extend(consl)
var instance = new ComponentClass({
propsData: { output: this.output }
})
instance.$mount() // pass nothing
this.$refs.container.appendChild(instance.$el)
What I want to accomplish ?
I want to create a new consl component and add it to the DOM every time the old one is submited. (I want my app to emulate a terminal)
The problem
When submitted the new created component does not contain the #submit-to-vue event listener, which make it unable to recall the submitv() method.
Questions
How can I solve this problem ?
Is this the proper way to do things in VueJs or is there a more elegent way ?
In parent component, declare one data property=childs, it will includes all childs already created.
So once parent component receives the event=submit-to-vue, then add one new child to this.childs
Finally uses v-for to render these child components.
The trick: always consider the data-driven way, doesn't manipulate dom directly as possible.
below is one simple demo :
Vue.config.productionTip = false
Vue.component('child', {
template: `
<div>
<div>Label:<span>{{output}}</span></div>
<div>Value:<span>{{command}}</span></div>
<div id="input-line" class="input-line">
<div class="prompt">{{ prompt }}</div>
<div>
<input class="cmdline" autofocus
v-model.trim="command"
#keyup.enter="submit"
:readonly="submited" />
</div>
</div>
</div>`,
props: ['output'],
data() {
return {
submited: false,
command: ''
}
},
computed: {
prompt: function () {
return this.submited ? 'Already submitted, input is ready-only now' : ''
}
},
methods: {
submit: function () {
this.$emit('submit-to-vue')
this.submited = true
}
}
})
app = new Vue({
el: "#app",
data: {
childs: [{'output':'default:'}]
},
methods: {
addChild: function () {
this.childs.push({'output': this.childs.length})
}
}
})
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<div>
<ul>
<li v-for="(child, index) in childs" :key="index">
<child :output="child.output" #submit-to-vue="addChild()"></child>
</li>
</ul>
</div>
</div>

How can I change data value from one component to another component in Vue Js?

I am new in Vue Js. So, I am facing a problem to changes data value from another component.
I have a component A:
<template>
<div id="app">
<p v-on:click="test ()">Something</p>
</div>
</template>
import B from '../components/B.vue';
export default {
components: {
B
},
methods: {
test: function() {
B.data().myData = 124
B.data().isActive = true
console.log(B.data().myData);
console.log(B.data().isActive);
}
}
}
Component B:
export default {
data() {
return {
myData: 123,
isActive: false
}
}
}
It still component B data.
But it cannot be affected component B data. I want to data changes of component B from component A. How can I do that?
Please explain me in details. I have seen vue js props attribute but I don't understand.
You're looking for Vuex.
It's the centralized store for all the data in your applications.
Take a look at their documentation, it should be pretty straightforward.
You can pass down props to the component B. These props can be updated by the parent component. You can think of B as a stupid component that just renders what the parent tells it to rendern. Example:
// Component A
<template>
<div id="app">
<p v-on:click="test ()">Something</p>
<b data="myData" isActive="myIsActive"></b>
</div>
</template>
<script>
import B from '../components/B.vue';
export default {
components: {
B
},
data() {
return {
myData: 0,
myIsActive: false,
};
},
methods: {
test: function() {
this.myData = 123
this.myIsActive = true
}
}
}
</script>
// Component B
<template>
<div>{{ data }}{{ isActive }}</div>
</template>
<script>
export default {
props: {
data: Number,
isActive: Boolean
};
</script>
There are few ways...
if your components have a parent child relationship you can pass data values from parent into child.
If your want to communicate back to parent component when child component has changed something, you can use vuejs event emitter(custom event) to emit a event when data value change and that event can be listened in another component and do what you want.
If your components doesn't have a relationship, then you have to use use something else than above things. You can use two things.one is event bus, other one is state management library.for vue there is a official state management library called VueX.it is very easy to use.if you want to use something else than vuex, you can use it such as redux, mobx etc.
This documentation has everything what you want to know. I don't want to put any code, because of doc is very clear.
VueX is the most preferable way to do this! Very easy to use..
https://v2.vuejs.org/v2/guide/components.html
//component A
Vue.component('my-button', {
props: ['title'],
template: `<button v-on:click="$emit('add-value')">{{title}}</button>`
});
Vue.component('my-viewer', {
props: ['counter'],
template: `<button>{{counter}}</button>`
});
new Vue({
el: '#app',
data: {
counter: 0,
},
methods: {
doSomething: function() {
this.counter++;
}
}
})
Vue.component('blog-post', {
props: ['title'],
template: '<h3>{{ title }}</h3>'
});
//parent
new Vue({
el: '#blog-post-demo',
data: {
posts: [{
id: 1,
title: 'My journey with Vue'
},
{
id: 2,
title: 'Blogging with Vue'
},
{
id: 3,
title: 'Why Vue is so fun'
}
]
}
});
Vue.component('blog-post2', {
props: ['post'],
template: `
<div class="blog-post">
<h3>{{ post.title }}</h3>
<button v-on:click="$emit('enlarge-text')">
Enlarge text
</button>
<div v-html="post.content"></div>
</div>`
})
new Vue({
el: '#blog-posts-events-demo',
data: {
posts: [{
id: 1,
title: 'My journey with Vue'
},
{
id: 2,
title: 'Blogging with Vue'
},
{
id: 3,
title: 'Why Vue is so fun'
}
],
postFontSize: 1
},
methods: {
onEnlargeText: function() {
this.postFontSize++;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<p>Two components adding & viewing value</p>
<div id="app">
<my-button :title="'Add Value'" v-on:add-value="doSomething"></my-button>
<my-viewer :counter="counter"></my-viewer>
</div>
<br>
<br>
<p>Passing Data to Child Components with Props (Parent to Child)</p>
<div id="blog-post-demo">
<blog-post v-for="post in posts" v-bind:key="post.id" v-bind:title="post.title"></blog-post>
</div>
<p>Listening to Child Components Events (Child to Parent)</p>
<div id="blog-posts-events-demo">
<div :style="{ fontSize: postFontSize + 'em' }">
<blog-post2 v-for="post in posts" v-bind:key="post.id" v-bind:post="post" v-on:enlarge-text="onEnlargeText"></blog-post2>
</div>
</div>
First, you need a parent so two component can communicate. when my-button component is clicked triggers an event add-value that calls doSomething() function, then updates the value & show it to my-viewer component.
HTML
<!--PARENT-->
<div id="app">
<!--CHILD COMPONENTS-->
<my-button :title="'Add Value'" v-on:add-value="doSomething"></my-button>
<my-viewer :counter="counter"></my-viewer>
</div>
VUE.JS
//component A
Vue.component('my-button',{
props:['title'],
template:`<button v-on:click="$emit('add-value')">{{title}}</button>`
});
//Component B
Vue.component('my-viewer',{
props:['counter'],
template:`<button>{{counter}}</button>`
});
//Parent
new Vue({
el: '#app',
data:{
counter:0,
},
methods:{
doSomething:function(){
this.counter++;
}
}
})
This is base on Vue Components Guide
Passing Data to Child Components with Props (Parent to Child)
VUE.JS
//component (child)
//Vue component must come first, else it won't work
Vue.component('blog-post', {
/*Props are custom attributes you can register on a component. When a
value is passed to a prop attribute, it becomes a property on that
component instance*/
props: ['title'],
template: '<h3>{{ title }}</h3>'
});
//parent
new Vue({
el: '#blog-post-demo',
data: {
posts: [
{ id: 1, title: 'My journey with Vue' },
{ id: 2, title: 'Blogging with Vue' },
{ id: 3, title: 'Why Vue is so fun' }
]
}
});
HTML:
v-for will loop on posts and pass data to blog-post component
<div id="blog-post-demo">
<blog-post v-for="post in posts"
v-bind:key="post.id"
v-bind:title="post.title"></blog-post>
</div>
Listening to Child Components Events (Child to Parent)
HTML
You must first register the event by v-on:enlarge-text="onEnlargeText" to use $emit and make sure that it's always set to lower case or it won't work properly. example enlargeText and Enlargetext will always be converted to enlargetext, thus use enlarge-text instead, because its easy to read & valid, for a brief explanation about $emit you can read it here
<div id="blog-posts-events-demo">
<div :style="{ fontSize: postFontSize + 'em' }">
<blog-post
v-for="post in posts"
v-bind:key="post.id"
v-bind:post="post"
v-on:enlarge-text="onEnlargeText"></blog-post>
</div>
</div>
VUE.JS
When user clicks the button the v-on:click="$emit('enlarge-text')" will trigger then calling the function onEnlargeText() in the parent
//component (child)
Vue.component('blog-post', {
props: ['post'],
template: `
<div class="blog-post">
<h3>{{ post.title }}</h3>
<button v-on:click="$emit('enlarge-text')">
Enlarge text
</button>
<div v-html="post.content"></div>
</div>`
})
//parent
new Vue({
el: '#blog-posts-events-demo',
data: {
posts: [
{ id: 1, title: 'My journey with Vue' },
{ id: 2, title: 'Blogging with Vue' },
{ id: 3, title: 'Why Vue is so fun' }
],
postFontSize: 1
},
methods:{
onEnlargeText:function(){
this.postFontSize++;
}
}
})
Actually props suck sometimes you got some old external library in jquyer and need just damn pass value. in 99% of time use props that do job but.
A) spend tons of hours debuging changing tones of code to pass variables
B) one line solution
Create main variable in data letmeknow as object {}
this.$root.letmeknow
then somewhere in code from component
this.$root.letmeknow = this;
and then boom i got component console.log( this.$root.letmeknow ) and see now can change some values

Categories

Resources