Vuejs v-model with 2-dimensional array dynamic name - vue.js

How can you name a dynamic v-model with 2 dynamic variables? If I used only 1 dynamic variable like v-model="shirt_count[brand]" it works, however using v-model="shirt_count[brand][color]" does not work, code below for sample:
<template>
<div v-for="brand in brands" >
<div v-for="color in colors" >
<input type="text" v-model="shirt_count[brand][color]" />
</div>
</div>
</template>
<script>
export default {
props: ['brands', 'colors'],
data(){
return {
shirt_count: []
}
}
}
</script>
I want to have an output like below, that's why I need it 2 dimensional:
shirt_count: [
'brand_a': [
'red': 5
'blue': 4
],
'brand_b': [
'red': 1
'blue': 3
]
]

you can use a string template literal with the dynamic variables as placeholders.
<template>
<div v-for="brand in brands">
<div v-for="color in colors">
<input type="text" :value="shirt_count[`${brand}_${color}`]" #input="updateShirtCount($event, brand, color)" />
</div>
</div>
</template>
<script>
export default {
props: ['brands', 'colors'],
data() {
return {
shirt_count: {}
};
},
methods: {
updateShirtCount(event, brand, color) {
this.$set(this.shirt_count, `${brand}_${color}`, event.target.value);
}
}
};
</script>

Related

How to display many times the same component with vuex link inside

I'm new to Vue and Vuex.
I made a component with radio button, and this component is two-way bind with the store.
Now that the component is working, i would like to use it for multiple item by calling it several time, and indicates to it which item of the vuex store it should be bound to. In this way, i'll be able to loop of the array containing all item on which apply the radio button modification.
In my store, I have this array :
state {
[...other data...]
arrayX {
item1: { name : 'a', value : 'nok'}
item2: { name : 'b', value : 'ok}},
[...other data...]
This array is bind with v-model inside the component :
<template>
<div class="radio">
<input type="radio" name="a" value="nok" v-model="arrayX.item1.value" id="nok"/>
<label for="nok"> Nok </label>
</div>
<div class="radio">
<input type="radio" name="a" value="" v-model="arrayX.item1.value" id="empty"/>
<label for="empty">None</label>
</div>
<div class="radio">
<input type="radio" name="a" value="ok_but" v-model="arrayX.item1.value" id="ok_but"/>
<label for="ok_but">Ok but</label>
</div>
<div class="radio">
<input type="radio" name="a" value="ok" v-model="arrayX.item1.value" id="ok"/>
<label for="ok">Ok</label>
</div>
</div>
</template>
<script>
import { mapFields } from 'vuex-map-fields';
export default {
name: 'SwitchColors',
computed:{
...mapFields(['arrayX' ])
}
}
</script>
I created an example scenario in my Vue 2 CLI sandbox app. Each radio button component is bound to the Vuex store array element through a computed property. I pass the store array index value as a prop from the parent to the radio component.
RadioButtonVuex.vue
<template>
<div class="radio-button-vuex">
<hr>
<div class="row">
<div class="col-md-6">
<input type="radio" value="One" v-model="picked">
<label>One</label>
<br>
<input type="radio" value="Two" v-model="picked">
<label>Two</label>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
pickedIndex: {
type: Number,
required: true
}
},
computed: {
picked: {
get() {
return this.$store.state.radioSelections[this.pickedIndex].picked;
},
set(newValue) {
this.$store.commit('updateSelection', { index: this.pickedIndex, newValue: newValue });
}
}
},
}
</script>
<style scoped>
label {
margin-left: 0.5rem;
}
</style>
Parent.vue
<template>
<div class="parent">
<h3>Parent.vue</h3>
<radio-button-vuex :pickedIndex="0" />
<radio-button-vuex :pickedIndex="1" />
</div>
</template>
<script>
import RadioButtonVuex from './RadioButtonVuex.vue'
export default {
components: {
RadioButtonVuex
}
}
</script>
store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
export default new Vuex.Store({
state: {
radioSelections: [
{
picked: 'One'
},
{
picked: 'Two'
}
]
},
mutations: {
updateSelection(state, payload) {
state.radioSelections[payload.index].picked = payload.newValue;
}
}
})

VueJS - Scoped slots and manipulation of data

I have a vuejs component edit-form with a named slot body inside of it and data called model.
<template>
<div>
<pre>{{ model }}</pre>
Name: <input v-model="model.name"><br>
<slot name="body" v-bind="model"></slot>
</div>
</template>
<script>
export default {
data: function() {
return {
model: {
name: "x",
title: "y"
}
}
}
}
</script>
I am using this component in a parent component like so:
<edit-form>
<template v-slot:body="model">
Title: <input v-model="model.title">
</template>
</edit-form>
When I now enter something in the name input field, then the <pre>{{ model }}</pre> tag gets updated. Modifying the title input field has no effect on the <pre>.
See https://codepen.io/bernhardh/pen/KKwEVZo
How to make the title input field work?
You should do in this code some changes! For example
Html code:
<html>
<head></head>
<body>
<div id="container">
<edit-form v-model="model">
<template slot="body">
Title: <input v-model="model.title">
</template>
</edit-form>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</body>
</html>
Vuejs code:
Vue.component('edit-form', {
template : `<div>
<pre>{{ value }}</pre>
Name: <input v-model="value.name"><br>
<slot name="body"></slot>
</div>`,
props:{
value: {
reqired: false,
type: Object
}
},
data: function() {
return {
};
}
});
new Vue({
el: '#container',
data: {
value: '',
model: {
name: "x",
title: "y"
}
},
});

Why can't you add a Vue.js reactive property directly to root model?

Here is some code that uses $set() to add a new reactive prop to the model. It works fine.
<template>
<div id="app">
<div>
Prop1: {{ x.prop1 }}
</div>
<div>
<input type="button" value="Go" #click="go()">
</div>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
x: {}
};
},
methods: {
go() {
this.$set(this.x, 'prop1', 'yay');
}
}
};
</script>
Now, if I remove the x root property and try to add prop1 directly to the this it doesn't work.
<template>
<div id="app">
<div>
Prop1: {{ prop1 }}
</div>
<div>
<input type="button" value="Go" #click="go()">
</div>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
};
},
methods: {
go() {
this.$set(this, 'prop1', 'yay');
}
}
};
</script>
I get that you should do this kind of thing, but I can't figure out why it doesn't work.
As stated in the docs:
The target object cannot be a Vue instance, or the root data object of a Vue instance.
It's a technical limitation.

