I'm having trouble properly initializing the select boxes in a form, built with nested ng-repeats. I have parent table rows built with an ng-repeat, and a select box whose value is bound to an attribute in the parent row. The values in the select box are pulled from a nested child array, constituting available selections for the row.
<tr ng-repeat="asv in asvs">
<td>{{asv.scenario_asv_id}}</td>
<td>{{asv.asv_target_id}}</td>
<td><select ng-model="asv.asv_target_id">
<option ng-repeat="version in asv.asv_targets"
value="{{version.asv_target_id}}"
ng-selected="asv.asv_target_id == version.asv_target_id">
ID: {{version.asv_target_id}} - Name: {{version.asv_target_desc}}
</option>
</select>
</td>
</tr>
In my Plunker, you'll see that the first 2 selects initialize properly, yet the 3rd does not. Can someone advise how to implement this properly?
https://plnkr.co/edit/NGcEMNSHCDAYK8UBOKYl
It is recommended that you use ng-options with select elements. Please replace your select code with the example below:
<select convert-to-number
ng-options="version.asv_target_id as ('ID: ' + version.asv_target_id + ' - Name: ' + version.asv_target_desc) for version in asv.asv_targets track by version.asv_target_id"
ng-model="asv.asv_target_id">
</select>
Because your value is numeric you will have to add the following directive as per https://code.angularjs.org/1.4.7/docs/api/ng/directive/select
module.directive('convertToNumber', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
ngModel.$parsers.push(function(val) {
return val != null ? parseInt(val, 10) : null;
});
ngModel.$formatters.push(function(val) {
return val != null ? '' + val : null;
});
}
};
});
The problem happening here is during the creation of select options using ng-repeat
{
"scenario_asv_asv_target_id": "16150-CI101592-3475",
"scenario_asv_id": 16150,
"appl_ci_id": "CI101592",
"asv_target_id": 3475,
"asv_target_desc": "Default General Purpose",
}
If you move your data which are going to match your criteria to the end of the json, your value will get selected.
Because in the loop the select option is getting created,so it resets the already created ng-select values.
For example, I have moved the option which are going to be by default to the end of json set and the value is getting selected.
This is not recommended, for understandging.
Check into the plunker:
https://plnkr.co/edit/G83l6OdIfr5vlNgMSeR7?p=preview
I haven't edited your code, i have just replaced your data set.
So,
you should use ng-options as follows
<tr ng-repeat="asv in asvs">
<td>{{asv.scenario_asv_id}}</td>
<td>{{asv.asv_target_id}}</td>
<td>
<select ng-model="asv.asv_target_id"
ng-options="version.asv_target_id as version.asv_target_desc for version in asv.asv_targets"
ng-selected="true">
</select>
</td>
</tr>
final code
https://plnkr.co/edit/w8nOENAiL72SX68L7LVv?p=preview
disclaimer:
I have concentrate on explaining and helping you to select default option, so don't expect me to prettify the option data by appending data and desc in it. Do it on your own.
Related
I want to create select, which show count of elements in filter. Seems like that
Here is PlunkR with code, that i have wrote. It's great make his job, but i cannot add count of active/deactive elements in select. How can I do this?
Thank's
You can format the values before binding with the select options.
<select ng-options="i as format(i) for i in statusList" ng-model="statusFilter">
</select>
The format function is,
$scope.format = function(val) {
return val.status + ' ' + $scope.objs.filter(function(state) {
return state.status === val.value;
}).length;
}
Where the $scope.objs is the array of values which has active/inactive items.
Here is the working plunkr
I already searched the web but I haven't found a solution to my question yet. Maybe I searched for the wrong terms, so I'm sorry if the solution to my problem might be very simple.
Example-table:
So, what I want to do is the following:
provide a dropdown with several options
every option provides a custom filter (e.g. 'option1' shows all
records that are available, 'option2' shows all records that are
available and have label F, 'option3' shows all records that are
available and have label E, …)
apply the filter immediately 'live'
So, how can I achieve this?
Thanks in advance.
I created ready to use component ( pure js no Jquery needed ) with filtering of table rows by column values. Component use data attributes, it can be customised for filtering any column.
var FilterSelect=function(selectSelector,tableSelector){
this.$select=document.querySelector(selectSelector);//our select
this.$table=document.querySelector(tableSelector); //our table
this.filter=null; //filter value
this.columnNum=null; //column num to check value
this._bind();
//run setting and filtering in beginning ( option can be set )
this._setAndFilter();
};
// method sets parameters from chosen option
FilterSelect.prototype._set=function(){
var option=this.$select.options[this.$select.selectedIndex];//get current option
this.filter=typeof option.dataset.filter!='undefined'?option.dataset.filter:null;
this.columnNum=typeof option.dataset.columnNum!='undefined'?option.dataset.columnNum:null;
};
//method filters table
FilterSelect.prototype._filterTable=function(){
var trs=this.$table.querySelectorAll("tr");
for (var i=0; i<trs.length; i++){
var tr=trs[i];
if (this.columnNum==null){
//no filters
tr.style.display="block"
continue;
}
var td=tr.querySelectorAll("td")[parseInt(this.columnNum)];//get td
var value=td.innerText;//get td inner text to compar
if (value!=this.filter)
tr.style.display="none"; //filter does not match - hide
else
tr.style.display="block";//display block - filter match
}
};
//filtering table and setting options
FilterSelect.prototype._setAndFilter=function(){
this._set();//set current filter and column
this._filterTable();//do table filtering
};
//bind select change event
FilterSelect.prototype._bind=function(){
this.$select.addEventListener("change",function(){
this._setAndFilter();
}.bind(this));
};
//usage
var filterSelect=new FilterSelect('#select','#table');
<select id="select">
<option>No filter</option>
<option data-column-num="3" data-filter="E">Filter Label E</option>
<option data-column-num="2" data-filter="Yes">Filter Available</option>
<option data-column-num="3" data-filter="F">Filter Label F</option>
<option data-column-num="2" data-filter="No">Filter Not Available</option>
</select>
<table id="table">
<tr>
<td>1</td><td>One</td> <td>Yes</td><td>E</td>
</tr>
<tr>
<td>2</td><td>Two</td> <td>Yes</td><td>F</td>
</tr>
<tr>
<td>3</td><td>Three</td> <td>No</td><td>E</td>
</tr>
</table>
Information about data attributes:
data-column-num - number of column to check filter value ( starts from 0 )
data-filter - value of filter to check in text inside chosen by columnNum column
Above component enables filtering by single column value. Here is component with possibility to filter for many columns - https://jsfiddle.net/maciejsikora/0kbubfts/
Can't figure this out for the life of me. Using AngularJS.
I have a dropdown Select field with several options. It is a part of a form that may be completed multiple times (ie "add another" type form). Now, one of the options may only be used once. How can I remove this option from all other select fields after it has been used?
What I'm working with:
html
<select ng-model="item.itemtype">
<option ng-repeat="i in itemtype" value="{{i}}" ng-init="item.itemtype = itemtype[0]">{{i}}</option>
</select>
angularjs
$scope.Items = [
{ 'itemtype': '', 'desc': '', 'color': '' }
];
$scope.itemtype = [
'shirt',
'pants',
'hats',
'shoes',
'special'];
What I've tried (and really doesn't work)
$scope.specialremove = function() {
var exist = Items.indexOf("special")
if (exist !== 0) {
return '';
}
else {
return 'special';
}
}
I'm hoping I don't have to turn to any other framework to solve this. Feel free to point out any other problems/errors/inefficiencies in my code.
The first thing that can help is using ng-options:
ng-options="type for type in itemType".
It would be better to have objects with label and value properties, in order to write it as
ng-options="type.value as type.label for type in itemType"
and separate the displayed label from the actual value of the selection.
In your controller you should have something like:
$scope.itemType= [
...
];
$scope.selectedItem= $scope.itemType[0];
So that you can write the select as:
<select ng-Model="selectedItem" ng-options="item.value as item.label for item in itemType"></select>
To remove a selected item you can watch the value of selectedItem in the controller: when it matches the value you want, remove it from the array and update selectedItem accordingly.
Here is a basic fiddle. I simply remove the third option after selecting it, and reset the selected item to the first.
I have this small demo.
Basically , it's a select element with this data :
address: {
select: {
code: "0",
name: "Select proof of address"
},
letter: {
code: "1",
name: "Letter"
},
photograph: {
code: "2",
name: "Photograph"
}
}
And here is the select
<select ng-model="current.addressCode" ng-options="value.code as value.name for (key,value) in student.address"</select>
Questions:
Question #1 - Looking at the Ng doc -
And so , I try to compare
value.code as value.name for (key,value) in student.address
to the second line ( which i find most appropriate)
select as label for (key , value) in object
What is going on here ?
value.code goes to select ???
Html select element has an option tag with value and inside text like :
<option value="volvo">Volvo</option> thats' all.
What did they mean in their docs ?
Question #2
How can I bind this object of myne to a regular sane value,text select ?
(I want the code to be the value and the name to be as text)
Currently I dont see any value in the dom :
You are correct in understanding that example two fits your needs, that is:
select as label for (key , value) in object
Here, as the documentation mentions:
select: The result of this expression will be bound to the model of the
parent <select> element. If not specified, select expression will
default to value.
So, for your use case, your existing code for the select tag is correctly structured:
<select ng-model="current.addressCode" ng-options="value.code as value.name for
(key,value) in student.address"></select>
value.code value is stored in current.addressCode while, in the select dropdown, you should see value.name as the options label.
EDIT: So, to answer your questions:
Question 1
In this case, "select" is just a variable name - a placeholder if you may like for the documentation to explain about it (which it does further below). In your code, the value of the variable that is used instead of select is the one that gets bound to the select's ng-model (In your case, value.code)
Question 2
Exactly as you have mentioned:
<select ng-model="current.addressCode" ng-options="value.code as value.name for
(key,value) in student.address"></select>
This will give you the necessary value, text select tag automatically. When you select an option, the value.code associated with that option gets stored in current.addressCode
http://jsfiddle.net/7FL76/1/
how about this:
it requires simple change :
address: [
{
code: "0",
name: "Select proof of address"
},
{
code: "1",
name: "Letter"
},
{
code: "2",
name: "Photograph"
}
]
the reason is your Key is same as nested Name ( like, letter - Letter ) , so json is a bit redundant.
as result you will see.
value as a code, and text as a name .
I have a directive as follows. The <select> created by the directive will update when I change the scope var bound to it in ng-model, but when I change the <select> itself it doesn't update the scope var. I think I'm missing registering a $watch in the link, but I'm not sure... Thanks for the help.
template.html
<div ng-repeat="searchField in searchFields">
<select-search-field label="{{searchField.label}}" field="$parent[searchField.field]"></select-search-field>
</div>
resulting html
<div>
<label>Landing Gear</label><br>
<select ng-model="gear">
<option value="">Any</option>
<option value="fixed">Fixed</option>
<option value="retractable">Retractable</option>
</select>
</div>
controller.js
$scope.searchFields = [{
label: 'Landing Gear'
field: 'gear'
}];
$scope.gear = ''; //This is getting sent to directive in "field" (the $parent[] part)
directive.js
.directive('selectSearchField',function() {
function template() {
return '<label>{{label}}</label><br>' +
'<select ng-model="field">' +
'<option value="">Any</option>' +
'<option value="fixed">Fixed</option>' +
'<option value="retractable">Retractable</option>' +
'</select>';
}
return {
restrict: 'E',
scope: {
label: '#',
field: '=',
options: '='
},
template: template()
};
});
Edit: Here are the files on github: https://github.com/andypandy/littleplanefinder (see stuff in public/js/controllers.js and public/js/directives.js
Also: see it live: http://www.littleplanefinder.com
It looks like you are showing the label using {{label}} but updating field, so you won't see updates there since label is never updated.
Also you don't need any of the $parent stuff for the field attribute, it will evaluate it as part of the isolated scope.
I made a fiddle that does what you are looking for http://jsfiddle.net/G5UCm/3/
You can click the get label button to alert the value that the controller sees, so you can be sure that the two way data-binding is set up correctly.
Here are the relevant parts:
// in markup in the scope of the controller
<select-search-field field="searchField.field">
</select-search-field>
// part of the template, make sure to display the item we are changing!
return '<label>{{field}}</label><br>' +
Edit: Updating for the new info
Oh so you are trying to use ng-model on the parent gear property. This is a little weird because you are trying to get around the isolation. If you just don't isolate you can just inherit scope variables. Alternatively, you still pass the field as a two-way data binding and just use the model on the parent, like this:
'<select ng-model="$parent[field]">' +
Here is the updated fiddle: http://jsfiddle.net/G5UCm/4/
Hope this helped!