Problem with checking ID in filter function inside a method - vue.js

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)
}
}
},
}

Related

Changing one Vuejs v-model value is changing all other values

I have a Nuxtjs/Vuejs application within which I am creating multiple Nodes. These Nodes have the Radio button for which I have assigned v-model. However, when I change the value of one Vuejs v-model is affecting all other Node Values. Following is the code sample that I have created for the Node. The ID value is unique for each Node.
<template>
<div ref="el">
<div class="header">
Node: {{ ID }}
</div>
<div>
Syntax:
<input
id="identifierTypeURN"
v-model="identifierSyntax"
type="radio"
value="URN"
name="instanceIdentifierURN"
#change="instanceIdentifiersSyntaxChange('URN')"
>
<label for="identifierTypeURN">URN</label>
<input
id="identifierTypeWebURI"
v-model="identifierSyntax"
type="radio"
value="WebURI"
name="instanceIdentifierWebURI"
#change="instanceIdentifiersSyntaxChange('WebURI')"
>
<label for="identifierTypeWebURI">WebURI</label>
</div>
</div>
</template>
I am aware that this is happening because I am using the same v-model name for all the Nodes so I changed to something like this. But still the issue persists:
<template>
<div ref="el">
<div class="header">
Identifiers
Node: {{ ID }}
</div>
<div>
Syntax:
<div v-for="node in allNodeInfo" :key="node.identifiersId">
<div v-if="node.identifiersId === ID">
<input
id="identifierTypeURN"
v-model="node.identifierSyntax"
type="radio"
value="URN"
name="instanceIdentifierURN"
#change="instanceIdentifiersSyntaxChange('URN')"
>
<label for="identifierTypeURN">URN</label>
<input
id="identifierTypeWebURI"
v-model="node.identifierSyntax"
type="radio"
value="WebURI"
name="instanceIdentifierWebURI"
#change="instanceIdentifiersSyntaxChange('WebURI')"
>
<label for="identifierTypeWebURI">WebURI</label>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data () {
return {
ID: '',
nodeId: '',
eventCount: '',
bizStep: '',
allNodeInfo: [],
instanceIdentifierSyntax: ''
}
},
mounted () {
this.$nextTick(() => {
const id = this.$el.parentElement.parentElement.id
const data = this.$df.getNodeFromId(id.slice(5))
this.ID = data.data.ID
this.nodeId = data.data.nodeId
this.allNodeInfo = JSON.parse(JSON.stringify(this.$store.state.modules.ConfigureIdentifiersInfoStore.identifiersArray, null, 4))
const identifiersNode = this.allNodeInfo.find(node => node.identifiersId === this.nodeId)
this.instanceIdentifierSyntax = identifiersNode.identifierSyntax
console.log(JSON.stringify(this.allNodeInfo, null, 4))
})
},
methods: {
// On change of the IdentifierSyntax change, change the value in the respective node info
instanceIdentifiersSyntaxChange (syntaxValue) {
// Change the value of the respective syntax within the Node information in IdentifiersNode array
console.log(this.ID + " --- " + syntaxValue)
}
}
}
</script>
<style>
</style>
I know I am making some small mistake where I need to differentiate each Nodes V-model but nothing is clicking me. Can someone please help me.
You have to pass your node and your v-model also to your methods within this event.. like this, because for every loop in your v-for there will be a "new created node" which includes your v-model and than you can refer to this:
#change="instanceIdentifiersSyntaxChange('URN', node, identifierSyntax)"
In your methods you just do everything based on your node.identifierSyntax = HERE WHAT YOU WANT..
Hopefully I understood the question correct and helped you out!
EDIT: (Standard procedure)
Normally it looks like this when you add a single "input" to your v-for.
Template:
<div v-for="node in inputs" :key="node.id">
<input v-model="node.someDefinition" :value="node.someDefinition"/>
<div #change="another_Function(node, someDefinition)></div>
</div>
<button #click="add_new_Input>ADD</button>
Script:
data() {
return {
id: 0,
inputs: [{ //this is representing the first input when side will be loaded
id: this.id,
//your stuff in here as well
}]
}
}
methods: {
add_new_Input() {
this.inputs.push({
id: this.id += 1
})
}
}
This should be enough.. So in your template you have a v-for where your are looping over an array (or something else but in my case it's an array) with all inputs - be aware every input of me gets an unique ID when it will be created or added.
Also you have - in my case here - an input-tag where you can get the v-model or set the :value. It's binded to my unique node which I have created in my methods.
If you pass your #change you also have to pass the current node and the v-model / value that the correct one will be changed.
Hopefully it now helps you out!

VueJs get value input radio in request axios

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

Vue input event not capturing entire field

I have a vue component that adds a search bar and search bar functionality. It contains this line:
<input class="input" type="text" placeholder="Address" v-model="searchQuery" v-on:input="(event) => this.$emit('queryChange', event)">
This captures the text in the search bar and emits it.
In my vue, this triggers my updateSearchQuery function:
this.searchQuery = event.data which merely saves the users input in the searchQuery property in my vue. Everything works fine when I do this, until, I make a search and then, make another call using the same this.searchQuery data.
For example, I'm trying to filter results with the search query '956'. I enter it and this call is made: GET /users?cp=1&pp=20&se=956, just like it should. Then after the page loads, if I go to page 2 of the results, this is the call that is made to the server: GET /users?cp=2&pp=20&se=6. Instead of saving 956 as the queryStr in the the view, it only saves the most recent character entered, instead of the entire content of the serch text.
This happens every time I type in multiple characters as a search query, and then make another call to the server using the unchanged this.searchQuery variable. If my initial search query is only a single character, it works just fine.
What am I doing wrong here? How can I emit the entirety of the text in the search bar, after any change, so that I can always save the whole search query, instead of the just the most recent change?
EDIT: I've add some more code below so the data flow is easier to follow:
Here is the template and script for the search component:
<template>
<div class="level-item">
<div class="field has-addons">
<div class="control">
<input class="input" type="text" placeholder="Address" v-model.lazy="searchQuery" v-on:input="(event) => this.$emit('queryChange', event)">
</div>
<div class="control">
<div class="button is-light" #click="clearInput">
<span class="icon is-small">
<i class="fa fa-times" style="color:#ffaaaa"></i>
</span>
</div>
</div>
<div class="control">
<button class="button is-info" #click="onSearch(searchQuery)">Search</button>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Search',
props: {onSearch: Function},
data () {
return {
searchQuery: ''
}
},
watch: {},
methods: {
clearInput () {
this.searchQuery = ''
}
}
}
</script>
the emitted queryChange event is caught and listened to in the vue page:
<Search :onSearch="onSearch" v-on:queryChange="updateSearchQuery"> and this triggers the updateSearchQuery function:
updateSearchQuery (event) {
this.searchQuery = event.data
console.log(event.data + ' || event.data')
console.log(this.searchQuery + ' || this.searchQuery')
}
Theoretically, the searchQuery data in my vue should be a copy of the searchQuery data in my component, which is itself merely a copy of whatever the user has input in the search bar.
Then when I make a call to the server I'm using the value in this.searchQuery in my vue:
onSearch (search) {
this.makeServerQuery(1, search)
},
onPaginate (page) {
this.makeServerQuery(page, this.searchQuery)
},
makeServerQuery (page = null, search = null) {
let queryStr = ''
if (page !== null) {
queryStr += '?cp=' + page + '&pp=' + this.perPage
}
if (this.searchQuery !== '') {
queryStr += '&se=' + this.searchQuery
} .....
The on onSearch(search) function is called whenever the search button is pressed. That seems to work fine, because when the button is pressed the entire searchQuery is passed, not just the last change.
An input event's data value appears to be the last typed character, and not the current value of the input. A simple fix is:
#input="$emit('queryChange', searchQuery)"
This works because the model will always be updated before the input event handler runs.
Here's a complete working component example:
<input
v-model="searchQuery"
type="text"
placeholder="Address"
#input="onInput"
/>
export default {
data() {
return { searchQuery: '' };
},
methods: {
onInput() {
console.log(this.searchQuery);
this.$emit('queryChange', this.searchQuery);
},
},
};

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

Vue and Vuex: Updating state based on changes to the view

I'm trying to build
An application that renders a form, where the default input values are equal to the data from the store.
When the save button is clicked, the state will be updated according to the new data added to the view by the user.
Currently the inputs are bound to the store data, and so I have no reference to the "live" value of the inputs. When the user clicks save, how do I grab the "live" values?
Component Template
<input type="text" class="form-control" :value="item.name">
<input type="text" class="form-control" :value="item.price">
<button class="btn btn-primary" v-on:click="updateItem(item)">Save</button>
Component
data: function() {
return {}
},
methods: {
updateItem(item) {
this.$store.commit('updateItem', item);
},
},
computed: {
items() {
return this.$store.getters.getItem;
}
}
Potential Solutions
I thought I could perhaps create a "clone" of the store, and bind the inputs to the cloned item data. Then this object will be updated as the view changes, and so I can grab those "live" values, and commit the data from the view to the store. Is this a good solution?
If you wanted to update without the user having to click the button, then I would suggest one of the methods explained in the docs.
But since you want to do it wen they click the button, try something like this:
<template>
<form>
<input type="text" class="form-control" v-model="item.name">
<input type="text" class="form-control" v-model="item.price">
<button class="btn btn-primary" #click.prevent="updateItem">Save</button>
</form>
</template>
<script>
export default {
data() {
return {
item: {
name: null,
price: null,
}
}
},
mounted() {
// Set the default value of this.item based on what's in the store
this.item = this.$store.getters.getItem
},
methods: {
updateItem() {
this.$store.commit('updateItem', this.item);
}
}
}
</script>