I am trying to make dynamic select input that populates it's option elements depending on the the response of ajax with the following code. Everything is working fine if the data is hardcoded however when I try to make option dynamic based on the response I don't get reactivity.
In short let's say I have an object {Foo: 'Bar', Lorem: 'Ipsum'} I get
<select>
<option value="Foo">Bar</option>
<option value="Lorem">Ipsum</option>
</select>
My problem is when I want to make the data dynamic using the function below I still get the same option even though I now have {Enet: 'Dolor', Magna: 'Aliqua', mollit: 'Anim'} Is there a better approach to this? Or Am I missing Something.
Function responsible for populating the object:
$.get('/registrar/levels', function (data) {
for (const datum of data) {
addModal.levelFields[datum.name] = datum.id;
}
});
The component:
Vue.component("modal-add-form", {
props: {
formName: String,
formType: [String, Number],
options: Object,
},
template: `<div class="form-group" v-if="formType !== 'select'">
<label :for="formName" v-text="formName"></label>
<input :type="formType" class="form-control" :name="formName" :id="formName" value="" :placeholder="formName">
</div>
<div v-else>
<select :name="formName" class="form-control">
<option v-for="(value, name) in options" :value="value" v-text="name"></option>
</select>
</div>`
});
You shouldn't directly reassign data value because Vue cannot detect property additions and rerender view. You should do it via Vue.set or this.$set like this:
$.get('/registrar/levels', (data) => {
for (const datum of data) {
this.$set(this.addModal.levelFields, datum.name, datum.id)
// addModal.levelFields[datum.name] = datum.id;
}
});
Related
I am retrieving a list of data from an api and need to fill the specific <select></select> tags, which is associated to a few radio button, with some of the data as <options></options>. The radio buttons waiting for an event (#change/#click) and executing and axios get request. Everthing works fine. I click on a radio button and retrieving the data as response (vue tools also showing the right data) but the <option></option> tags are not updating. Now when I click on another radio button, I am getting again the right data from the api BUT now the <option></option> tags are refreshing with the data from the previous response.
Template
<!-- CREATING 7 RADIO BUTTONS FOR THE CURRENT WEEK FROM MON-SUN -->
<div class="wrapper" v-for="item in inputDetails">
<input :id="'datetime[0]['+item.labelText+']'" type="radio" name="datetime[0][date]" v-model="formData.datetime[0].date" :value="item.inputValue" #change="getTimes" />
</div>
<!-- CREATING THE TIME PICKER -->
<select id="datetime[0][time]" name="datetime[0][time]" v-model="formData.datetime[0].time">
<option selected="selected"></option>
<option v-for="item in selectOptionTimes[0]" :value="item.value">{{ item.label }}</option>
</select>
<!--
2 MORE RADIO BUTTON SECTION AND TIME PICKER SECTIONS WITH DIFFERENT INDEXES
<input id="datetime[1][time]"...
-->
Script
data() {
return {
formData: {
datetime: [
{date: '', time: ''},
{date: '', time: ''},
{date: '', time: ''},
]
}
selectOptionTimes: [],
}
},
methods: {
getTimes: function (current) {
let instanceIndex = current.currentTarget.id.match(/(?<=\[)([0-9])(?=])/g)[0]; // getting the index of the current datetime section
axios.get('/api-url', {
params: {
location_id: this.formData.location_id,
date: current.currentTarget.value
}
}).then(response => {
this.selectOptionTimes[instanceIndex] = response.data;
});
}
}
Does someone know what the problem is here?
You cannot assign a value to an arbitrary index within an empty Array in this way. You must either completely replace the Array with values that hydrate that index, or you must use $set.
So, to recap:
BAD
this.selectOptionTimes[instanceIndex] = response.data
GOOD
this.$set(this.selectOptionTimes, instanceIndex, response.data)
Note though, that this has an unintended consequence. If you have an empty array, and call this.$set on an index greater than 0, the array will be filled with empty values up to your index.
What might make more sense is using an {} instead along with this.$set and looping over the Object.keys instead of the array directly.
Fiddle showing $set on index with an empty array
Fiddle showing Object usage instead
How do I set the first option as a default value in dropdownlist?
<select :disabled=true class="custom-select" v-model="Status">
<option disabled value>Select Any</option>
<option v-for="status in statusList" v-bind:value="{StatusId:status.StatusId,StatusName:status.StatusName}" :key="status.StatusId">{{ status.StatusName }}</option>
</select>
Here v-model="Status" is an object. So, when I have set it like below It's not working:
data() {
return {
Status: 1
};
},
Here, 1 is the id of first option.
Status needs to be an object as well if you want it to match.
data() {
return {
Status: { StatusId: 1, StatusName: 'name' }
}
}
All of the properties of your default Status will need to match one of the options in order for it to be selected.
But note that there is probably no good reason here to use this pattern of setting the option value to an object. Better to set it to the StatusId, and use that selected id or a computed to process the option wherever you need to use it.
I am making a request management system for my company.
The requirements are:
-Able to add a new row of the request.
-Choosing the description of the request will generate the parameters to be chosen. Parameters are on the same row as its respective description.
-Store the description and parameter as an array.
To approach this, we've used vue.js to do scripting within a blade template in the Laravel framework.
Vue.component('request', {
props: ["index"],
// Template for every individual row of test
template: `
<tr>
<td>#{{ index }}</td>
<td>
<select :name="description" #change="populate" required>
<option value="" selected disabled>--Select--</option>
#foreach ($types->sortBy('description') as $types)
<option value="{{$types->description}}">{{$types->description}}</option>
#endforeach
</select>
</td>
<td>
<select :name="parameter" required >
<option >#{{ shared.parameter.parameter1 }}</option>
<option >#{{ shared.parameter.parameter2 }}</option>
<option >#{{ shared.parameter.parameter3 }}</option>
</select>
</td>
`,
data(){
return{
// bind the name field of the form, for submission
shared: store,
description: 'tests['+this.index+'][description]',
parameters: 'tests['+this.index+'][parameter]',
something: 2,
}
}
,
methods: {
populate: ()=>{
var self = this.index;
$.ajax({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
url:'/parametersByDescription',//this is specified in web routes
type: 'POST',
data: {description: this.description},
success: function(data){
store.parameter = data;
}
})
return;
}
}
})
let store = {
parameter: [],
index increases with a function in methods of root. A new row is also added when done so. The whole basis of adding another row is the reason a large chunk of the form is generated by the template in vue.component request. The populate function sends my description through data: to the function in my controller specified by the URL.
This is where I start having problems. I need to parse the description I have selected in the form in the populate function, however I can't find the specific term to use. In Vue Devtools, I can see description as one of the data values but I get Uncaught TypeError: Cannot read property 'description' of undefined when I try to parse this.description. I have also hard-coded the value of 2 into something and tried to parse it, however the same error appears. I just need to get this particular description value and everything else should run smoothly. Thank you for your time.
The this in this.description refers to the ajax object, declare let self = this; so it becomes self; self.description.
Also as a side note, use Axios instead of Ajax, it saves you from a lot of trouble.
I made a simple change in syntax together with the suggestion made by #Quinten and it works now.
data: function(){
return{
// bind the name field of the form, for submission
shared: store,
description: 'tests['+this.index+'][description]',
parameters: 'tests['+this.index+'][parameter]',
something: 2, //some placeholder value, I am adding another variable in my actual code along with the template of the component
}
}
,
methods: {
populate: function(){
var self = this.something;
$.ajax({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
url:'/parametersByDescription',//this is specified in web routes
type: 'POST',
data: {description: self},
success: function(data){
store.parameter = data;
}
})
return;
}
}
})
I'm using KnockoutJS to manage my web front-end. I am writing a CRUD/Admin site, but I am having problems with the selectedOptions binding.
The case:
The view model has form.products.all and form.products.selected observables. Both of these are generated by ko.mapping.
The HTML form has a multi-select of the form:
<select required="" multiple="" data-bind="options: products.all, optionsText: function (item) { return item.value.name.unName(); }, selectedOptions: products.selected" class="form-control">
The HTML form correctly shows all the options.
The HTML form does not show the selected options on load. In particular, if I inspect the viewModel object, I can see that the right objects get loaded into the products.selected array on load. But the multi-select does not select them automatically.
If I select objects in the form and then inspect the products.selected observable, I do see the objects in the array.
If I post the form, the right objects end up in the database, and then end up in the viewModel object on the next page load (so the only part missing in the cycle is actually marking the form based on what is in products.selected.
What am I doing wrong? I've seen conflicting advice, and some of it is outdated, so I'm not sure how to proceed.
The selectedOptions binding works as designed.
Your error is very likely that your selected observable does not contain the identical objects (i.e., references to the objects in all), but merely objects that have equal property values.
Knockout maintains the binding through object identity, it does not make any other comparisons.
Consider this simple example:
var vm = {
products: {
selected: ko.observableArray(),
all: ko.observableArray([
{
value: {
name: {
unName: ko.observable("Foo")
}
}
}, {
value: {
name: {
unName: ko.observable("Bar")
}
}
}
])
}
};
vm.products.selected.push(vm.products.all()[1]);
ko.applyBindings(vm);
pre {
font-size: small;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.0.0/knockout-min.js"></script>
<select required="" multiple="" class="form-control" data-bind="
options: products.all,
optionsText: function (item) {
return item.value.name.unName();
},
selectedOptions: products.selected
"></select>
<pre data-bind="text: ko.toJSON($root, null, 2)"></pre>
Unrelated side-note: Try to avoid defining functions in the binding attribute. It's ugly, inefficient, potentially repetitive and not very idiomatic. Make a corresponding property on the view model, for example like this:
function Product(data) {
ko.utils.extend(this, data);
this.displayName = this.value.name.unName;
}
and
<div data-bind="with: products">
<select required="" multiple="" class="form-control" data-bind="
options: all,
optionsText: 'displayName',
selectedOptions: selected
"></select>
</div>
I am using AngularJs + Select2. I am trying to get the data from remote. Below is my code
HTML :
<div class="col-md-4 left">
<input type="text" style="width:300px" ui-select2="multi" ng-model="multi2Value" multiple="multiple" />
</div>
JS : [UPDATED]
$scope.multi = {
ajax: {
headers: { 'Content-Type': 'application/json' },
url: "http://localhost:63591/Lookup?lookup=Lookup&key=acc",
data: function (term, page) {
return {};
},
results: function (data, page) {
console.log(data);
return {results: data.LookupValue};
}
}
And my response will be
{ "LookupValue" : [ "AAAA","BBBB","CCC" ] }
But in the console i am seeing the response. But it is not loading into the select dropdown.
What went wrong in my code. Can anyone plz help me ? Thanks
I think that you must your response save in some scope variable like $scope.listLookup and return it in your ajax, and after this step, try to bind it in HTML like:
<select ui-select2 ng-model="multi2Value" data-placeholder="Pick a Lookup">
<option value=""></option>
<option ng-repeat="lookup in listLookup " value="{{lookup.value}}">{{lookup.text}}</option>
</select>
where lookup is every item in your list if items of listLookup, and value and text are some dummy properties of lookup...
Take look on this article, hope that you understand the idea.
instead of
return {results: data};
use
return {results: data.LookupValue};
It might be either array of primitive types
[string,string,string]
or array of objects
[{},{},{}]
as well as :
data: function (term, page) {
return {
q: term, //search term
page: page // page number
};
},