VueJs get value input radio in request axios - vue.js

Hello i have this object
values: {
false: 'order expected',
true: 'order cancel'
}
I'm loop this object inside radio button i wan't to get the value (true or false) in radio button to add in my request axios i'm do that
<div
v-for="(index, type) in values"
:key="type.id"
class="filters__filter-items__type"
>
<radio-button
v-model="order"
checked-color="black"
class="filters__filter-items__type__radio"
:value="index"
:name="type"
:label="type"
/>
</div>
<button #click="applyFilters"> send </button>
export default {
data() {
return {
order:''
}
},
methods: {
async applyFilters() {
const app = { $axios: this.$axios };
const results = await endPoint.searchOrder(
app,
this.order
);
return results;
},
}
}
when i do that my request params is &params=order expected not &params=false
I don't understand why please help me thanks

When iterating an object with v-for, the first argument is the value, and the second is the key:
<div v-for="(index, type) in values">
👆value 👆key
You're currently binding the object value (i.e., order canceled, etc.) to the radio button's value, but the binding should actually use the object key (i.e., false).
A quick fix is to swap the index variable for type in the v-for:
<!-- BEFORE -->
<div v-for="(index, type) in values">
<!-- AFTER -->
<div v-for="(type, index) in values">
...or in your bindings:
<!-- BEFORE -->
<radio-button v-model="order"
:value="index"
:name="type"
:label="type"
/>
<!-- AFTER -->
<radio-button v-model="order"
:value="type"
:name="index"
:label="index"
/>
demo

Related

Problem with checking ID in filter function inside a method

I work on CRUD based on Vuejs. I have three components Goods.vue, UpdateGood.vue, App.vue
My Problem is in the "updateGood" method inside App.vue . I have to click the "update" button twice to update my current good item in table.
First click is for filter function to get the current item.
Second click is for splice function to replace updated item with old one. I want to implement the filter function in my editGood() method for getting the id of current item, but don't know how to do it.
I tried to set the filter function in editGood() method where I take current object by clicking the button
App.vue file:
template:
<template>
<div id="app">
<!-- Component for adding a good -->
<AddGood v-on:add-good="addGood"/>
<!-- Component for managing goods in table -->
<Goods v-bind:goods="goods" v-on:del-good="deleteGood" v-on:edit-good="editGood"/>
<UpdateGood v-bind:good="goodToUpdate" v-on:update-good="updateGood" />
</div>
</template>
script:
import Goods from './components/Goods.vue';
import AddGood from './components/AddGood.vue';
import UpdateGood from './components/UpdateGood.vue';
methods : {
addGood(newGood){
this.goods = [...this.goods,newGood];
},
deleteGood(id){
this.goods = this.goods.filter(good => good.id !== id);
},
editGood(good){
this.goodToUpdate = Object.assign({}, good);
},
updateGood(updatedGood){
// Creating new array with only one object inside
e
let goodOriginalIndex = this.goods.indexOf(goodOriginalObject[0]);
this.goods.splice(goodOriginalIndex, 1, updatedGood);
alert("Js"+JSON.stringify(updatedGood));
}
}
Goods.vue:
<tr v-bind:key="good.id" v-for="good in goods"
v-on:del-good="$emit('del-good',good)">
<td>{{good.id}}</td>
<td>{{good.date}}</td>
<td>{{good.name}}</td>
<td>{{good.price}}</td>
<td>{{good.amount}}</td>
<td>{{good.sum}}</td>
<td><input type="submit" value="Удалить"
#click="$emit('del-good',good.id)"/></td>
<td><input type="submit" value="Изменить"
#click="$emit('edit-good',good)"/></td>
</tr>
UpdateGood.vue
template:
<div class="form-style-6">
<h1>Текущий Товар</h1>
<form #submit="updateGood">
<input type="text" v-model="good.id" placeholder="Артикул" />
<input type="text" v-model="good.date" placeholder="Дата Поступления" />
<input type="text" v-model="good.name" placeholder="Название" />
<input type="text" v-model="good.price" placeholder="Цена" />
<input type="text" v-model="good.amount" placeholder="Количество" />
<input type="text" v-model="good.sum" placeholder="Сумма" />
<input type="submit" value="Обновить" class="btn" />
</form>
</div>
script:
<script>
export default {
name : 'UpdateGood',
props: ["good"],
methods : {
updateGood: function(e){
e.preventDefault();
// Send up to parent
this.$emit('update-good', this.good);
}
}
}
</script>
I have to get the id of object in the editGood() method which pass this id to updateGood() function where splice is impelemented.
You can use watch function to subscribe when goodToUpdate object was updated and call updateGood function after that.
// your main component
{
methods: {
updateGood(updatedGood){
// Creating new array with only one object inside
let goodOriginalIndex = this.goods.indexOf(goodOriginalObject[0]);
this.goods.splice(goodOriginalIndex, 1, updatedGood);
alert("Js"+JSON.stringify(updatedGood));
this.goodToUpdate = null // reset after update
// if you just want to replace the old good by the new good, you can use map
this.goods = this.goods.map(good => good.id === updatedGood.id ? updatedGood : good )
},
...
},
watch: {
// whenever question changes, this function will run
goodToUpdate: function (newGood, oldGood) {
if(newGood){
this.updateGood(newGood)
}
}
},
}