Send data from one component to another in vue

Hi I'm trying to send data from one component to another but not sure how to approach it.
I've got one component that loops through an array of items and displays them. Then I have another component that contains a form/input and this should submit the data to the array in the other component.
I'm not sure on what I should be doing to send the date to the other component any help would be great.
Component to loop through items
<template>
<div class="container-flex">
<div class="entries">
<div class="entries__header">
<div class="entries__header__title">
<p>Name</p>
</div>
</div>
<div class="entries__content">
<ul class="entries__content__list">
<li v-for="entry in entries">
{{ entry.name }}
</li>
</ul>
</div>
<add-entry />
</div>
</div>
</template>
<script>
import addEntry from '#/components/add-entry.vue'
export default {
name: 'entry-list',
components: {
addEntry
},
data: function() {
return {
entries: [
{
name: 'Paul'
},
{
name: 'Barry'
},
{
name: 'Craig'
},
{
name: 'Zoe'
}
]
}
}
}
</script>
Component for adding / sending data
<template>
<div
class="entry-add"
v-bind:class="{ 'entry-add--open': addEntryIsOpen }">
<input
type="text"
name="addEntry"
#keyup.enter="addEntries"
v-model="newEntries">
</input>
<button #click="addEntries">Add Entries</button>
<div
class="entry-add__btn"
v-on:click="openAddEntry">
<span>+</span>
</div>
</div>
</template>
<script>
export default {
name: 'add-entry',
data: function() {
return {
addEntryIsOpen: false,
newEntries: ''
}
},
methods: {
addEntries: function() {
this.entries.push(this.newEntries);
this.newEntries = '';
},
openAddEntry() {
this.addEntryIsOpen = !this.addEntryIsOpen;
}
}
}
</script>
Sync the property between the 2:
<add-entry :entries.sync="entries"/>
Add it as a prop to the add-entry component:
props: ['entries']
Then do a shallow merge of the 2 and emit it back to the parent:
this.$emit('entries:update', [].concat(this.entries, this.newEntries))
(This was a comment but became to big :D)
Is there a way to pass in the key of name? The entry gets added but doesn't display because im looping and outputting {{ entry.name }}
That's happening probably because when you pass "complex objects" through parameters, the embed objects/collections are being seen as observable objects, even if you sync the properties, when the component is mounted, only loads first level data, in your case, the objects inside the array, this is performance friendly but sometimes a bit annoying, you have two options, the first one is to declare a computed property which returns the property passed from the parent controller, or secondly (dirty and ugly but works) is to JSON.stringify the collection passed and then JSON.parse to convert it back to an object without the observable properties.
Hope this helps you in any way.
Cheers.
So with help from #Ohgodwhy I managed to get it working. I'm not sure if it's the right way but it does seem to work without errors. Please add a better solution if there is one and I'll mark that as the answer.
I follow what Ohmygod said but the this.$emit('entries:update', [].concat(this.entries, this.newEntries)) didn't work. Well I never even need to add it.
This is my add-entry.vue component
<template>
<div
class="add-entry"
v-bind:class="{ 'add-entry--open': addEntryIsOpen }">
<input
class="add-entry__input"
type="text"
name="addEntry"
placeholder="Add Entry"
#keyup.enter="addEntries"
v-model="newEntries"
/>
<button
class="add-entry__btn"
#click="addEntries">Add</button>
</div>
</template>
<script>
export default {
name: 'add-entry',
props: ['entries'],
data: function() {
return {
addEntryIsOpen: false,
newEntries: ''
}
},
methods: {
addEntries: function() {
this.entries.push({name:this.newEntries});
this.newEntries = '';
}
}
}
</script>
And my list-entries.vue component
<template>
<div class="container-flex">
<div class="wrapper">
<div class="entries">
<div class="entries__header">
<div class="entries__header__title">
<p>Competition Entries</p>
</div>
<div class="entries__header__search">
<input
type="text"
name="Search"
class="input input--search"
placeholder="Search..."
v-model="search">
</div>
</div>
<div class="entries__content">
<ul class="entries__content__list">
<li v-for="entry in filteredEntries">
{{ entry.name }}
</li>
</ul>
</div>
<add-entry :entries.sync="entries"/>
</div>
</div>
</div>
</template>
<script>
import addEntry from '#/components/add-entry.vue'
import pickWinner from '#/components/pick-winner.vue'
export default {
name: 'entry-list',
components: {
addEntry,
pickWinner
},
data: function() {
return {
search: '',
entries: [
{
name: 'Geoff'
},
{
name: 'Stu'
},
{
name: 'Craig'
},
{
name: 'Mark'
},
{
name: 'Zoe'
}
]
}
},
computed: {
filteredEntries() {
if(this.search === '') return this.entries
return this.entries.filter(entry => {
return entry.name.toLowerCase().includes(this.search.toLowerCase())
})
}
}
}
</script>

Vue.js - component that binds a binary string to checkbox inputs

In Vue.js, how would I write a component that binds to a string of 1s and 0s, such as "111001011", and represents this data to the user as a set of named checkboxes?
Maybe this is what you'r looking for:
App.vue
<template>
<div id="app">
<checkboxes :binary="binary"></checkboxes>
</div>
</template>
<script>
import Checkboxes from './components/Checkboxes'
export default {
name: 'app',
data(){
return {
binary: "11001011"
};
},
components: {
Checkboxes
}
}
</script>
Checkboxes.vue:
<template>
<div>
<ul>
<li v-for="position in binary.length">
<label>
<input type="checkbox" :name="binary[position - 1]" :checked="binary[position - 1] == '1' ? true : false"> {{ binary[position - 1] }}
</label>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'checkboxes',
props: {
binary: {
required: true,
type: String
}
}
}
</script>
This will go through the string length and for each character will mark as checked/unchecked based on the binary value(1/0)
Result: