How can I unify text that take from multiple group radio button? (vue.js 2) - radio-button

My vue component like this :
<template>
<div>
<div class="col-md-4">
<ul class="list-unstyled">
<li><strong>England</strong></li>
<li>
<div class="checkbox">
<label>
<input type="radio" name="england" class="radio"> Chelsea
</label>
</div>
</li>
<li>
<div class="checkbox">
<label>
<input type="radio" name="england" class="radio"> Mu
</label>
</div>
</li>
<li>
<div class="checkbox">
<label>
<input type="radio" name="england" class="radio"> Arsenal
</label>
</div>
</li>
</ul>
</div>
<div class="col-md-4">
<ul class="list-unstyled">
<li><strong>Spain</strong></li>
<li>
<div class="checkbox">
<label>
<input type="radio" name="spain" class="radio"> Madrid
</label>
</div>
</li>
<li>
<div class="checkbox">
<label>
<input type="radio" name="spain" class="radio"> Barcelona
</label>
</div>
</li>
<li>
<div class="checkbox">
<label>
<input type="radio" name="spain" class="radio"> Atletico
</label>
</div>
</li>
</ul>
</div>
<div class="col-md-4">
<ul class="list-unstyled">
<li><strong>Italy</strong></li>
<li>
<div class="checkbox">
<label>
<input type="radio" name="italy" class="radio"> Juve
</label>
</div>
</li>
<li>
<div class="checkbox">
<label>
<input type="radio" name="italy" class="radio"> Milan
</label>
</div>
</li>
<li>
<div class="checkbox">
<label>
<input type="radio" name="italy" class="radio"> Inter
</label>
</div>
</li>
</ul>
</div>
<span id="result-select">Result</span>
</div>
</template>
<script>
export default {
props: ['...'],
...
}
</script>
My javascript code like this :
$('input[type="radio"]').click(function(){
var $radio = $(this);
var name = $(this).prop("name");
// if this was previously checked
if ($radio.data('waschecked') == true)
{
$radio.prop('checked', false);
$radio.data('waschecked', false);
$('#result-select').text('');
}
else{
$radio.data('waschecked', true);
$(`input[name=${name}]:not(:checked)`).data('waschecked', false);
$('#result-select').text(txt);
}
let output = [];
var txt;
$('input[type="radio"]:checked').each( function() {
txt = $(this).parent().text();
output.push(txt);
});
$('#result-select').text(output.join(' - '));
});
I still use javascript to unify each selected radio button group. This javascript code is working fine. But I want to change it using vue component. So there is no javascript code. But I am still confused
I will explain the plot first
I want to :
If I select chelsea, madrid and juve the result like this :
Chelsea - Madrid - Juve
If I select chelsea and madrid the result like this :
Chelsea - Madrid
So if I check radio button, it display the text. If I uncheck radio button, it not display the text
How can I do it in vue component?
Update :
The radio button can be unchecked
For example :
If I select chelsea, madrid and juve the result like this :
Chelsea - Madrid - Juve
Then I uncheck madrid, the result like this :
Chelsea - Juve

