Using object property in select - angularjs

I currently have the following select:
<select ng-model="current_event"
ng-options="event.event_id for event in current_level.events.event"
ng-change="currentEventChanged()">
</select>
current_level.events.event is an array of objects which looks like this:
[
{
"event_id": 0,
"event_type": {
"collision": {
"object_id1": 0,
"object_id2": 1,
"allow_overlap": "no"
}
}
},
{
"event_id": 1,
"event_type": {
"player_input": {
"object_id": 0,
"level_id": 0,
"allow_overlap": "no"
}
}
}
]
The select works perfectly, but the text for each option is just the event_id, e.g. "0" or "1". I want the text for an item to actually be the event_type (with underscores converted to spaces), e.g. "collision" or "player input". Is this possible in AngularJS?

You're using event.event_id, so there is nothing wrong in the behaviour of AngularJS. You should simply put the right label in the ng-options of <select> (see the documentation).
However, you need to call a function before in order to obtain the correct label, since what you want is pretty complex.
$scope.getLabel = function (eventType)
{
var firstKey = null;
angular.forEach(eventType, function (value, key)
{
if (firstKey === null)
{
firstKey = key;
}
});
return firstKey.replace('_', ' ', 'g');
};
<select
ng-model="current_event"
ng-options="event.event_id as getLabel(event.event_type) for event in current_level.events.event"
ng-change="currentEventChanged()"
>
</select>
jsFiddle

Related

can not map array inside of object

I am lost here, can not find a way how to map through an array that is inside of object. I have received an object from API that includes an array wit values like this:
{
"shelters": [
{
"id": 1,
"name": "Útulok pre psov - TEZAS"
},
{
"id": 2,
"name": "OZ Tuláčik Brezno"
}
]
}
now I need to iterate through the array that is inside and take action on every item to populate the DOM.
my code is:
render() {
// const { isPending, shelters} = this.props;
if (this.props.isPending === false) {
var listOfShelters = this.props.shelters;
console.log('loaded', typeof(listOfShelters), listOfShelters)
return (
<div>
<select style={this.selector} name="útulky" onChange={this.sendId}>
{ listOfShelters.shelters.map(function(shelter, i) {
<option id={shelter.id}>{shelter.name}</option>
})}
</select>
</div>
)
} else if (this.props.isPending === true) {
console.log('fetching')
return (
<select style={this.selector} name="útulky" onChange={this.sendId}>
<option id='0'> Nahravam Data... </option>
</select>
)}
}
I get typeof always an object even if I do this.props.shelters.shelters which should be direct access to that array. if I do this.props.shelters.shelters.map nothing happens, my list of shelters does not get populated. it stays empty. Where do I make the mistake here?
I have found what the problem was. I had different set of parentheses in the .map function, I was using {} instead of (). Now it works like a dream.

AngularJS nested filter hiding elements without destined property

