VueJS handling dynamic input - vue.js

I have an vs-input with :id=info.tag. How can I change the input value based on the :id tag? Data is dynamic
<template>
<vs-row vs-type="flex" vs-justify="space-around">
<div v-for="info in information" :key="info.id">
<vs-col vs-type="flex" vs-justify="center" vs-align="center" vs-w="3">
<vx-card :title="info.user_1" :subtitle="info.desc">
<div slot="footer">
<vs-row vs-justify="center">
<vx-input-group class="mb-base">
<vs-input :id="info.tag" placeholder="Data Output" readonly />
<br>
<div class="text-center">
<vs-button color="primary" #click="grabData($event, generator.tag)">Grab data</vs-button>
</div>
</vx-input-group>
</vs-row>
</div>
</vx-card>
</vs-col>
</div>
</vs-row>
</template>
<script>
export default {
data() {
return {
information: null
}
},
created () {
this.$store.dispatch('user/getInformation').then(this.$$nextTick).then(() => {
this.information = this.$store.state.user.information
})
},
methods: {
grabData(data, tag) {
this.$store.dispatch('user/grabData', {tag})
.then(res => {
//Nice! Set input value based on the tag
})
.catch(error => {
//hmm
})
},
}
}
</script>

I'll assume that each item in information has a field named value
Then you can just change vs-input to this
If you don't have a field name value you can add it
created () {
this.$store.dispatch('user/getInformation').then(this.$$nextTick).then(() => {
this.information = this.$store.state.user.information.map(item => Object.assign({value:''}, item))
})
}

So given vs-input I assume you are using VueSax.
It has v-model property which will line up to the value.
add v-model to vs-input
<vs-input :id="info.tag" v-model="infoInput" placeholder="Data Output" readonly/>
make sure to include property into data
data() {
return {
information: null,
infoInput: '',
}
},
then when you have an event just change the value.
this.infoInput = "new value"
This will update the value in vs-input
More info can be found v-model api

Related

vue js filtering with search bar

I have created an app that requests from an API the data and creates flexboxes. Now I added a search box and I would like to filter the articles by their contact and/or title.
I've also created a computed property to filter the returned list of items but when I replace in line 11 the paginated('items') with paginated('filteredArticles') that returns nothing.
What did I do wrong?
<template>
<div id="app">
<div class="search-wrapper">
<input type="text"
class="search-bar"
v-model="search"
placeholder="Search in the articles"/>
</div>
<paginate ref="paginator" class="flex-container" name="items" :list="items">
<li v-for="(item, index) in paginated('items')" :key="index" class="flex-item">
<div id="image"><img :src="item.image && item.image.file" /></div>
<div id="date">{{ item.pub_date }}</div>
<div id="title"> {{ item.title }}</div>
<div class="article">{{item.details_en}}</div>
</li>
</paginate>
<paginate-links for="items" :limit="2" :show-step-links="true"></paginate-links>
</div>
</template>
<script>
import axios from "axios";
export default {
data() {
return {
items: [],
paginate: ["items"],
search:'',
};
},
created() {
this.loadPressRelease();
},
methods: {
loadPressRelease() {
axios
.get(`https://zbeta2.mykuwaitnet.net/backend/en/api/v2/media-center/press-release/?page_size=61&type=5`)
.then((response) => {
this.items = response.data.results;
});
},
},
computed:{
filteredArticles() {
return this.items.filter(item=>item.includes(this.search))
}
}
};
</script>
You need fields you want to search and connvert search string and fields with toLowerCase() or toUpperCase():
computed : {
filteredArticles() {
if (!this.search) return this.items
return this.items.filter(item => {
return (item.title.toLowerCase().includes(this.search.toLowerCase()) || item.contact.toLowerCase().includes(this.search.toLowerCase()));
})
}
}
Your computed doesn't seem correct. Since items is an array of objects, you'd need to do this:
filteredArticles() {
if (!this.search) {
return this.items;
}
return this.items.filter(item => {
return item.title.includes(this.search);
})
}
Note that this will only search the title field, and it's case sensitive.

Vue parent component not re-rendering when computed properties are changing