accordion only show one item at same time in vue js

//this code in vue js for accordion this is working but not show open only //one at the same time.
//there component and pass props data.
<packing-material-category v-for="(category, index) in packingMaterialCategories" :category="category" :key="index"></packing-material-category>
//there template to render data.
<template>
<div class="packing-categories">
<div :class="packingCategoryClass">
<h3 class="packing-category__title" #click="toggleAccordion(category.title)" v-text="category.title" />
<div v-show="accordionOpen===true" class="packing-category__content">
<div v-if="category.description" class="packing-category__description" v-text="category.description" />
</div>
</div>
</div>
//call method for open and close
toggleAccordion() {
this.accordionOpen = !this.accordionOpen;
}
You need to track which item is shown outside of packing-material-category component (value of openedIndex) and to pass that value down to components. When value changes in the packing-material-category component, you emit event change-accordion, and in your parent component you update value of openedIndex
Try this
In your parent component, add openedIndex: 0 to data.
If you want everything closed by default, set value to false.
Update template to pass down the props index and openedIndex, so that components know when to show/hide.
<packing-material-category
v-for="(category, index) in packingMaterialCategories"
:key="index"
:index="index"
:openedIndex="openedIndex"
:category="category"
#change-accordion="(newOpen) => openedIndex = newOpen"
>
</packing-material-category>
And the packing-material-category component could look like this
<template>
<div class="packing-categories">
<div :class="packingCategoryClass">
<h3 class="packing-category__title" #click="toggleAccordion" v-text="category.title" />
<div v-show="index === openedIndex" class="packing-category__content">
<div v-if="category.description" class="packing-category__description" v-text="category.description" />
</div>
</div>
</div>
</template>
<script>
export default {
props: [
'category',
'index', // index of this component
'openedIndex', // which item should be shown/opened
],
data() {
return {
packingCategoryClass: '',
}
},
methods: {
toggleAccordion() {
// Show current item. If already opened, hide current item
let value = this.index === this.openedIndex ? false : this.index;
// notify parent component about the change
this.$emit('change-accordion', value)
}
}
}
</script>

VUE 2 with Bootstrap 4 - Radio button action not working when using data-toggle="buttons"

I have the following code:
<template>
<div class="btn-group" data-toggle="buttons">
<label class="btn btn-outline-dark active">
<input type="radio" name="grp" v-model="channel" value="vh1" checked> VH1
</label>
<label class="btn btn-outline-dark">
<input type="radio" name="grp" v-model="channel" value="mtv"> MTV
</label>
</div>
</template>
<script>
export default {
data() {
return {
channel: 'mtv'
}
},
watch: {
channel: function(newValue) {
console.log('value has been changed to ' + newValue);
}
}
}
</script>
When I click on a radio button, nothing happens. But when I removed the "data-toggle="buttons" attribute for the styling, then it starts working! Can anyone help me find a work around here?
Edit
For those of you who don't know, data-toggle is from bootstrap button plugin, it adds extra styles and functionality to the buttons you are making. Check out the docs here:
https://getbootstrap.com/docs/4.0/components/buttons/#toggle-states
Remove data-toggle and set active class by :class="{ 'active': channel === [value] }" for each input.
Finally got the solution
After long hours of searching, I was able to come up with a way to hack into the buttons.
Step 1
Change the above template to the following (removed v-model from the inputs)
<template>
<div class="btn-group" data-toggle="buttons" v-radio="channel">
<label class="btn btn-outline-dark active">
<input type="radio" name="grp" value="vh1" checked> VH1
</label>
<label class="btn btn-outline-dark">
<input type="radio" name="grp" value="mtv"> MTV
</label>
</div>
</template>
This solution was pretty difficult to find, and the solution was pretty hacky, but it got the job done.
Step 2 (UPDATED 6/18/2018)
Create a directive
In my case, since I was using single file components, I needed to bring it directives in the following way:
radio.js
export default {
inserted: function (el, binding) {
var btns = $(el).find('.btn');
var radioGrpName = $(btns[0]).find('input')[0].name;
$("input[name='" + radioGrpName + "'][value='" + binding.value + "']").closest('.btn').button('toggle');
},
bind: function (el, binding, vnode) {
var btns = $(el).find('.btn');
btns.each(function () {
$(this).on('click', function () {
var v = $(this).find('input').get(0).value;
(function set(obj, str, val) {
str = str.split('.');
while (str.length > 1) {
obj = obj[str.shift()];
}
return obj[str.shift()] = val;
})(vnode.context, binding.expression, v);
})
})
}
}
What this does is it binds a jquery click event to each radio button found in the div containing the v-radio. The second part where I'm doing function set (copied from the answer in reference), it checks to see if there's any dots in the expression, otherwise it sets the value of vnode.context["channel"], which updates the model. The bind hooks up to events before the component is loaded so it can be ready to fire off the internal workings of the radio button.
The inserted function is called after bind, during the physical insertion of the component into the parent node. So when you set an initial state (no event fired), the component will automatically reflect the value set in data.
Step 3
add directives radio to the script
import radio from '../../directives/radio'
<script>
export default {
directives: {
radio: radio
},
data() {
return {
channel: 'mtv'
}
},
//you can use computed property, but you need get and set
watch: {
channel: function(newValue) {
console.log('value has been changed to ' + newValue);
}
}
}
</script>
Using these links as references:
Old custom directive hack using vue 1
Update model from custom directive