I'm using AngularJS on a project and I need to implement a select box with a filter to a nested property. My object (I have an array of those and I'm iterating through them via ng-repeat) has a structure similar to this:
{
id: 1,
name: 'Example',
groups: [
{ id: 1, name: 'Group 1' },
{ id: 2, name: 'Group 2' }
]
}
I need to filter the group ID of the elements, and after searching I've come up with two ways to do it:
1.
| filter: { $: { id: search.item_id } }
Which has these problems:
Apparently it searches for any nested properties named ID, so if I have more than one object inside my main object, with a property called ID too, it would add it to the filter. Example:
{
id: 2,
name: 'Example 2',
groups: [
{ id: 1, name: 'Group 1' },
{ id: 2, name: 'Group 2' }
],
categories: [
{ id: 1, name: 'Cat 1' },
{ id: 2, name: 'Cat 2' }
]
}
Filtering for ID 1 would select not only group with ID 1, but category with ID 1 too.
Also, with this method, even before setting the filter (search.item_id model is null), objects without groups are being filtered and not appearing in the list. These objects are like this:
{
id: 3,
name: 'Example 3',
groups: []
}
and the other way is:
2.
| filter: { groups: [{ id: search.item_id }] }
In this case, the problem is that it simply doesn't work, it filters everything, leaving the list blank, no matter if it's set or which option is selected.
How can I make it work? I've been searching and couldn't find anything about this. Filtering nested properties is (or should be) a very basic thing.
Update:
So, xtx first solution kinda did it, but only if I use input text or number, but I need to use a select box (more specifically uib-dropdown, but working in a regular select is the next step). My select box is looking like this:
<select name="filter_classes_groups_test" ng-model="search.group_id">
<option val="group.id" ng-repeat="group in classesGroups">{{ group.name }}</option>
</select>
When I interact with it, nothing happens.
If creating a custom filter works for you, here is how you can do that:
app.filter('groupsFilter', function() {
return function(input, groupId) {
var out = [];
if (!groupId || isNaN(parseInt(groupId))) {
return input;
}
angular.forEach(input, function(item) {
if (item.groups && angular.isArray(item.groups)) {
angular.forEach(item.groups, function (group) {
if (group.id === parseInt(groupId)) {
out.push(item);
}
});
}
});
return out;
}
});
As you can see, the custom filter has name groupsFilter, and takes group id to search for as a parameter. The filter can be applied like this:
<div ng-repeat="item in data | groupsFilter:search.item_id">
...
</div>
UPDATE:
Instead of creating a custom filter, you can just create a function that implements filtering logic, in scope like this:
$scope.groupsFilterLocal = function(value) {
if (!$scope.search.item_id || isNaN(parseInt($scope.search.item_id))) {
return true;
}
if (!value || !value.groups || !angular.isArray(value.groups)) {
return false;
}
for (var i = 0; i < value.groups.length; i++) {
if (value.groups[i].id === parseInt($scope.search.item_id)) {
return true;
}
}
return false;
};
and then apply it using build-in filter like this:
<div ng-repeat="item in data | filter:groupsFilterLocal ">
...
</div>
Notice that in this case you can't pass the value to search for (search.item_id) into your function groupsFilterLocal like it is done for the custom filter groupsFilter above. So groupsFilterLocal has to access search.item_id directly
UPDATE 2: How to create a select properly
The reason why the filter is not applied when you pick a group in your dropdown, is in the way how you defined the select. Instead of the id of the selected group, search.group_id gets assigned group's name.
Try defining the select like shown below:
<select name="filter_classes_groups_test" ng-model="search.item_id" ng-options="group.id as group.name for group in classesGroups">
<option value="">-- Choose Group --</option>
</select>
To ensure that search.item_id gets id of the group that is selected (and not its name), try temporarily adding {{ search.item_id }} somewhere in your template.

Comparing objects from two scopes to provide a value