I have a User-Select component that wraps a v-select. This components job is to fetch list of users as user types and allow the user to select one or more users.
Here is the code for this component.
<template>
<div>
<v-select
id="recipients"
name="recipients"
:options="options"
label="name"
multiple
#search="onSearch"
v-model="selectedVal"
/>
</div>
</template>
<script>
import vSelect from 'vue-select'
import axios from 'axios'
import _ from 'lodash'
export default {
name: 'UserSelect',
components: {
'v-select': vSelect
},
props: {
value: {
type: Array
}
},
data() {
return {
options: []
}
},
computed: {
selectedVal: {
get() {
return this.value
},
set(val) {
//this.value = val
this.$emit('input', val)
}
}
},
methods: {
onSearch(search, loading) {
loading(true)
this.search(loading, search, this)
},
setSelected: function(val) {
this.$emit('input', val)
},
search: _.debounce((loading, search, vm) => {
axios
.post('searchPeople', { search }, { useCredentails: true })
.then(res => {
vm.options = res.data
loading(false)
})
}, 350)
}
}
</script>
<style lang="scss" scoped>
#import url('https://unpkg.com/vue-select#latest/dist/vue-select.css');
</style>
As you can see I have a v-model linked to a computed property , which emits input event. Also my property name is value. Hence, I expect the parent component,that is using this UserEvent component to be able to v-model.
In the parent component , i have a computed property to which I have v-modelled the selected value. Here is the code.
<template>
<div>
<b-modal id="editMessage" :title="title" :static="true">
<form id="newMessageForm" class="row">
<div class="col-md-12">
<div class="form-group row">
<label for="to" class="col-sm-3 col-form-label">To:</label>
<user-select
class="col-sm-7"
style="padding-left: 0px; padding-right: 0px"
v-model="editedMessage.recipients"
/>
</div>
<div class="form-group row">
<label for="subject" class="col-sm-3 col-form-label"
>Subject:</label
>
<input
id="subject"
name="subject"
type="text"
class="form-control col-sm-7"
:value="editedMessage.messageSubject"
/>
</div>
<div class="form-group row">
<label for="date" class="col-sm-3 col-form-label"
>Schedule for later :
</label>
<input
type="checkbox"
class="form-control col-sm-7"
v-model="scheduleForLater"
id="scheduleCheckBox"
/>
</div>
<div class="form-group row" v-if="scheduleForLater">
<label for="date" class="col-sm-3 col-form-label"
>Scheduled Date:</label
>
<datetime
v-model="editedMessage.sentDate"
type="datetime"
class="col-sm-17"
input-class="form-control col-sm-15"
input-id="date"
/>
</div>
<div class="form-group row">
<label for="body" class="col-sm-3 col-form-label">Message:</label>
<textarea
id="body"
name="body"
type="text"
rows="10"
class="form-control col-sm-7"
:value="editedMessage.messageBody"
></textarea>
</div>
</div>
</form>
<template v-slot:modal-footer="{ hide }">
<!-- Emulate built in modal footer ok and cancel button actions -->
<b-button size="sm" variant="light" #click="hide()">
Cancel
</b-button>
<b-button
size="sm"
variant="secondary"
#click="
sendMessage(true)
hide()
"
>
Save Draft
</b-button>
<b-button
size="sm"
variant="primary"
#click="
sendMessage(false)
hide()
"
>
Send
</b-button>
</template>
</b-modal>
</div>
</template>
<script>
import { mapState } from 'vuex'
import UserSelect from '#/components/UserSelect.vue'
import axios from 'axios'
export default {
name: 'NewMessage',
components: {
'user-select': UserSelect
},
data() {
return {
options: [],
scheduleForLater: false
}
},
mounted() {},
computed: {
...mapState({
openMessage: state => state.message.openMessage,
messageAction: state => state.message.messageAction
}),
editedMessage: {
get() {
if (this.messageAction === 'new') {
return this.newMessage()
} else if (this.messageAction === 'reply') {
let openMessageClone = Object.assign({}, this.openMessage)
// make sender as the recipient.
return Object.assign(openMessageClone, {
messageSubject: 'RE: ' + this.openMessage.messageSubject,
recipients: [
{
name: this.openMessage.sender.name,
id: this.openMessage.sender.id
}
]
})
} else {
let openMessageClone = Object.assign({}, this.openMessage)
return Object.assign(openMessageClone, {
messageSubject: 'FW: ' + this.openMessage.messageSubject
})
}
},
set(val) {
this.$emit('input', val)
}
},
title() {
if (this.messageAction === 'new') {
return 'New'
} else if (this.messageAction === 'reply') {
return 'Reply'
} else {
return 'Forward'
}
}
},
methods: {
newMessage() {
return {
messageBody: '',
messageSubject: '',
recipients: [],
sender: {}
}
},
sendMessage(saveOrUpdateDraft) {
var url = ''
var message = {
recipients: this.editedMessage.recipients.map(x => x.id),
subject: this.editedMessage.messageSubject,
body: this.editedMessage.messageBody,
sentDate: this.scheduleForLater ? this.editedMessage.sentDate : null,
id: ['editDraft', 'viewScheduled'].includes(this.messageAction)
? this.editedMessage.messageId
: null
}
// id indiciates message id of draft message if one was opened.
if (saveOrUpdateDraft) {
// if no changes have been made to an opened draft, or the draft is completely empty for a new or existing draft , just go back.
if (message.id) {
url = `updateDraft`
} else {
url = 'saveNewDraft'
}
} else {
if (message.id) {
url = `sendSavedMessage`
} else {
url = 'sendMessage'
}
}
axios
.post(`eventdirect/${url}`, message, {
withCredentials: true
})
.then(response => {
if (url.includes('Draft') && this.messageAction === 'viewScheduled') {
this.$store.dispatch('sent/moveToDraft', response.data)
} else if (url.includes('Draft')) {
this.$store.dispatch('drafts/updateDraft', response.data)
} else {
// else part is sending a message or scheduling a draft.
if (this.messageAction === 'editDraft') {
this.$store.dispatch('drafts/deleteDraft', response.data)
}
// if we are sending an existing scheduled messsage , just update the sent vuex store , so that the message moves from scheduled to sent bucket.
if (this.messageAction === 'viewScheduled') {
this.$store.dispatch('sent/updateMessage', response.data)
} else {
this.$store.dispatch('sent/addSentItem', response.data)
}
}
})
.catch(() => {
// TODO , add a qtip here to notify user , this message should be sent later.
// messages in draft store with target , should be posted to the target
this.$store.dispatch('drafts/updateDraft', {
...message,
target: url
})
})
}
}
}
</script>
Now i can see in the vue dev tools the computed values in this NewMessage component gets changed. However this component does not re-render and the selected values are not passed down to UserSelect component until I toggle , schedule for later checkbox , that causes the components data to change and this triggers the Vue component to suddenly start showing the selected values.
Whats going on here. There is something about Vue's reactivity that I am not able to understand here.
Thanks in anticipation. You can try it here, or click on the edit sandbox button above to view and edit the code.
To try it, click on the Vue Message link , then hit reply , then type ‘Test’ and then select ‘Test User’ from the drop down. The selected user wont show until you click the checkbox - Schedule later.
PS: Within the component , UserSelect, In the setter of the computed property selectedVal , if I manually set the value of property value (by simply uncommenting line 39 - which is commented right now) , everything works fine. However , I get a warning that property should not be directly set. Since I am emitting the input event , parent component should change its v-model and re-render , thereby causing child component to re-render. Please correct me , if my understanding is wrong. Problem once again is that the parent component’s v-model changes , but it doesn’t re-render.