Avoid mutating a prop : vuejs 2

Here what appears in the console when I run my code:"Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "isChecked"".
I have seen what other posts say about it but i can't adapt it on my problem.
Could someone explain it to me please?
PARENT:
template:
<div class="checkBox-container">
<input type="checkbox"/>
<!-- <div class="check" :class="[size]" v-if="!isChecked"></div>
<div class="check" :class="[size]" v-else>
<div>v</div>
</div> -->
<div class="check" :class="[size]" #click="changeVal">
<div v-if="isChecked">v</div>
</div>
<div><label class="label">{{label}}</label></div>
<div><span class="subLabel">{{subLabel}}</span></div>
script:
export default {
name: "ax-checkbox",
props: {
label: String,
subLabel: String,
size: String,
isChecked: false,
checks: []
},
methods: {
changeVal() {
this.isChecked = !this.isChecked;
this.$emit("changeVal");
}
}
};
CHILD
<div class="filters">
<ax-checkbox label="Où :" subLabel="Ville" size="small"></ax-checkbox>
<div class="separator"></div>
<ax-checkbox label="Quoi :" subLabel="Thématique(s)" size="small"></ax-checkbox>
<div class="separator"></div>
<ax-checkbox label="Quand :" subLabel="Dans..." size="small"></ax-checkbox>
</div>
The prop you are mutating is isChecked.
So, create a local data variable (initialized it with isChecked) and mutate it instead:
export default {
name: "ax-checkbox",
props: {
label: String,
subLabel: String,
size: String,
isChecked: false,
checks: []
},
data() {
return {isCheckedInternal: this.isChecked}
},
methods: {
changeVal() {
this.isCheckedInternal = !this.isCheckedInternal;
this.$emit("changeVal");
}
}
};
And replace it in the template:
<div class="checkBox-container">
<input type="checkbox"/>
<!-- <div class="check" :class="[size]" v-if="!isCheckedInternal"></div>
<div class="check" :class="[size]" v-else>
<div>v</div>
</div> -->
<div class="check" :class="[size]" #click="changeVal">
<div v-if="isCheckedInternal">v</div>
</div>
<div><label class="label">{{label}}</label></div>
<div><span class="subLabel">{{subLabel}}</span></div>
Note: The code above will use the prop isChecked only as initializer. If the parent changes in any way the value it passed to isChecked, the child component will not pick that change up. If you want to pick it up, add a watch in addition to the proposed code above:
//...
watch: {
isChecked(newIsChecked) {
this.isCheckedInternal = newIsChecked;
}
}
};
Ideal
There are some possible improvements to your code. Here are some suggestions:
subLabel prop should be sub-label
instead of emitting a changeVal value, emit update:isChecked and then you can use :is-checked.sync="myCheckedValue" in the parent.
This way you can still bind internally to the prop isChecked and not change it, but emit events and react to when the parent changes isChecked instead.
If you wanted to go the extra mile (and think it is worth it), you could also add a model option to your component, so you can be able to use v-model instead of :is-checked.sync.
See demo below.
Vue.component("ax-checkbox", {
template: '#axCheckboxTemplate',
props: {
label: String,
subLabel: String,
size: String,
isChecked: false,
checks: []
},
model: { // <== this part to will also enable v-model besides :is-checked.async
prop: 'isChecked',
event: 'update:isChecked'
},
methods: {
updateIsChecked() {
this.$emit("update:isChecked", !this.isChecked);
}
}
})
new Vue({
el: '#app',
data: {
myCheckedValueSync: false, myCheckedValueVModel: false,
}
});
<script src="https://unpkg.com/vue#2.5.13/dist/vue.js"></script>
<template id="axCheckboxTemplate">
<div class="checkBox-container">
<input type="checkbox" :checked="isChecked" #change="updateIsChecked" />
<div class="check" :class="[size]" #click="updateIsChecked">
CLICK ME<div v-if="isChecked">v</div>
</div>
<label class="label">{{label}}</label><span class="subLabel">{{subLabel}}</span>
</div>
</template>
<div id="app">
<div class="filters">
<pre>parent's myCheckedValueSync: {{ myCheckedValueSync }}</pre>
<ax-checkbox label="Où :" sub-label="Ville" size="small" :is-checked.sync="myCheckedValueSync">
</ax-checkbox>
<pre>parent's myCheckedValueVModel: {{ myCheckedValueVModel }}</pre>
<ax-checkbox label="Quoi :" sub-label="Thématique(s)" size="small" v-model="myCheckedValueVModel">
</ax-checkbox>
</div>
</div>
As it appears you're trying to update the value of a property passed into your component, your component is in fact a custom input component. Take a look at the excellent Vue docs on this topic. I've summarised the idea below.
2-way databinding in Vue is handled by v-model. Applied to your isChecked property this comes down to v-model="isChecked", which is in fact syntactic sugar for :value="isChecked" #input="evt => isChecked = evt.target.value".
Thus for your component, you need to do the following:
Update the name of the isChecked property to value
In your changeVal method, emit an input event, like:
changeVal() { this.$emit("input", !this.value); }
If need be, you could still also emit the changeVal event.