Any time you need a value based on other values, you should look at using a computed. In this case, a computed that goes through the country data and picks out the selected city names and returns them as a list.
computed: {
selecteds() {
return this.countries
.map(c => c.selected)
.filter(s => s);
}
},
Check-and-uncheck functionality is supplied by v-model on the radio inputs. If the value of what is bound to v-model matches the value bound to value, the button is checked; if not, not.
I used a settable computed to handle the case where you're clicking an already-clicked button. If it is already selected, set the value to null to unselect it. Because I'm in a component, "set the value to null" is done by emitting an event that the parent uses to set the value.
computed: {
proxySelected: {
get() {
return this.data.selected;
},
set(newValue) {
this.$emit('update', this.data.name, this.data.selected === newValue ? null : newValue);
}
}
}
function countryData(name, cities) {
return {
name,
cities,
selected: null
};
}
new Vue({
el: '#app',
data: {
countries: [
countryData('England', ['Chelsea', 'MU', 'Arsenal']),
countryData('Spain', ['Madrid', 'Barcelona', 'Atletico']),
countryData('Italy', ['Juve', 'Milan', 'Inter'])
]
},
computed: {
selecteds() {
return this.countries
.map(c => c.selected)
.filter(s => s);
}
},
methods: {
update(countryName, newSelectedValue) {
this.countries.find(c => c.name === countryName).selected = newSelectedValue;
}
},
components: {
country: {
template: '#country-template',
props: ['data'],
data() {
return {
checked: []
};
},
computed: {
proxySelected: {
get() {
return this.data.selected;
},
set(newValue) {
this.$emit('update', this.data.name, this.data.selected === newValue ? null : newValue);
}
}
}
}
}
});
.list-unstyled {
list-style: none;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="app">
<country v-for="country in countries" :data="country" #update="update" :key="country.name"></country>
<div>{{selecteds.join(' - ')}}</div>
</div>
<template id="country-template">
<div class="col-md-4">
<ul class="list-unstyled">
<li><strong>{{data.name}}</strong></li>
<li v-for="city in data.cities">
<div class="checkbox">
<label>
<input type="radio" :name="data.name" class="radio" :value="city" v-model="proxySelected">
{{city}}
</label>
</div>
</li>
</ul>
</div>
</template>

Use v-model to do this:
<input type="checkbox" v-model="italy">
And add vue will create an array for italy which you can output in your vue template like {{ italy.join(' - ') }}

Related

Nested Scoped Slots Variable Collisions VueJS

I've created a simple scoped slot component that I need to nest, but I'm struggling to figure out how I can avoid naming collisions.
Vue Component nested-fields
<script>
export default {
props: [
"entityName",
"items"
],
data: function() {
return {
formItems: this.items
}
},
methods: {
addItem: function() {
this.items.push({})
},
removeItem: function(index) {
if (this.items[index].id) {
this.$set(this.items[index], '_destroy', true);
} else {
this.items.splice(index, 1);
}
}
}
}
</script>
<template>
<div class="nested-fields">
<div v-show="item._destroy !== true || typeof item._destroy == 'undefined'" class="card nested-fields__field-set mb-2" v-for="(item, index) in formItems" :key="index">
<div class="card-header d-flex justify-content-between">
<span>Add {{entityName}}</span> <span class="fa fa-times" #click="removeItem(index)"></span>
</div>
<div class="card-body">
<slot name='item-fields' v-bind="{item, index}"></slot>
</div>
</div>
<button class="btn btn-primary btn-xs mb-2" type="button" #click="addItem()">Add {{entityName}}</button>
</div>
</template>
HTML
<nested-fields entity-name="Rotap Analysis" :items="[]">
<template #item-fields="{item, index}">
<div class="form-group col-sm-4">
<label>
Amount (g)
<input type="number" min="0" v-model="item.amount_grams" :name="'setup[input_material_attributes][rotap_analysis_attributes]['+index+'][amount_grams]'" class="form-control">
</label>
</div>
<div class="form-group col-sm-4">
</div>
<div class="form-group col-sm-4">
</div>
<nested-fields entity-name="Sieve" :items="[]">
<template #item-fields="{item2, index2}">
<label>
Sieve Size (US Mesh)
<input type="number" min="0" v-model="item2.size_mesh" :name="'setup[input_material_attributes][rotap_analysis_attributes]['+index+'][rotap_sieves]['+index2+'][size_mesh]'" class="form-control">
</label>
</template>
</nested-fields>
</template>
</nested-fields>
I need to rename the variables in the nested template shown here:
<nested-fields entity-name="Sieve" :items="item.rotap_sieves || []">
<!-- this line --><template #item-fields="{item2, index2}">
So I can use them here:
<input type="number" min="0" v-model="item2.size_mesh" :name="'setup[input_material_attributes][rotap_analysis_attributes]['+index+'][rotap_sieves]['+index2+'][size_mesh]'" class="form-control">
... BUT it does not let me rename the destructuring as I have it from "item" and "index" to "item2" and "index2".
For what it's worth, I'm attempting to replace Cocoon rails gem for nesting forms in my Rails app, though that shouldn't really matter.
Question - How can I rename the variables in nested scoped slots to avoid variable collisions?
I figured out it's a simple destructuring syntax solution:
<template #item-fields="{item: item2, index: index2}">
Works perfectly as expected!

Actively Searching For A String & Replacing it

I've got input fields on one side of the page which is then displayed into boxes on the other side that actively shows what a user has inputted but in an organized way. I need to actively search the input fields for a specific string, in this case 'key' and then it would instantly change to a value stored in data. I've got a searchkeyword() function that should go through the array of objects where input fields are stored but haven't made it work just yet.
For example, if user types in 'key this is david' in input1 then it would change 'key' to it's stored value which is 'hello'. The value of key is also changing if a user clicks on other options. Really don't know where to go from here so any input helps :)
var app = new Vue({
el: '#app',
data: {
activeKeyword: 'HELLO',
inputs: [
{
input1: 'oranges',
input2: 'Online',
input3: 'Free'
}
]
},
methods: {
searchKeyword() {
for(var x = 0; x < this.ads.length; x++){
for(var input in this.inputs[x]){
if(this.ads[x] !== "boolean"){
this.ads[x][input] = String(this.inputs[x][input]).replace(/_keyword_/g, this.activeKeyword)
}
}
}
}
}
})
<link href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="box" v-for="(key, index) in inputs">
<div>
<span class="headline-ok">{{key.input1}} | </span>
<span class="headline-ok">{{key.input2}} | </span>
<span class="headline-ok">{{key.input3}}</span>
<br>
</div>
</div>
<div class="box" v-for="(key, index) in inputs">
<div class="form-inputs">
<label class="label is-capitalized">Input 1</label>
<div class="field">
<div class="control is-expanded">
<input class="input" type="text" v-model="key.input1">
</div>
</div>
</div>
<div class="form-inputs">
<label class="label is-capitalized">Headline Two </label>
<div class="field">
<div class="control is-expanded">
<input type="text" v-model="key.input2" class="input">
</div>
</div>
</div>
<div class="form-inputs">
<label class="label is-capitalized">Headline Three </label>
<div class="field">
<div class="control is-expanded">
<input type="text" v-model="key.input3" class="input">
</div>
</div>
</div>
</div>
</div>
Use a filter method to search for the matching substring of each input:
new Vue({
el: '#app',
filters: {
keyword(value, key, replacer) {
return (value && value.includes(key)) ? value.replace(key, replacer) : value
}
},
data() {
return {
replacer: 'Hello',
inputs: [{
key: 'foo',
model: null
},
{
key: 'bar',
model: null
},
{
key: 'baz',
model: null
}
],
demo: '',
demoString: 'Watch as blah is replaced with Hello'
}
},
mounted () {
let index = 0
setInterval(() => {
this.demo += this.demoString.charAt(index)
if (this.demo === this.demoString) {
this.demo = ''
index = 0
} else {
index++
}
}, 250)
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<template v-for="(i, j) in inputs">
<label>Replace {{ i.key }} with {{ replacer }}</label>
<input v-model="i.model" :key="`input-${j}`">
<p :key="`p-${j}`">{{ i.model | keyword(i.key, replacer) }}</p>
</template>
<hr/>
<label>{{ this.demoString }}</label>
<input v-model="demo">
<p>{{ demo | keyword('blah', 'Hello') }}</p>
</div>

Can I send more than only value with v-model to v-for

I am new to Vue but I think that this can be really useful.
I have my first problem this simplified example
var appvue = new Vue({
el: '#appvue',
data: {
selectedProd: []
}
});
body {
background: #20262E;
}
.form-row{
width:24%;
display:inline-block;
}
#appvue {
background: #fff;
padding:10px;
}
#prod_adjust{
margin-top:20px;
background: #eee
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js"></script>
<div class="prod-wrap" id="appvue">
<div class="form-row">
<input v-model="selectedProd" name="product-1260" id="product-1260" type="checkbox" value="1260">
<label for="product-1260">
<div class="thumb">
<img src="https://picsum.photos/100/100">
</div>
<div class="details">
<span class="brand">Brand 1</span>
<span class="product-title">Product 1</span>
</div>
</label>
</div>
<div class="form-row">
<input v-model="selectedProd" name="product-1261" id="product-1261" type="checkbox" value="1261">
<label for="product-1261">
<div class="thumb">
<img src="https://picsum.photos/100/100">
</div>
<div class="details">
<span class="brand">Brand 2</span>
<span class="product-title">Product 2</span>
</div>
</label>
</div>
<div class="form-row">
<input v-model="selectedProd" name="product-1263" id="product-1263" type="checkbox" value="1263">
<label for="product-1263">
<div class="thumb">
<img src="https://picsum.photos/100/100">
</div>
<div class="details">
<span class="brand">Brand 3</span>
<span class="product-title">Product 3</span>
</div>
</label>
</div>
<div class="form-row">
<input v-model="selectedProd" name="product-1264" id="product-1264" type="checkbox" value="1264">
<label for="product-1264">
<div class="thumb">
<img src="https://picsum.photos/100/100">
</div>
<div class="details">
<span class="brand">Brand 4</span>
<span class="product-title">Product 4</span>
</div>
</label>
</div>
<div id="prod_adjust">
<p v-for="product in selectedProd">{{product}}</p>
</div>
</div>
I have a lot of products like the checkboxes above. I need to have the list of checked items in another place.
I did this with v-model and v-for - but my main problem is now that there are sending only the value from the checkboxes - I also need img-src, brand, product-title - all these parameters I also can have more attributes in an input.
But how I can pass them in data: { selectedProd:[]} with Vue?
Or I do I need to create a separate JS function which will collect all this data and send it to the array selectedProd?
Thank you in advance
You would want to create an object with the data you need for the product. Then you can use v-for to loop through and display them the way you'd like.
In the example, I included a computed function that automatically returns the ones that get checked. This is done by v-model binding the checkbox to a flag inside your object. In this example, I called it selected, for simplicity.
var appvue = new Vue({
el: '#appvue',
data: {
products: [{
id: 1260,
brand: "Brand A",
product: "Bubblegun",
image: "https://picsum.photos/100/100",
selected: false
}, {
id: 1261,
brand: "Brand B",
product: "Bubblegum",
image: "https://picsum.photos/100/100",
selected: false
}],
},
computed: {
selectedProd() {
return this.products.map(product => {
if (product.selected) return product
})
}
}
})
body {
background: #20262E;
}
.form-row {
width: 24%;
display: inline-block;
}
#appvue {
background: #fff;
padding: 10px;
}
#prod_adjust {
margin-top: 20px;
background: #eee
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.min.js"></script>
<div class="prod-wrap" id="appvue">
<div class="form-row" v-for="product in products">
<input v-model="product.selected" name="product-1260" id="product-1260" type="checkbox">
<label for="product-1260">
<div class="thumb">
<img :src="product.image">
</div>
<div class="details">
<span class="brand">{{product.brand}}</span>
<span class="product-title">{{product.product}}</span>
</div>
</label>
</div>
<div id="prod_adjust">
<p v-for="product in selectedProd">{{product}}</p>
</div>
</div>
If you have the input as a JSON which has all the properties you are asking for (product_ID, img src, brand name) then you can assign the object to v-model instead of product_ID.
Your JSON array should look like this,
productsArray: [
{ productID: 1260,
product-title: "Product 1",
brand: "Brand 1",
img: "https://picsum.photos/100/100"
},
{ productID: 1261,
product-title: "Product 2",
brand: "Brand 2",
img: "https://picsum.photos/100/100"
},]
Then inside v-for you can read each object and when selected you can assign the entire object into selectedProd
I used next way finally
vueproducts = function(){
appvue.selectedProd = [];
var tempprod = {};
$('.form-row input').each(function(){
if ($(this).is(":checked")){
tempprod.id = $(this).val();
tempprod.img = $(this).parent().find('img').attr('src');
tempprod.title = $(this).parent().find('.product-title').html();
appvue.selectedProd.push(tempprod);
tempprod = {};
};
});
}

Not getting correct value from click method

I am trying to get the value from a data controlled by v-model within a component.
The data changes in the app and in Vue dev tools but if I console log that data I always get the previous value.
Vue App
Here is the data I'm passing to the components
new Vue({
el: "#app",
data: {
"children": {
"haveAnyOtherDependents": 'yes',
"havePets": 'no',
},
},
})
Vue Component
Here I pass the data I need from the app
// button-group component
Vue.component('button-group', {
props: {
title: {
type: String
},
name: {
type: String
},
content: {
type: String
},
dataSource: {
type: String
}
},
data: function () {
return {
myTitle: this.title,
myName: this.name,
myContent: ''
}
},
created(){
this.myContent = this.setMyContent
},
methods: {
sendData: function(){
console.log('data: ', this.myContent, 'source', this.dataSource);
}
},
computed: {
setMyContent: function(){
return this.content
}
},
template: `
<div class="form-group row mb-1">
<br>
<div class="col-sm-6 pt-1 text-md" v-text="myTitle + ' = ' + myContent"></div>
<div class="col-sm-6 text-right">
<div class="btn-group m-0">
<label class="btn mr-05 p-0 rounded-left">
<input type="radio" :name="myName" value="no" v-model="myContent" #click="sendData()">
<span class="d-block p-1 px-3 text-sm rounded-left">
<i class="fas fa-check mr-1 text-xs"></i>No
</span>
</label>
<label class="btn p-0 rounded-right">
<input type="radio" :name="myName" value="yes" v-model="myContent" #click="sendData()">
<span class="d-block p-1 px-3 text-sm rounded-right">
<i class="fas fa-check mr-1 text-xs"></i>Yes
</span>
</label>
<label class="d-none btn p-0">
<input class="d-none" type="radio" :name="myName" value="unknown" v-model="myContent" #click="sendData()" checked>
<span class="d-block p-1 px-3 text-sm">
<i class="fas fa-check d-none mr-1 text-xs"></i>Unknown
</span>
</label>
</div>
</div>
</div>
`
});
// end button-group
HTML
<div id="app" class="container">
<button-group title="Other Dependants" name="other_dependents"
:content="children.haveAnyOtherDependents" data-source="children.haveAnyOtherDependents"></button-group>
<button-group title="Pets" name="pets" :content="children.havePets" data-source="children.havePets"></button-group>
</div>
Here is a fiddle, https://jsfiddle.net/yktoL8oz/
As answered in the comments by #Bert
The click event fires before the model is updated. Use a different event like change"
I changed the #click="sendData()" to #change="sendData()" and that solved the issue.

Vue 2 Vuex - update values via form or add new item

I have following template:
<template>
<div class="is-half">
<form #submit.prevent="save">
<input type="hidden" name="bookID" :value="book.id">
<div class="field">
<label class="label">Title</label>
<div class="control">
<input class="input" type="text" placeholder="Title" :value="book.title">
</div>
</div>
<div class="control">
<div class="select">
<select>
<option
v-for="author in this.$store.state.authors"
:value="author.name"
:selected="author.name == book.author"
>{{ author.name }}</option>
</select>
</div>
</div>
<div class="field">
<label class="label">Description</label>
<div class="control">
<textarea class="textarea" placeholder="Description" :value="book.description"></textarea>
</div>
</div>
<div class="control">
<button class="button is-primary">Submit</button>
</div>
</form>
</div>
</template>
<script>
export default {
data() {
return {
book : {
id: null,
title: '',
isbn: '',
author: '',
description: '',
added: ''
}
}
},
methods: {
save(book) {
console.log(this.book);
}
},
created() {
if(this.$store.state.book != 'undefined'){
this.book = Object.assign({}, this.$store.state.book);
}
},
computed: {}
}
</script>
<style></style>
I am trying to update the value of selected item, but whenever I press save, the object has the same values which it gets on load.
How can I update values if the I load new object, or insert new object if id is null?
If i understand your question, the problem is that when you type something in the input, it doesn't update the model.
The problem is you're using :value to bind the values and this is a one-way binding. For 2 way binding replace all :value with v-model: v-model="book.title"