Send value from API request from a component to another Vue.JS

I have a component which allow to retrieve the datas from a rest API...
My template allow user to enter an input (id) and to find the user associated with the user. I also have a component which is called dynamically.
<template>
<div>
<!-- form -->
<form>
<input type="text" v-model="userId" id="userId">
<button type="submit" class="btn btn-primary" #click="getUser($event); !isExistingUser">Get User</button>
</form>
<!-- result -->
<div v-if="!showComponent">
{{ user["id"] }} {{ user["username"] }} {{ user["email"] }}
<button #click="showComponent = !showComponent">Editer</button>
</div>
<!-- Edit the user -->
<div v-if="showComponent">
<edit-user :toUpdate="updateUser"></edit-user>
</div>
</div>
</template>
In the script part I have datas and methods :
The objective is to send the user that i collect and to send it to the update user. For this I created a data binding.
I also try to set the value of the object in the getUser method. And i can display the value.
<script>
import axios from "axios";
import EditUserForUpdate from "./EditUserForUpdate";
export default {
name: "FindUser",
components: {
"edit-user": EditUserForUpdate
},
data() {
return {
toUpdate: Object,
user: null,
isExistingUser: false,
userId: "",
userEmail:"",
userUsername: "",
showComponent: false
};
},
methods: {
getUser(event) {
axios
.get("http://localhost:4000/api/users/" + this.userId)
.then(response => {
console.log(response);
this.user = response.data.data;
var toUpdate = {};
toUpdate = { upUserName: this.user.username, upUserEmail: this.user.email, upId: this.user.id};
console.log(toUpdate);
});
}
}
};
</script>
Finally in the child component :
<script>
export default {
name: "EditUserForUpdate",
data: function () {
return {
updateUser: ''
}
},
props: {
updateUser: Object
},
methods: {
beforeMount () {
var updateUser = this.updateUser // save props data to itself's data and deal with it
console.log("userToUpdate : " + updateUser);
}
}
}
</script>
My issue is that I don't retrieve the data in the child module for an unknown reason.
The property is named toUpdate and not updateUser.
Update your prop accordingly in the EditUserForUpdate component:
props: {
toUpdate: Object
}
And of course, localize that object for manipulation:
beforeMount() {
this.updateUser = this.toUpdate
}

