I'm having issues looping over an object (a json response from a get request).
Is it not possible to use for ... in ... in a vue js method? or inside an axios method?
Even if I replace the loop to a dummy loop like so, I get no output to my console.
fetchData(id){
this.$Progress.start()
axios.get(base_path+'/admin_api/testDetailByUser/'+id)
.then(response => {
ob ={"a":1,"b":2,"c":3}
for (i in ob){
console.log(ob[i]);
}
this.$Progress.finish()
}).catch(error=>{
this.$Progress.fail()
});
},
The actual code looks something more like this:
export default {
data(){
return {
form : new Form({
id :'',
examiner_id :[],
}),
}
},
methods:{
fetchData(id){
this.$Progress.start()
axios.get(base_path+'/admin_api/testDetailByUser/'+id)
.then(response => {
this.myData = response.data;
$('#detailDiv').modal('show');
this.form.examiner_id = [];
for (property in this.myData){
this.form.examiner_id.push(this.myData[property].id);
}
this.$Progress.finish()
}).catch(error=>{
this.$Progress.fail()
});
},
with the following html excerpt
<form>
<tr v-for="(mailPdf,i) in mailPdfs" :key="mailPdf.id">
<select v-model="form.examiner_id[i]" name="examiner_id[i]" id="examiner_id" :value="mailPdf.examiner.id">
<option value="" disabled>choose examiner</option>
<option :value="mailPdf.examiner.id">{{mailPdf.examiner.name}} - current examiner</option>
<option v-for="testExaminer in filteredTestExaminer" :value="testExaminer.examiner.id">{{ testExaminer.examiner.name }}</option>
</select>
</tr>
</form>
the idea is to set the default value for the <select> input, since selected="selected" doesn't work on vuejs, but I can't feed it into data(){ because it has a new value for each individual get request.
Thanks in advance!
you can do that with Object. values()
your code should be like this
...
for (property in Object.values(this.myData)){
this.form.examiner_id.push(this.myData[property].id);
}
...
or just use forEach()
...
Object.values(this.myData).forEach(item => this.form.examiner_id.push(item.id))
...
Related
This is my code, i'm not using webpack, just the vuejs cdn.I want to get the value of the districts in the regionComponent to the another variable in the districtComponent and display it.
const regionsComponent = Vue.component('region-component', {
template: `
<div class="col-lg-6">
<div class="form-group" >
<label class="label-text" for="officeRegion">Region</label> <span class="red">*</span>
<select class="form-control" id="officeRegion"
name="officeRegion" :value = "value" v-on:input ="updateRegion($event.target.value)">
<option value='' >Select region</option>
<option v-for="region in regions" :value="region.regionId">{{region.region}}</option>
</select>
</div>
</div>`,
data() {
return {
regions:[],
}
},
created(){
this.getRegions();
},
props:['value','districts'],
methods: {
getRegions: function() {
let apiurl = document.getElementById("apiurl").value;
let apikey = document.getElementById("apikey").value;
let headers = {
'Content-Type': 'application/json',
'apiKey': apikey
}
axios.get(apiurl+'regions/all', {headers: headers})
.then((res)=>{
console.log(res.data.data[0].region)
if(res.data.responseCode === "01"){
this.regions = res.data.data;
} else {
console.log("failed to load regions")
}
})
},
updateRegion: function(value){
this.$emit('input', value);
// console.log(value)
if(value){
thisdistricts = this.getRegionDistrict(value)
}
},
getRegionDistrict: function (regionId){
let apiurl = document.getElementById("apiurl").value;
let apikey = document.getElementById("apikey").value;
let headers = {
'Content-Type': 'application/json',
'apiKey': apikey
}
axios.get(apiurl+'region/districts/all?regionId='+ regionId, {headers: headers})
.then((res)=>{
console.log(res.data)
if(res.data.responseCode==="01"){
return this.districts = res.data.data
// console.log(this.districts)
}
})
}
}
})
const districtComponent = Vue.component('district-component', {
template:`
<div class="col-lg-6">
<div class="form-group">
<label class="label-text" for="officeDistrict">District</label> <span class="red">*</span>
<select class="form-control" id="officeRegion" name="officeRegion" v-bind="district" v-on:input= "updateDisct($event.target.value)">
<option value='' >Select district</option>
<option v-for="district in districts" :value="district.districtId">{{district.districtName}}</option>
</select>
</div>
</div>`,
props:['district'],
data() {
return {
districts:[],
}
},
methods: {
updateDisct: function(district){
this.$emit('input', district);
console.log(district)
}
}
})
var app = new Vue({
el: '#myForm',
components: {
vuejsDatepicker,
regionsComponent,
},
data: function (){
return {}
}
})
If Vuex is too complicated for now, you can use the native $root when you need a simple way.
I use Vuex with name spaced modules it is brilliant but it is a large learning curve and the documentation is not really that great on it.
To leverage $root you could move regions to to the data attribute of your new Vue({}) instance and reference that from anywhere using this.$root.regions in script or $root.regions in template.
var app = new Vue({
el: '#myForm',
components: {
vuejsDatepicker,
regionsComponent,
},
data: function (){
return {
regions: []
}
}
})
getRegions: function() {
...
axios.get(apiurl+'regions/all', {headers: headers}).then((res)=>{
if(res.data.responseCode === "01"){
this.$root.regions = res.data.data;
} else {
console.log("failed to load regions")
}
})
},
<select ... >
<option value='' >Select region</option>
<option v-for="region in $root.regions" :value="region.regionId">{{region.region}}</option>
</select>
This way any component at any nested level can access a global data set without any sharing faff.
this.$set($root.myGlobal, newValue); can also help out with any
reactivity issues in some cases.
You can use Vuex and have a global store to store your data
With Vuex you can share data/complex state management across components. A Vuex store instance is composed of state, getters, actions, and mutations. In summary, the state "holds" your data, you can retrieve it via your state or the getters. Actions can be async and for example can hold an API call, that later on, you can pass the data from the API call to a mutation that ultimately make the change effectively, mutations cannot be async.
See: The simplest store
All the operations made with Vuex have a trace of what is happening inside your app. It seems a little daunting at first, but actually, it is really easy to manage your data.
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.
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
I have a <select>-element that has a data property bound to it using v-model in Vue.
Sometimes I want to change that value dynamically. I also have an event-listener attached to this element which is triggered on the change-event. See code example:
<template>
<div class="mySelector">
<select id="testSelect" v-model="mySelectModel"
#change="onChange($event)">
<template v-for="(item, index) in someList">
<option :class="['btn', 'btn-default', 'removing-button']" :value="index">{{item.name}}</option>
</template>
</select>
</div>
</template>
<script>
export default {
data() {
return {
mySelectModel: null
}
},
props: {
},
methods: {
customChange: function() {
this.mySelectModel = ... // some value we from somewhere else that is set dynamically on some condiftion
},
onChange: function (event) {
if (!event) return;
// DO SOMETHING THAT WE ONLY WANT TO DO ON A REAL CLICK
}
},
}
</script>
The problem I have is that when I change the data value mySelectModel dynamically, like in the customChange-method, the change event is also called, triggering the method onChange. I only want to do stuff in that method if it was really triggered by a real click, not when it was changed dynamically.
I can not find a way to distinguish between those cases when the change-event is triggered by a click or when it is just changed for some other reason. Any suggestions?
See vue-js-selected-doesnt-triggering-change-event-select-option, it appears that select does not trigger #change when v-model is updated by JS (only when the selected value is changed by user).
A directive can add the functionality
Vue.directive('binding-change', {
update: function (el, binding, vnode) {
const model = vnode.data.directives.find(d => d.name === 'model')
if (model) {
binding.value(model.value)
}
}
})
use like
<select id="testSelect"
v-binding-change="onChange"
v-model="mySelectModel"
#change="onChange($event)">
Not sure about the parameter to onChange - I'll give it a test.
Similar to this suggested solution, you can make a settable computed that you v-model in your widget:
The get function simply returns the data item
The set function does whatever you want a change in the widget to do, in addition to setting the data item
Other code can change the data item directly and will not execute the set code of the computed.
new Vue({
el: '#app',
data: {
values: ['one','two','three'],
selectedItem: 'two'
},
computed: {
wrappedSelectedItem: {
get() { return this.selectedItem; },
set(value) {
console.log("Changed in widget");
this.selectedItem = value;
}
}
},
methods: {
changeToThree() {
console.log("Stealth change!");
this.selectedItem = 'three';
}
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
<select v-model="wrappedSelectedItem">
<option v-for="value in values" :value="value">{{value}}</option>
</select>
<button #click="changeToThree">Set to three</button>
</div>
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;