Multiple select on the edit form - vuejs2

I have the following form:
This is the edit form.
As you can see I have a multiple select. I need to bind the values from the server to the select.
Here is structure of my objects from the server.
1) All elements for the multiple select:
2) Particular objects, that I want to see selected. As you can see, there's an additional field called 'pivot'.
As a result, I would like to see the following when I open my form:
I have tried something like this, but without success:
<div class="form-group">
<label for="bk">Связанные бк</label>
<select class="form-control form-control-sm" id="bk" v-model="formFields.applicationBk" multiple>
<option v-for="bk in allBk" v-if="applicationBk.find(x => x.id === bk.id) 'selected'" >
{{ bk.name }}
</option>
</select>
Here is full js code:
<script>
import { EventBus } from '../../app';
export default {
name: "ApplicationEdit",
props: ['applicationId', 'name', 'offer', 'bundleId', 'isBlackMode', 'applicationBk', 'allBk'],
mounted: function(){
console.log(this.applicationBk)
},
methods:{
submit: function (e) {
window.axios.post('/application/edit/' + this.applicationId, this.formFields)
.then(res => {
console.log('Сохранил!');
$('#applicationEdit' + this.applicationId).modal('hide');
EventBus.$emit('reloadApplicationsTable');
}).catch(err => {
if(err.response.status === 422){
this.errors = err.response.data.errors || [];
}
//console.error('Ошибка сохранения приложения. Описание: ');
console.error(err)
});
}
},
data(){
return {
formFields: {
applicationId: this.applicationId,
applicationBk: this.applicationBk,
name: this.name,
offer: this.offer,
bundle_id: this.bundleId,
giraffe: this.isBlackMode,
bk: this.applicationBk,
},
errors: []
}
}
}

You might consider using your loop as you have but using v-model to an array of the selected values. Here is vue's example of this: https://v2.vuejs.org/v2/guide/forms.html#Select

Related

How can I get a specifc selection in select vue.js?

