Cannot render multiple components in Vue - javascript

I'm just starting with Vue and I have defined 2 components, however, I cannot render them in the same 'Vue' instance.
Here are my 2 components:
Vue.component('mat-example', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
Next, I do have the 'Vue' entry point defined:
var app = new Vue({
el: '#app'
});
And in my HTML, I do have the following code:
<div id="app">
<button-counter />
<mat-example />
</div>
The 'Vue' development tools does only show the 'button-counter' component:
If I remove the 'button-counter', the 'mat-example' is showed up in the Vue Developer Tools.
How does it come that I cannot render those 2 components in my Vue entry point?

this is going to work:
<div id="app">
<button-counter></button-counter>
<mat-example></mat-example>
</div>
demo:
Vue.component('mat-example', {
data: function() {
return {
count: 0
}
},
template:
'<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
Vue.component('button-counter', {
data: function() {
return {
count: 0
}
},
template:
'<button v-on:click="count++">You clicked me {{ count }} times</button>'
})
var app = new Vue({
el: '#app'
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<button-counter></button-counter>
<mat-example></mat-example>
</div>

Use this tag instead of using self closing tag
<div id="app">
<button-counter></button-counter>
<mat-example></mat-example>
</div>

Related

How do you update a vue interpolation using an onclick method?

I am struggling to update the interpolation of thing.
This simple function should change the value of the data but instead does nothing.
Chrome logs the change of thing in the console but the HTML does not update.
<template>
<div class="container-xl pb-5">
<button class="mb-2 btn-lg selected-dropdown" #click="dropSelect('B')">
{{ thing }}
</button>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
data() {
return {
thing: "A",
};
},
methods: {
dropSelect(thing: any) {
console.log(thing);
return this.thing;
},
},
});
</script>
Try to update your data property instead of returning:
dropSelect(thing: any) {
this.thing = thing
},
One small observation : Use this.thing = thing instead of return this.thing
Demo :
new Vue({
el: '#app',
data: {
thing: "A"
},
methods: {
dropSelect(thing) {
this.thing = thing;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="dropSelect('B')">
{{ thing }}
</button>
</div>

Vue state not updated with default injected value

If you click the button, you can see the value of state updated in the console, but it isn't updated in the page output. How can I make it work with a default injected value?
const Component = {
inject: {
state: {
default: () => ({
example: 1
})
}
},
template: `<div>
<div>{{ state }}</div>
<button #click="click">click</button>
</div>`,
methods: {
click() {
this.state.example += 1
console.log(this.state)
}
}
}
new Vue({
el: "#app",
components: {
Component
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<component></component>
</div>
Is it related to as Vue docs say "Note: the provide and inject bindings are NOT reactive. This is intentional. However, if you pass down an observed object, properties on that object do remain reactive."? I'm confused about the difference between the BINDINGS not being reactive but the OBSERVED OBJECT being reactive. Could you show an example to demo the difference?
Sorry, but it is not clear what you want - where's the provider for the "injection"? Why do you use inject in the same component as you use the value itself?
Here's your code, without inject:
1. Use the data attribute
const Component = {
data() {
return {
state: {
example: 1
}
}
},
template: `<div>
<div>{{ state }}</div>
<button #click="click">click</button>
</div>`,
methods: {
click() {
this.state.example += 1
console.log(this.state)
}
}
}
new Vue({
el: "#app",
components: {
Component
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<component></component>
</div>
Just use the data attribute - you can have a default value for example.
2. Use injection
inject is something completely different - it's a way to pass values from a provider to a consumer:
const Component = {
inject: ['state1'],
data() {
return {
state: {
example: 1
}
}
},
template: `<div>
<div>injected: {{ state1 }}</div>
<div>{{ state }}</div>
<button #click="click">click</button>
</div>`,
methods: {
click() {
this.state.example += 1
console.log(this.state)
}
}
}
new Vue({
el: "#app",
provide: {
state1: {
example1: 1
}
},
components: {
Component
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<component></component>
</div>
You can "skip" levels of components and use the provided value in components where you inject it - you don't have to pass it all the way down as props.
3. Create reactive inection
If you want to have reactive injection then you need to pass down something more complex:
const Component1 = {
inject: ['state2'],
data() {
return {
state: {
example: 1
}
}
},
template: `<div>
<div>injected: {{ state2.state2P }}</div>
<div>{{ state }}</div>
<button #click="click">click</button>
</div>`,
methods: {
click() {
this.state.example += 1
console.log(this.state)
}
}
}
new Vue({
el: "#app",
data() {
return {
state2: {
example2: 1
}
}
},
provide() {
// create an object (state2)
const state2 = {}
// define a property on the object (state2P), that
// has a get() function that always gets the provider's
// value you want to inject
Object.defineProperty(state2, 'state2P', {
enumerable: true,
get: () => this.state2,
})
// return the created object (with a property that always
// gets the value in the parent)
return {
state2
}
},
components: {
Component1
},
methods: {
parentClick() {
this.state2.example2 += 1
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<component1></component1>
<button #click="parentClick">PARENT CLICK</button>
</div>
I added a button to the template, so you can see that a method defined in the provider component's scope changes the value displayed in the consumer component's scope. (Also had to change the component's name, as Vue started to "whine" about using a restricted word.)

Unable to pass data to vue template

I am trying to pass the name from data to my settingTemplate.
However, I have not been able to make it work. Could anyone advice how I should approach this? I am new to Vue.
app.js
var app = new Vue({
el: '#app',
components: {
settingTemplate
},
data: {
name: 'Elon'
}
})
setting-template.js
const settingTemplate = {
template:
`<div class="d-flex flex-column text-md-center">
<div class="p-2">
<b-btn id="popoverButton-disable" variant="primary"></b-btn>
</div>
<div class="p-2">
<b-popover :disabled.sync="disabled" target="popoverButton-disable" title="{{name}}">
<a>Profile</a> | <a>Logout</a>
</b-popover>
</div>
</div>`
,
data() {
return {
disabled: false
}
},
methods: {
disableByRef() {
if (this.disabled){
this.$refs.popover.$emit('enable')
}else{
this.$refs.popover.$emit('disable')
}
}
},
props: ['name']
}
You should pass name to your component by attribute, not by data:
<div id="app">
<setting-template name="Elon" />
</div>
Here you can check working example:
https://codepen.io/anon/pen/dwrOqY?editors=1111
data is component private data - it's not passed to child components.
If you still need name in your main component data you can pass it to the child component like this:
<div id="app">
<setting-template :name="name" />
</div>
var app = new Vue({
el: '#app',
components: {
settingTemplate
},
data: {
name: 'Leon'
}
})
https://codepen.io/anon/pen/qLvqeO?editors=1111

how to render component from a component in vue js

I have a component with following template:
<div v-for:"item in store" v-bind:key="item.type">
<a>{{item.type}}</a>
</div>
I have another component called 'StoreComponent'
On click of a element in first component I want to clear the current component and show the StoreComponent and able to pass item.type to StoreComponent.
I don't want to use router-link or router.push as I don't want to create a new url but override the current component with the new one depending on the item.type value.
StoreComponent.vue
export default{
name: 'StoreComponent',
props: ['item'],
data: function () {
return {
datum: this.item
}
},
methods: {
//custom methods
}
}
You could use dynamic components and pass the item-type as a prop.
Vue.component('foo', {
name: 'foo',
template: '#foo'
});
Vue.component('bar', {
name: 'bar',
template: '#bar',
props: ['test']
});
new Vue({
el: "#app",
data: {
theComponent: 'foo', // this is the 'name' of the current component
somethingWeWantToPass: {
test: 123 // the prop we are passing
},
},
methods: {
goFoo: function() {
this.theComponent = 'foo';
},
goBar: function() {
this.theComponent = 'bar';
},
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<button #click="goFoo">Foo</button>
<button #click="goBar">Bar</button>
<component :is="theComponent" v-bind="somethingWeWantToPass"></component>
</div>
<template id="foo">
<div>
Foo
</div>
</template>
<template id="bar">
<div>
Bar
<div>This is a prop: {{ this.test }}</div>
</div>
</template>

Is it possible to append a component on a button press?

Here's a problem that I want to solve:
I have a button that when is pressed must append my-component to a dom.
If it is pressed 2 times there must be 2 <p> tegs. How can I achieve this?
js:
<script>
Vue.component('my-component', {
template: "<p>hello</p>",
})
var vue = new Vue({
el: "#App",
data: {},
methods: {
append: function() {
// unknown code here
}
}
})
</script>
html:
<div id = "App">
<button #click="append" class="btn btn-primary">Spawn stuff!</button>
</div>
Here is one way you could do that. This code iterates over a counter using v-for to iterate over a range.
Vue.component('my-component', {
template: "<p>hello</p>",
})
var vue = new Vue({
el: "#App",
data: {
hellocount: 0
},
methods: {
append: function() {
// unknown code here
this.hellocount++
}
}
})
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<div id="App">
<my-component v-for="n in hellocount" :key="n"></my-component>
<button #click="append" class="btn btn-primary">Spawn stuff!</button>
</div>
This is a little atypical; normally you will drive the components rendered from actual data, as #RoyJ suggests in your comments.
From your comment below, you could build a form something like this.
Vue.component('my-input', {
props:["value", "name"],
data(){
return {
internalValue: this.value
}
},
methods:{
onInput(){
this.$emit('input', this.internalValue)
}
},
template: `
<div>
{{name}}:<input type="text" v-model="internalValue" #input="onInput">
</div>
`,
})
var vue = new Vue({
el: "#App",
data: {
form:{
name: null,
email: null,
phone: null
}
},
methods:{
append(){
const el = prompt("What is the name of the new element?")
this.$set(this.form, el, null)
}
}
})
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<div id="App">
<my-input v-for="(value, prop) in form"
:key="prop"
v-model="form[prop]"
:name="prop">
</my-input>
<button #click="append">Add New Form Element</button>
<div>
Form Values: {{form}}
</div>
</div>
The code defines a form object and iterates over the properties of the form to render inputs for each property.
This is obviously extremely naive, handles only input texts, etc. But hopefully you get the idea.

Categories

Resources