VueJS - Ajax communication between templates

I'm very new to VueJS and i'm having a difficult to share a result from Two template, that come from AJAX Request.
This is the home page:
<div>
<search-bar></search-bar>
<tracking-results></tracking-results>
</div>
This is the search-bar component, where i have a text input field and after press the button, it will do an Ajax Request:
<template>
<div class="row">
<div class="col-lg-8 col-lg-offset-3">
<div class="col-lg-5">
<div class="input-group">
<input type="text" class="form-control" placeholder="Numero Spedizione" v-model="trackingNumber">
<span class="input-group-btn">
<button class="btn btn-default"
type="button"
#click.prevent="search">Ricerca</button>
</span>
</div><!-- /input-group -->
</div><!-- /.col-lg-3 -->
</div>
</div><!-- /.row -->
</template>
<script>
export default {
data() {
return {
trackingNumber: '',
}
},
methods: {
search() {
Vue.http.options.emulateJSON = true;
this.$http.post('/endpoint').then(function (response) {
var parsedResponse = JSON.parse(response.data) || undefined;
/* HERE I WANT TO SEND THE RESPONSE TO ANOTHER COMPONENT */
}, function (err) {
console.log('ERROR', err);
});
}
}
}
</script>
I did tried with $broadcast, but my components arent child, are sibling.
I did see a way can be Vuex, but my application will not be written entirely with Vue. I will use this framework just to "simplify some Javascript process".
The only alternative i did find is to "merge" the search-bar and tracking-result in a single component. In this way the data will be "shared", and i can communicate with the state.
[Update: sync is removed in Vue 2, so you would need to follow the standard props-down, events-up design pattern]
You can have the parent viewmodel pass a prop to each of the components, using sync for the search bar. The search bar would populate the value in the ajax call, it would sync up to the parent and down to the tracking-results.
Some example code:
Vue.component('child1', {
props: ['ajaxData'],
methods: {
loadData: function () {
this.ajaxData = 'Some data is loaded';
}
},
template: '<div>Child1: {{ajaxData}} <button v-on:click="loadData">Load data</button></div>'
});
Vue.component('child2', {
props: ['ajaxData'],
template: '<div>Child2: {{ajaxData}}</div>'
});
new Vue({
el: 'body',
data: {
hi: 'Hello Vue.js!'
}
})
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<child1 :ajax-data.sync='hi'></child1>
<child2 :ajax-data='hi'></child2>
Ideally, you can send data to the parent, then the parent send data to the component via props. The parent handles the communication between the siblings.
Another way of doing it is using state management or vuex. But that depends on the complexity of your project. If it's a simple thing, I suggest to let the parent handle the communication.