I'll try to simplify the problem as much as I can.
Let's say I have 2 scopes
$scope.section1 = [
{label: 'label1'},
{label: 'label2'}
];
$scope.section2 = [
{value: 'one'},
{value: 'two}
];
Those scopes are used to generate buttons with ng-repeat
<button ng-repeat="item in section1 type="button">{{item.label}}</button>
and
<button ng-repeat="item in section2 type="button">{{item.value}}</button>
Now what I would like to do it to create a third scope that would attach values to the combinations of objects from the two previous ones, say:
$scope.combo = [
{ section1.label:label1 + section2.value: one = 'result1' },
{ section1.label:label2 + section2.value: one = 'result2' },
{ section1.label:label1 + section2.value: two = 'result3' },
{ section1.label:label2 + section2.value: two = 'result4' }
];
Now here comes the tricky part. What I would need to do, is to add a function that would take the values of clicked ng-repeat buttons from each section and then display the results based on the third scope in an input field or something.
So, if you click the button with label:label1 and the one with value:two the input field would show result3.
I'm very green when it comes to Angular and I have no idea how to approach it, especially that all values are strings.
If I understand correctly you could setup your combo something like ...
$scope.combo = {
"label1": {
"one": "result1",
"two": "result2"
},
"label2": {
"one": "result3",
"two": "result4"
}
}
You can then reference the correct value as combo[valueFromButton1][valueFromButton2] where valueFromButton1 and valueFromButton2 point at a model that contains the result of the clicked buttons. Your controller function then just needs to tie everything together by updating the model when the buttons are clicked.
See this plunkr ... https://embed.plnkr.co/GgorcM/
Without changing much you can also try like below provided code snippet.Run it to check the demo.
var app = angular.module('app', []);
app.controller('Ctrl',['$scope' ,function($scope) {
var key1, key2;
$scope.click = function(type, item) {
if (type == 'label') {
key1 = item;
} else if (type == 'val') {
key2 = item;
}
$scope.key = key1 + '+' + key2;
angular.forEach($scope.combo, function(val, key) {
if(val[$scope.key]){
$scope.finalVal = val[$scope.key];
}
});
};
$scope.section1 = [{
label: 'label1'
}, {
label: 'label2'
}];
$scope.section2 = [{
value: 'one'
}, {
value: 'two'
}];
$scope.combo = [{
'label1+one': 'result1'
}, {
'label2+one': 'result2'
}, {
'label1+two': 'result3'
}, {
'label2+two': 'result4'
}];
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='app' ng-controller='Ctrl'>
<button ng-repeat="item in section1" ng-click="click('label',item.label)" type="button">{{item.label}}</button>
<button ng-repeat="item in section2" ng-click="click('val',item.value)"type="button">{{item.value}}</button>
<input type="text" ng-model="finalVal"/>{{key}} {{finalVal}}
</div>

AngularJS filter already selected option from dynamic field

I have a form where you can add x number of fields. Each field contains option select. I want to filter out the already chosen option when this option is already chosen in one or multiples field before. Each field has a remove button and the form has 1 add button.
How can I filter out the dynamic fields?
Any help,guidance is most welcome.Thanks in advance. :)
This is how my HTML looks like:
<div data-ng-repeat="choice in choices">
<select data-ng-model="choice.option"
data-ng-options="item as item.Value for item in options">
</select>
<button data-ng-click="removeChoice(choice)">Remove choice</button>
<div>
<button data-ng-show="choices.length <= 4" data-ng-click="addNewChoice()">Add Choice</button>
</div>
</div>
And my controller:
$scope.options = [
{
"Key": "0",
"Value": "Select an option"
},
{
"Key": "Option1",
"Value": "Option1"
},
{
"Key": "Option2",
"Value": "Option2"
},
{
"Key": "Option3",
"Value": "Option3"
},
{
"Key": "Option4",
"Value": "Option4"
},
{
"Key": "Option5",
"Value": "Option5"
}
];
$scope.choices = [{ id: '1' }];
$scope.addNewChoice = function () {
var newItemNo = $scope.choices.length + 1;
$scope.choices.push({ id: newItemNo, option: $scope.option, value: $scope.value });
};
$scope.removeChoice = function () {
var index = $scope.choices.indexOf(choice);
$scope.choices.splice(index, 1);
};
ok
i can give simple recommendation which will be this.
1: add variable $scope.selectedOptions = [];
this will contain list of already selected options from all select elements .
2: create function $scope.AddSelectedOption(item);
this will add the selected object when we change option from any select element because we are going to use for all selects ng-change= "AddSelectedOption(item);"
3: add checkIfSelected(item); this will check if given object value is already selected or not ..
will user in
hope you understand what it will do just check like this
$scope.checkIfSelected = function (item) {
$scope.selectedFound = $scope.selectedOptions.filter(function
(option) {
if(option.value == item.value)
{
return day;
}
});
if($scope.selectedFound.length == 0 ) { return false; } else {
return true; }
}
This will return true if give item found in the options.
if not out.. you can invite me to help again .
This is possible. I'm explaining a basic version of this requirement. See the working example here http://plnkr.co/edit/S9yZpjhY55lXsuifnUAc?p=preview
What wer are doing is maintaining another options which is the copy of the original options. Copying the options will make it to not reference existing options since objects are pass by reference in Javascript.
The main logic is in this function, which modify the options on selection:
$scope.optionSelected = function(choice) {
$scope.availableOptions = $scope.availableOptions || angular.copy($scope.options);
if (choice.option) {
var index = -1;
// See if available options has that key
angular.forEach($scope.availableOptions, function(item, i) {
if (item.Key === choice.option.Key) {
index = i;
}
});
if (index > -1) {
// And then remove it
$scope.availableOptions.splice(index, 1);
}
}
};

select box : display text 'error' if value not exist in array

I have a key value pair defined as below, which is being used for select using ng-options
$scope.BucketEnum = [
{ display: 'Error', value: 0 },
{ display: '1', value: 1 },
{ display: '2', value: 2 },
{ display: '3', value: 3 },
{ display: '4', value: 4 },
{ display: '5', value: 5 },
{ display: 'Flows', value: 125 },
{ display: 'Recovery', value: 151 }
];
I am using this key value pair to display select box in ng-options
<select ng-model="selectedBucket" ng-options="row.value as rows.display for row in BucketEnum" multiple="multiple" ></select>
now if I set ng-model i.e. $scope.selectedBucket = 10, I want to display the text Error. Is it possible to show value Error for all the values which are not there in $scope.BucketEnum array.
NOTE
I am looking at a more generic way to do this e.g a filter for doing this
SCENARIO
There is certain historical data in database, which has some garbage and some good data.
For each garbage value, i need to show the current garbage value as well as the valid values to select from, so for the end users to fix it.
Would this fit your needs ?
jsfiddle
app.filter('bootstrapValues', function(){
return function(initial, baseBucket){
var result = [];
for(var i=0; i<initial.length; i++){
var flag = false;
for(var j=1; j<baseBucket.length; j++){ //from 1 or 0.. you call
if(initial[i] === baseBucket[j].value){
flag = true;
result.push(baseBucket[j]);
break; // if there are repeated elements
}
}
if(!flag)
result.push(baseBucket[0])
}
return result;
};
});
Using it to start the selectedBucket, in your controller:
// setting initials
$scope.selectedBucket = $filter('bootstrapValues')(initialSet, $scope.bucketEnum);
Does it help?
Edit: Here is other jsfiddle with little modifications, if the value is not in the bucket it add the element to the list with Error display and as a selected value.
Using ng-options generates multiple HTML <select> elements for each item in your BucketEnum array and 'returns' the selected value in your ng-model variable: selectedBucket. I think the only way to display the options without an additional blank entry is to ensure the value of selectedBucket is a valid entry in BucketEnum.
Your question states:
if I set ng-model i.e. $scope.selectedBucket = 10, I want to display
the text Error.
I assume you want to display the value: {{BucketEnum[selectedBucket].display}}
So... starting with $scope.selectedBucket = 10, we want some generic way of implementing a select using ng-options which will reset this value to a default.
You could do this by implementing an attribute directive, allowing you to write:
<select ng-model="selectedBucket" select-default="BucketEnum"
ng-options="row.value as row.display for row in BucketEnum"
multiple="multiple">
An example of this approach is shown below. Note that this assumes the default value is zero and does not handle multiple selections (you'd have to iterate over the selections when comparing to each item in BucketEnum and decide what to do if there is a mix of valid and invalid selections).
app.directive("selectDefault",function(){
return{
restrict: 'A',
scope: false,
link:function(scope,element,attrs){
var arr= scope[attrs.selectDefault]; // array from attribute
scope.$watch(attrs.ngModel,function(){
var i, ok=false;
var sel= scope[attrs.ngModel]; // ng-model variable
for( i=0; i<arr.length; i++){ // variable in array ?
if( arr[i].value == sel ) // nasty '==' only for demo
ok= true;
}
if( ! ok )
scope[attrs.ngModel]=0; // set selectedBucket to 0
});
}
};
});
I've run up a jsfiddle of this here
The downside of this is that I've used a $watch on the ng-model which causes side-effects, i.e. any assignment of the named variable will trigger the $watch function.
If this is the sort of solution you were looking for, you could expand the directive in all sorts of ways, for example:
<select ng-model="selectResult"
select-default="99" array="BucketEnum" initial="selectedBucket"
ng-options="row.value as row.display for row in BucketEnum"
multiple="multiple">
...the idea being that the select-default directive would read the default value ("99" here), the array and an initial value then set selectResult accordingly
You would need to code for this explicitly. Scan the choices you want to set against the choices that are present. If you don't find it, select the Error value too.
Note also that you need to pass an array for selectedBucket and it needs to include the actual option objects not just the values inside them.
<div ng-app="myApp">
<div ng-controller="myController">
<p>Select something</p>
<select ng-model="selectedBucket"
ng-options="row as row.display for row in bucketEnum" multiple="multiple">
</select>
</div>
</div>
.
var app = angular.module('myApp', []);
app.controller('myController', function ($scope) {
var initialSet = [1, 5, 10];
$scope.bucketEnum = [
{ display: 'Error', value: 0 },
{ display: '1', value: 1 },
{ display: '2', value: 2 },
{ display: '3', value: 3 },
{ display: '4', value: 4 },
{ display: '5', value: 5 },
{ display: 'Flows', value: 125 },
{ display: 'Recovery', value: 151 }
];
var selected = [];
var error = $scope.bucketEnum[0];
angular.forEach(initialSet, function(item) {
var found;
angular.forEach($scope.bucketEnum, function (e) {
if (+item == +e.value) {
console.log('Found ', e);
found = item;
selected.push(e);
}
});
if (typeof found === 'undefined') {
selected.push(error);
}
$scope.selectedBucket = selected;
console.log(selected);
});
});

Resources