How are you?
I'm studying Vue and I'm stuck on the current task not knowing where to go.
I have a select that when I click I need to show on screen only what corresponds to that selection. For example, when placing the "to do" option in the select, only the tasks with a concluded=false should appear on the screen. I've only gotten this far and I need help to continue. Can you help me? Thanks
This is my App.vue
<template>
<div id="app">
<h1>Lista de Tarefas</h1>
<List :data="list" #remove="handleRemove"/>
<Form #add="addNewTask" #onChange="handleN"/>
</div>
</template>
<script>
import List from "./components/List.vue";
import Form from "./components/Form.vue";
export default {
components: {
List,
Form,
},
data() {
return {
list: [],
};
},
methods: {
addNewTask(newTask) {
this.list.push(newTask);
},
handleRemove(item) {
const index = this.list.findIndex(i => i.id === item.id)
this.list[index].excluded = true
},
handleN(item) {
const index = this.list.findIndex(i => i.id === item.id)
this.list[index].concluded = true
}
},
};
</script>
This is my List.vue
<template>
<ul>
<select v-model="selected" #change="onChange($event)">
<option disabled value="">Escolha a visualização</option>
<option v-for="option in options" :key="option.text">
{{ option.text }}
</option>
</select>
<li v-for="item in itens" :key="item.id">
<input type="checkbox" id="checkbox" v-model="item.concluded" />
<label for="checkbox"> {{ item.description }} </label>
<button #click="() => $emit('remove', item)">Excluir</button>
</li>
</ul>
</template>
<script>
export default {
props: {
data: {
type: Array,
default: () => {},
},
},
data() {
return {
selected: "",
options: [
{ text: "Todos", value: "1" },
{ text: "A fazer", value: "2" },
{ text: "Concluído", value: "3" },
{ text: "Deletado", value: "4" },
],
};
},
computed: {
itens() {
return this.data.filter((item) => item.excluded === false);
},
},
methods: {
onChange(event) {
console.log(event.target.value);
return this.data.filter((item) => item.concluded === false);
},
},
};
</script>
This is my Form.vue
<template>
<form #submit.prevent="handleNewTask">
<input type="text" v-model="newTask" placeholder="Insira a tarefa"/>
<input type="submit" value="Adicionar"/>
</form>
</template>
<script>
import Task from '../types/Task.js'
export default {
data() {
return {
newTask: "",
};
},
methods: {
handleNewTask() {
this.$emit('add', new Task(this.newTask))
this.newTask = ''
}
},
};
</script>
And this is my Task.js
export default class {
constructor(description) {
this.description = description,
this.id = Math.random(),
this.concluded = false,
this.excluded = false
}
}
I watch some tutorials, read the documentation and some StackOverflow questions but I really can't get out of here
Thanks in advance for the help
Based on how you have structured your app, our only concern should be with the List.vue file.
Your goal is to filter the results based on the selection (selected property). However, your issue is that you are not even using that anywhere.
I know you are hard coding the filter on the onChange method but that is, first of all wrong because you aren't really changing anything (you are returning an array), and secondly it's inefficient.
A better way to do it is to update the computed itens function like so:
itens() {
return this.data.filter((item) => {
if (this.selected === '1'){
return item.concluded === false
} else if (this.selected === '2'){
// filter another way
} else if (... // so on and so forth
});
},
Also, I would filter out the excluded items before sending them to the component. If you aren't going to use it, don't send it.
Remove the onChange event on the <select> and the associated method since they are now unused.

Clearing Vue JS v-for Select Field

I have a simple application that uses a v-for in a select statement that generates two select tags. The groupedSKUAttributes variable that creates the select statement looks like this:
groupedSKUAttributes = {colour: [{id: 1, name: 'colour', value: 'red'},
{id: 2, name: 'colour', value: 'blue'}],
size: [{id: 3, name: 'size', value: '40'},
{id: 4, name: 'size', value: '42'}]}
I also have a button that I want to clear the select fields. How do I get the clear method to make each of the select fields choose their default <option value='null' selected>select a {{ attributeName }}</option> value? I can't figure out if I'm meant to use a v-model here for the groupedSKUAttributes. Any advice would be appreciated.
The template looks like this:
<template>
<div>
<select
v-for='(attribute, attributeName) in groupedSKUAttributes'
:key='attribute'
#change='update(attributeName, $event.target.value)'>
<option value='null' selected>select a {{ attributeName }}</option>
<option
v-for='a in attribute'
:value='a.id'
:label='a.value'
:key='a.id'>
</option>
</select>
</div>
<button #click='clear'>clear</button>
</template>
And the JS script looks like this:
<script>
export default {
name: 'app',
data() {
return {
groupedSKUAttributes: null,
}
},
methods: {
clear() {
console.log('clear');
},
update(attributeName, attributeValue) {
console.log(attributeName, attributeValue);
},
getSKUAttributes() {
API
.get('/sku_attribute/get')
.then((res) => {
this.skuAttributes = res.data;
this.groupedSKUAttributes = this.groupBy(this.skuAttributes, 'name');
})
.catch((error) => {
console.error(error);
});
},
},
created() {
this.getSKUAttributes();
}
}
</script>
The v-model directive works within the v-for without any issues.
<script>
export default {
name: 'app',
data() {
return {
groupedSKUAttributes: null,
selected: {}
}
},
methods: {
clear() {
this.generateDefaultSelected(this.generateDefaultSelected);
},
update(attributeName, attributeValue) {
this.selected[attributeName] = attributeValue;
},
getSKUAttributes() {
API
.get('/sku_attribute/get')
.then((res) => {
this.skuAttributes = res.data;
this.groupedSKUAttributes = this.groupBy(this.skuAttributes, 'name');
// Call this method to reset v-model
this.generateDefaultSelected(this.groupedSKUAttributes);
})
.catch((error) => {
console.error(error);
});
},
generateDefaultSelected(groupedSKUAttributes) {
// Reset the object that maintains the v-model reference;
this.selected = {};
Object.keys(groupedSKUAttributes).forEach((name) => {
// Or, set it to the default value, you need to select
this.selected[name] = '';
});
}
},
created() {
this.getSKUAttributes();
}
}
</script>
In the above code, generateDefaultSelected method resets the selected object that maintains the v-model for all your selects.
In the template, you can use v-model or unidirectional value/#change pair:
<!-- Using v-model -->
<select
v-for='(attribute, attributeName) in groupedSKUAttributes'
:key='attributeName' v-model="selected[attributeName]">
<!-- Unidirection flow without v-model -->
<select
v-for='(attribute, attributeName) in groupedSKUAttributes'
:key='attributeName' :value="selected[attributeName]"
#change='update(attributeName, $event.target.value)'>

Retrieve data attribute value of clicked element with v-for

I've made a datalist which is filled dynamically and it works correctly.
Now, I need listen the click event on the options to retrieve the data-id value and put it as value in the input hidden.
I already tried with v-on:click.native and #click but there is no response in the console.
Any idea? I'm just starting at Vue, hope you can help me.
Edit:
Looks like it doesn't even fire the function. I've tried v-on:click="console.log('Clicked')" but nothing happens.
<input type="hidden" name="id_discipline" id="id_discipline">
<input list="disciplines" id="disciplines-list">
<datalist id="disciplines">
<option
v-for="discipline in disciplines"
:key="discipline.id_discipline"
:data-id="discipline.id_discipline"
v-on:click="updateDisciplineId($event)"
>{{discipline.name}}</option>
</datalist>
methods: {
updateDisciplineId(event) {
console.log('clicked!);
}
},
Using datalist is not suited for what you want to acheive, however there's a workaround with a limitation.
Template:
<template>
<div>
<input
type="text"
name="id_discipline"
v-model="selectedID"
placeholder="Data id value of clicked"
/>
<input
#input="onChange"
list="disciplines"
id="disciplines-list"
class="form-control"
placeholder="Seleccionar disciplina"
/>
<datalist id="disciplines">
<option
v-for="discipline in disciplines"
:key="discipline.id_discipline"
:data-value="discipline.id_discipline"
>{{ discipline.name }}</option
>
</datalist>
</div>
</template>
Script Part:
<script>
export default {
data() {
return {
selectedID: "",
id_discipline: "",
disciplines: [
{
id_discipline: 1,
name: "Yoga"
},
{
id_discipline: 2,
name: "Functional"
}
]
};
},
methods: {
onChange(e) {
this.getID(e.target.value).then(
resposnse => (this.selectedID = resposnse)
);
},
async getID(value) {
let promise = new Promise((resolve, reject) => {
this.disciplines.forEach(item => {
if (item.name === value) resolve(item.id_discipline);
});
});
return await promise;
}
}
};
</script>
Here's a working Sandbox demo.
**Limitation: Discipline name (Yoga, functional) should be unique.

how to make el-select and v-model work together when extracting a custom component

I'm using el-select to build a select component. Something like this:
<template>
//omitted code
<el-select v-model="filterForm.client"
filterable
remote
placeholder="Please enter a keyword"
:remote-method="filterClients"
:loading="loading">
<el-option
v-for="item in clientCandidates"
:key="item._id"
:label="item.name"
:value="item._id">
</el-option>
</el-select>
</template>
<scripts>
export default {
data() {
filterForm: {
client: ''
},
clientCandidates: [],
loading: false
},
methods: {
filterClients(query) {
if (query !== '') {
this.loading = true;
setTimeout(() => {
this.loading = false;
this.clientCandidates = [{_id: '1', name: 'foo'}, {_id: '2', name: 'bar'}];
}, 200);
} else {
this.clientCandidates = [];
}
}
}
}
</scripts>
So far so good, but since the component will appear in different pages, so I want to extract a custom component to avoid duplication.
According to the guideline,
v-model="fullName"
is equivalent to
v-bind:value="fullName"
v-on:input="$emit('input', $event)"
So I extracted the select component like this:
<template>
<el-select
v-bind:value="clientId"
v-on:input="$emit('input', $event)"
placeholder="Filter by short name"
filterable="true"
remote="true"
:remote-method="filter"
:loading="loading">
<el-option
v-for="item in clients"
:key="item._id"
:label="item.name"
:value="item._id">
</el-option>
</el-select>
</template>
<scripts>
export default {
props: {
clientId: {
type: String,
required: true
}
},
data() {
return {
clients: [],
loading: false,
}
},
methods: {
filter(query) {
if (query !== '') {
this.loading = true;
setTimeout(() => {
this.loading = false;
this.clients = [{_id: '1', name: 'foo'}, {_id: '2', name: 'bar'}];
}, 200);
} else {
this.clients = [];
}
}
}
}
</scripts>
And the parent component looks like this:
<select-client v-model="filterForm.clientId"></select-client>
The select drop down works fine, but unfortunately, the select does not reveal the option I selected, it remains empty after I choose an option. I suspect that maybe I should switch the v-on:input to 'v-on:change', but it does not work either.
UPDATE
I created a simple example, you can clone it here, please checkout the el-select-as-component branch. Run
npm install
npm run dev
You will see a simple page with 3 kinds of select:
The left one is a custom component written in raw select, it works fine.
The middle one is a custom component written in el-select, the dropdown remains empty but you can see the filterForm.elClientId in the console once you click Filter button. This is why I raise this question.
The right one is a plain el-select, it works fine.
The guideline says v-model is equivalent to v-bind:value and v-on:input but if you look closer, in the listener function, the variable binded is set with the event property. What you do in your exemple isn't the same, in your listener you emit another event. Unless you catch this new event, your value will never be set.
Another thing is you can't modify a props, you should consider it like a read-only variable.
If you want to listen from the parent to the emitted event into the child component, you have to do something like this
<template>
<el-select
:value="selected"
#input="dispatch"
placeholder="Filter by short name"
:filterable="true"
:remote="true"
:remote-method="filter"
:loading="loading">
<el-option
v-for="item in clients"
:key="item._id"
:label="item.name"
:value="item._id">
</el-option>
</el-select>
</template>
<script>
export default {
name: 'SelectClient',
data() {
return {
selected: '',
clients: [],
loading: false,
}
},
methods: {
filter(query) {
if (query !== '') {
this.loading = true;
setTimeout(() => {
this.loading = false
this.clients = [{_id: '1', name: 'foo'}, {_id: '2', name: 'bar'}]
}, 200)
} else {
this.clients = []
}
},
dispatch (e) {
this.$emit('input', e)
this.selected = e
}
}
}
</script>
NB: a v-model + watch pattern will work too. The important thing is to $emit the input event, so the v-model in the parent will be updated.
And in your parent you can use this component like this: <select-client v-model="clientId"/>.
Tips: if you want to modify the same data in different place, you should have a single source of truth and prefer something like vuex. Then your component will be like this
<template lang="html">
<select
v-model="clientId">
<option
disabled
value="">Please select one</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
</template>
<script>
export default {
data () {
return {
clientId: ''
}
},
watch: {
clientId (newValue) {
// Do something else here if you want then commit it
// Of course, listen for the 'setClientId' mutation in your store
this.$store.commit('setClientId', newValue)
}
}
}
</script>
Then in your other components, you can listen to $store.state.clientId value.

Vue selectize, getting json data from option?

I'm using vue2-selectize to display list of options fetched from axios call:
<template>
<selectize v-model="selected" :settings="settings">
<option v-for="option in options" :value="option.id">
({{ option.author }}) - {{ option.description }}
</option>
</selectize>
</template>
<script>
export default {
props: ['old-value'],
data() {
return {
selected: this.oldValue,
options: [],
settings: {
preload: true,
placeholder: "Search All Authors",
dropdownParent: 'body',
closeOnSelect: true,
render: {
option: function (data) {
console.log(data);
return '<div>' +
data.displayName +
'<div class="item-bio">' +
data.bio +
'</div>';
}
},
load: async (query, callback) => {
axios.get(route('api.showAllAuthors')).then(response => {
this.options = response.data;
callback();
})
}
},
}
},
}
</script>
The issue is that once you setup the <option> for the select you can only work with the two values it passes to the render function (text and value) and not the original object (whereas the actual <option> has access to this object).
The manual mentions the optional parameter of dataAttr available for <option> tags, but setting <option :data-data="option"> has no effect and I still can't access the original properties.
How can I access the original JSON attributes that the <option> is using in the render function?
var value = selectizeObject.getValue(),
optionJson = selectizeObject.options[value];
selectizeObject is object for given select / input. For example with jQuery:
var selectizeObject = $('#my-select')[0].selectize;