add comment using v-model inside v-for loop in posts

I'm getting a posts array from a vuex getter and looping through them with v-for to display each post and it's comment then i added an input field and binded it with v-model to get the input value and dispatch an action to send the value to the API
<div class="post-content" v-for="(post, index) in posts">
<div class="post-comment">
<input type="text" class="form-control" placeholder="Add a comment" v-model="comment" #keyup.enter="addComment(post.id)">
</div>
</div>
<script>
export default {
data() {
return {
postContent: '',
comment: ''
}
},
methods: {
addPost() {
this.$store.dispatch('addPost', {
content: this.postContent
})
this.postContent = ''
},
addComment(postID, index) {
this.$store.dispatch('addComment', {
body: this.comment,
post_id: postID
})
}
},
created(){
this.$store.dispatch( 'loadFeed' )
},
computed: {
postsLoadStatus(){
return this.$store.getters.getPostsLoadStatus
},
posts(){
return this.$store.getters.getFeed
}
},
}
</script>
but when i set the v-model to a data property and try to type something in the input it's assigned on all posts so what's the right way to grab the comment data
Create a getter that accepts a function:
getters () {
getCommentByPostId(state) => (post_id) => {
return state.posts.find((post) => post.id === post_id).comment
}
}
Then use that getter on that :value and not v-model:
<input type="text" class="form-control" placeholder="Add a comment" :value="$store.getters['getCommentByPostId'](post.id)" #keyup.enter="addComment(post.id)">
Make sure to handle scenarios where the comment doesn't exist and return an empty string, too.

this.$nextTick not working the way expected

I populate a dropdown, based on the result of another dropdown. And if i got a default value i want to set the second dropdown.
select country so i get al the country regions.
If i am on my edit page i already have the country id, so i trigger the ajax request on created() and populate the regions select.Ofc i have the region id and i would like to set it.
getRegions() {
let countryId = this.form.country_id;
if (countryId) {
axios.get('/getRegion/' + countryId)
.then(response => {
this.regions = response.data;
if (this.regions && this.form.country_region_id) {
this.$nextTick(() => {
$(".country_region").dropdown("set selected",
this.form.country_region_id).dropdown("refresh");
});
/*
setTimeout(() => {
$(".country_region").dropdown("set selected",
this.form.country_region_id).dropdown("refresh");
}, 1000);
*/
}
})
.catch(error => {
this.errors.push(error)
});
}
},
The code segment with the setTimeout is working 1sec later the correct value is selected.
Edit:
My dropdown actually got a wrapper component, so i can use v-model on it.
But the nextTick still doesnt seem to work.
<sm-dropdown v-model="form.country_region_id" class="country_region">
<input type="hidden" :value="form.country_region_id" id="country_region_id">
<div class="default text">Gebiet</div>
<i class="dropdown icon"></i>
<div class="menu">
<div v-for="region in regions" class="item" :data-value="region.country_region_id"
:key="region.country_region_id" >
{{ region.internal_name }}
</div>
</div>
</sm-dropdown>
Dropdown component:
<template>
<div class="ui fluid search selection dropdown" ref="input">
<slot></slot>
</div>
</template>
<script>
export default {
props: ['value'],
mounted() {
let self = this;
$(this.$el)
.dropdown()
.dropdown({
forceSelection: false,
onChange: function(value) {
self.$emit('input', value);
self.$emit('change');
}
}).dropdown("refresh")
;
}
}
</script>