AngularJS unique filter not working - javascript

I have the following code and I cant get it to work with unique filter.
Also on jsfiddle it isn't working. In my HTML, the select is populated but unique filter is not working.
https://jsfiddle.net/krayziekry/sw8dvy3u/
Thank you
<div ng-app="example" ng-controller="exampleCtrl">
<!-- Select Basic -->
<div class="form-group span6">
<label class="control-label" for="from">Calatorind de la</label>
<div>
<select id="from" name="from" class="form-control" ng-model="selected.valp">
<option value="">Selecteaza...</option>
<option ng-repeat="rec in myRecs | filter: {vals: selected.vals} | orderBy:'plecare' | unique: 'plecare'" value="{{rec.valp}}">{{rec.plecare}}</option>
</select>
</div>
</div>
<!-- Select Basic -->
<div class="form-group span6">
<label class="control-label" for="to">catre</label>
<div>
<select id="to" name="to" class="form-control" ng-model="selected.vals">
<option value="">Selecteaza...</option>
<option ng-repeat="rec in myRecs | filter: {valp: selected.valp} | orderBy:'plecare'" value="{{rec.vals}}">{{rec.sosire}}</option>
</select>
</div>
</div>
</div>
<script>
angular.module('example', []).controller('exampleCtrl', function($scope) {
$scope.myRecs = [{
valp: 'AHO',
plecare: 'Alghero (AHO)',
sosire: 'Torino - Caselle (TRN)',
vals: 'TRN'
}, {
valp: 'ATH',
plecare: 'Atena (ATH)',
sosire: 'Constanta (CMD)',
vals: 'CMD'
}, {
valp: 'ATH',
plecare: 'Atena (ATH)',
sosire: 'Larnaca (LCA)',
vals: 'LCA'
}, {
valp: 'ATH',
plecare: 'Atena (ATH)',
sosire: 'Londra - Luton (LTN)',
vals: 'LTN'
}];
});
</script>

As far i Know the unique filter needs to be called from another module 'ui.filters'.
Sorry if my english its not hat good.

I forked your jsfiddle here
You will need to create a custom filter like this:
.filter('unique', function() {
return function(collection, keyname) {
var output = [],
keys = [];
angular.forEach(collection, function(item) {
var key = item[keyname];
if (keys.indexOf(key) === -1) {
keys.push(key);
output.push(item);
}
});
return output;
};
})
And use it like this:
<option ng-repeat="rec in myRecs | unique: 'valp' | orderBy:'plecare'" value="{{rec.valp}}">

Related

How to handle two selects fields in a reactive Angular form?

I have a screen where I create elements dynamically using reactive form, basically I create cards, where each has two selection fields:
Scenario: when I add a card and select a layout, the options of that specific layout are loaded in the select of assets through a service that makes the filter in the API, however when I add another card and select some other option in the layout select the two selects of assets are left with the same option.
Template
<div class="card"
formArrayName="scriptOperationOrders"
*ngFor="let workstation of formOperation.get('scriptOperationOrders')['controls']; index as idx"
>
<div class="card-body" [formGroupName]="idx">
<div class="form-row">
<div class="form-group col-md-1">
<label>Rank</label>
<input type="text" name="rank" class="form-control" formControlName="rank"/>
</div>
<div class="form-group col-md-2">
<label>Layout</label>
<select formGroupName="layout" (ngModelChange)="searchAssetsByLayouts($event)">
<option value="">Choose Layout</option>
<option
*ngFor="let lay of (layouts$ | async)?.dataArray "
[value]="lay.id">{{ lay.description }}
</option>
</select>
</div>
<div class="form-group col-md-2">
<label>Asset</label>
<select formGroupName="asset">
<option value="">Choose Asset</option>
<option
*ngFor="let asset of (assets$ | async)?.dataArray "
[value]="asset.id">{{ asset.description }}
</option>
</select>
</div>
</div>
</div>
</div>
Controller
layouts$: Observable<IResponse<ILayoutModel>>;
assets$: Observable<IResponse<IAssetModel>>;
ngOnInit() {
...
this.buildFormOperation();
this.layouts$ = this.layoutService.listLayouts();
this.providers$ = this.providerService.listProviders();
}
buildFormOperation() {
this.formOperation = this.fb.group({
script: [],
descriptionCode: [],
description: [],
scriptOperationOrders: new FormArray([])
})
}
searchAssetsByLayouts(layoutId: number) {
this.assets$ = this.assetService.listAssetsRoots(layoutId); // The assets$ variable is overridden here
}
Asset listing Service
listAssetsRoots(layoutId?: number | string): Observable<IResponse<IAssetModel>> {
return this.apiService.crudeList({
url: `${this.url}/roots`,
queryParams: {
layoutId
},
})
.pipe(map((res) => {
return new IResponse<IAssetModel>(res, IAssetModel);
}));
}
As you could be doing when selecting an option in the select layout, the options for that layout are loaded only in the select of assets on the same card
this.assets$ = this.assetService.listAssetsRoots(layoutId); as The assets$ variable is overridden here
ts file:-
//declared what type of response is expected
interface Assets{
id:number,
description :string
}
//initially set to empty
assets$ : Observable<Assets[]> = of([]);
//accepting second argument idx as row_index
searchAssetsByLayouts(layoutId: number,row_index:number) {
this.assets$[row_index] = this.assetService.listAssetsRoots(layoutId); // The assets$ variable is no more overridden
}
//html:-
//here used formControlName for 'layout' and 'asset' control insted of formGroupName
//and passed 'idx' as second parameter to (ngModelChange)="searchAssetsByLayouts($event,idx)
<div class="card"
formArrayName="scriptOperationOrders"
*ngFor="let workstation of formOperation.get('scriptOperationOrders')['controls']; index as idx"
>
<div class="card-body" [formGroupName]="idx">
<div class="form-row">
<div class="form-group col-md-1">
<label>Rank</label>
<input type="text" name="rank" class="form-control" formControlName="rank"/>
</div>
<div class="form-group col-md-2">
<label>Layout</label>
<select formControlName="layout" (ngModelChange)="searchAssetsByLayouts($event,idx)">
<option value="">Choose Layout</option>
<option
*ngFor="let lay of (layouts$ | async)?.dataArray "
[value]="lay.id">{{ lay.description }}
</option>
</select>
</div>
<div class="form-group col-md-2">
<label>Asset</label>
<select formControlName="asset">
<option value="">Choose Asset</option>
<option
*ngFor="let asset of (assets$[idx] | async)"
[value]="asset.id">{{ asset.description }}
</option>
</select>
</div>
</div>
</div>
</div>

Hide option when selected by another Select

I have two select's who uses the same list. When i choose the option "Foo" from select A the same option "Foo" must be hidden on select B. Any ideia how to do it with AngularJS ? I'm trying something like this
<div class="col-md-6">
<label>
Testemunha 1 :
</label>
<select select-normal data-placeholder="..." ng-model="notificacaoOrientativa.testemunha1"
ng-options="obj as obj.pessoa.nome for obj in lstTestemunha">
<option></option>
</select>
</div>
<div class="col-md-6">
<label>
Testemunha 2 :
</label>
<select select-normal data-placeholder="..." ng-model="notificacaoOrientativa.testemunha2"
ng-options="obj as obj.pessoa.nome for obj in lstTestemunha ">
<option></option>
</select>
</div>
$scope.esconderTestemunhaSelecionada = function(obj){
if($scope.lstTestemunha.includes(obj)){
$scope.lstTestemunha.style.display = "none";
return $scope.lstTestemunha;
}
}
You could use a filter on your second select. This will filter the items in the second select such that the item selected in the first does not appear. If you need it to be more complex (for example, this will cause the second select to not have any options until something is selected in the first select which may be an undesirable side effect) you can always write your own custom filter.
angular.module('app', [])
.controller('ctrl', function($scope) {
$scope.selectItems = [];
$scope.selectedItemA = {};
$scope.selectedItemB = {};
for (let i = 1; i <= 10; i++) {
$scope.selectItems.push({
id: i,
name: 'Item #' + i
});
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<div>
Select Item A:
<select ng-model="selectedItemA" ng-options="item as item.name for item in selectItems"></select>
</div>
<div>
Select Item B:
<select ng-model="selectedItemB" ng-options="item as item.name for item in selectItems | filter: { id: '!' + selectedItemA.id }"></select>
</div>
</div>
I solved this problem not the way that i'd like but works fine.
<div class="col-md-6">
<label>
Testemunha 1 :
</label>
<select select-normal data-placeholder="..." ng-model="notificacaoOrientativa.testemunha1"
ng-options="obj as obj.pessoa.nome for obj in lstTestemunha">
<option></option>
</select>
</div>
<div class="col-md-6">
<label>
Testemunha 2 :
</label>
<select select-normal data-placeholder="..." ng-model="notificacaoOrientativa.testemunha2" ng-disabled="!notificacaoOrientativa.testemunha1"
ng-options="obj as obj.pessoa.nome for obj in lstTestemunha | filter: {pessoa:{ id: '!' + notificacaoOrientativa.testemunha1.pessoa.id }}">
<option></option>
</select>
</div>
I'm forcing the user to set the first select and filter the second according to the first.
By the way, sry my poor english.

Basic read of json fails

It's very simple. I have 2 selects. When the user selects the category, it will appear another select with a subcategory.
The problem is that subcategory select is always empty.
Category JSON (vm.categories):
[
{
"doctype":"1120",
"description":"bla bla",
"subcategories":[
{
"#id":1,
"subcategory":"1",
"description":"New Offer",
"templates":[
{
"template":"12",
"description":"asfafasga",
"deliveryChannels":[
]
}
]
},
{
"#id":2,
"subcategory":"2",
"description":"New Offer",
"templates":[
{
"template":"1121",
"description":"asfag",
"deliveryChannels":[
{
"deliveryType":"4",
"description":"Test"
}
]
}
]
}
]
}
]
HTML
<div class="row">
<div class="col-sm-12">
<!-- Categories -->
<label for="category-select"><b>Category </b></label>
<select name="category-select" ng-model="vm.selectedCategory" required>
<option value="" disabled>--- Please select a category ---</option> <!-- not selected / blank option -->
<option value="{{category}}"
ng-repeat="category in vm.categories">
{{category.description}}
</option>
</select>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<!-- Sub Categories -->
<label ng-show="vm.selectedCategory" for="subcategory-select"><b>Sub-Category </b></label>
<select name="subcategory-select"
ng-show="vm.selectedCategory"
ng-model="vm.selectedSubCategory">
<option value="" disabled>--- Please select a sub-category ---</option> <!-- not selected / blank option -->
<option value="{{subCategory}}"
ng-repeat="subCategory in vm.selectedCategory.subcategories">
{{subCategory.description}}
</option>
</select>
</div>
</div>
Any idea why this is happening? Somehow I can't read the subcategories array from the selected category
EDIT: If I put <span>{{vm.selectedCategory}}</span> in my html, it shows the json above. but if I put <span>{{vm.selectedCategory.subcategories}}</span> it's null
Another edit: Even <span>{{vm.selectedCategory.doctype}}</span> is null
It is because your selected value is not evaluated as object but as string. To handle this issue you can:
You can either convert selected string to object with ng-change directive on first select and the following function
$scope.convert = function(){
$scope.vm.selectedCategory = angular.fromJson($scope.vm.selectedCategory)
}
or use ng-options
<select name="mySelect" id="mySelect" ng-options="category.description for category in data" ng-model="vm.selectedCategory">
</select>
Demo
To make a working demo, had to simplify vm.[namedVariables] into simple variables: kindly check below
var app = angular.module("myApp", []);
app.controller("myCtrl", function($scope) {
$scope.selectedCategory = '';
$scope.categories = [{
"doctype": "1120",
"description": "bla bla",
"subcategories": [{
"#id": 1,
"subcategory": "1",
"description": "New Offer",
"templates": [{
"template": "12",
"description": "asfafasga",
"deliveryChannels": [
]
}]
},
{
"#id": 2,
"subcategory": "2",
"description": "New Offer2",
"templates": [{
"template": "1121",
"description": "asfag",
"deliveryChannels": [{
"deliveryType": "4",
"description": "Test"
}]
}]
}
]
}];
/*
for(var i=0; i<$scope.profiles.length; i++){
console.log($scope.profiles[0].TravelerExtended);
console.log($scope.profiles[0].TravelerExtended.ExtendedInt_1);
}
*/
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<div ng-repeat='item in categories'>
{{item.description}}
<div ng-repeat='subC in item.subcategories'>
{{subC.subcategory}} - {{subC.description}}
</div>
</div>
<hr/>
<div class="row">
<div class="col-sm-12">
<!-- Categories -->
<label for="category-select"><b>Category </b></label>
<select name="category-select" ng-model="selectedCategory" required>
<option value="" disabled>--- Please select a category ---</option>
<!-- not selected / blank option -->
<option value="{{category.doctype}}" ng-repeat="category in categories">{{category.description}}</option>
</select>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<!-- Sub Categories -->
<label ng-show="selectedCategory" for="subcategory-select"><b>Sub-Category </b></label>
<ng-container ng-repeat="item in categories">
<!--
<p>(1) {{selectedCategory}} </p>
<p>(2) {{item.doctype}} </p>
-->
<select name="subcategory-select" ng-show="selectedCategory == item.doctype" ng-model="vm.selectedSubCategory">
<option value="" disabled>--- Please select a sub-category ---</option>
<!-- not selected / blank option -->
<option value="{{subCategory}}" ng-repeat="subCategory in item.subcategories">
{{subCategory.description}}</option>
</select>
</ng-container>
</div>
</div>
</div>
Instead of using the value attribute with interpolation, use the ng-value directive:
<div class="row">
<div class="col-sm-12">
<!-- Categories -->
<label for="category-select"><b>Category </b></label>
<select name="category-select" ng-model="vm.selectedCategory" required>
<option value="" disabled>--- Please select a category ---</option> <!-- not selected / blank option -->
̶<̶o̶p̶t̶i̶o̶n̶ ̶v̶a̶l̶u̶e̶=̶"̶{̶{̶c̶a̶t̶e̶g̶o̶r̶y̶}̶}̶"̶
<option ng-value="category"
ng-repeat="category in vm.categories">
{{category.description}}
</option>
</select>
</div>
</div>
The double curly bracket interpolation directive attempts to convert the category value to a string. This does not work well when the category value is an object.
For more information, see AngularJS ng-value Directive API Reference.
It says [Object Object] when I do <span>{{vm.selectedCategory}}</span> and i can't read the subcategories
Pipe the vm.selectedCategory into the json filter:
<span>{{ vm.selectedCategory | json }}</span>
This allows you to convert a JavaScript object into JSON string.
This filter is mostly useful for debugging. When using the double curly notation the binding is automatically converted to JSON.
For more information, see AngularJS json Filter API Reference.

Select multiple display names instead of IDs using AngularJS

I have a multiple select like this :
<select ng-model="listProds" multiple>
<option value="10">product 1</option>
<option value="25">product 2</option>
<option value="35">product 3</option>
<option value="48">product 4</option>
</select>
The values are the Ids for these products ( and this selectbox is generated using PHP )
& I've got this simple code in my app.js file :
var app = angular.module('myapp', []);
app.controller("PurchasesController", function($scope) {
// Init products Array
$scope.listProds = [];
});
When I display the listProds like this {{ listProds }}, I get an array containing the current selected items, but it only shows the Ids like this if I select all of them ["10","25","35","48"].
<fieldset ng-show="listProds.length > 0">
<div data-ng-repeat="p in listProds track by $index">
{{ p }} <!– Or –> {{ listProds[$index] }}
<input type="text" name="pr{{ listProds[$index] }}" />
<input type="text" name="qt{{ listProds[$index] }}" />
</div>
</fieldset>
This code generate two text boxes to enter the Price and Quantity for each Product in selected from the selectbox. So instead of using {{ p }} or {{ listProds[$index] }} and displaying the Product Id, I want to display there the Product name.
Thank you in advance.
You can create two lists: one for all your products and a separate list for the selected products:
$scope.listProds = [
{ key: 10, value: 'Product 1' },
{ key: 25, value: 'Product 2' },
{ key: 35, value: 'Product 3' },
{ key: 45, value: 'Product 4' }
];
$scope.selectedProds = [];
Now in your markup, instead of writing out each option in your select manually, you can use ng-options to generate your options. Using this approach, you are basically saying that each option is an object, and you want to use the objects value as the display name.
<select ng-model="selectedProds" ng-options="prod.value for prod in listProds" multiple>
Now your $scope.selectedProds array will contain the product objects, and not just they keys. So now you can display the name easily:
<fieldset ng-show="selectedProds.length > 0">
<div data-ng-repeat="p in selectedProds track by $index">
{{ p.value }}
<input type="text" name="pr{{ selectedProds[$index] }}" />
<input type="text" name="qt{{ selectedProds[$index] }}" />
</div>
</fieldset>
Not sure what your want the name attribute of the inputs to be, but I hope you get the idea.
Try this.
var app = angular.module('selTest', []);
app.controller('MainCtrl', function($scope) {
$scope.selectedProducts = [];
$scope.products = [
{ id:1, name: 'POne' },
{ id:2, name: 'PTwo' },
{ id:3, name: 'PThree' }
];
$scope.getNames = function(prods) {
return prods.map(function(p) {
return p.name;
});
};
$scope.getIds = function(prods) {
return prods.map(function(p) {
return p.id;
});
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="selTest">
<div ng-controller="MainCtrl">
<select name="products"
class="form-control input-sm"
ng-model="selectedProducts"
ng-options="p.name for p in products track by p.id"
ng-style="{'width':'100%'}" multiple>
</select>
<div>Selected Names: {{ getNames(selectedProducts) }}</div>
<div>Selected Ids: {{ getIds(selectedProducts) }}</div>
</div>
</div>

Disable optgroup of multiple select which has specific label

I have taken a select list with multiple option using ng-options. I am using it as below:
<select ng-options="c as c.Text for c in ReceiverList track by c.Value" ng-model="ReceiverUserID" class="form-control" id="ddlReceiverUserID" ng-change="GetWorkers(ReceiverUserID, SenderUserID,'receiver')">
<option value="">--Select Supervisor--</option>
</select> <!-- Parent Drop Down-->
<select multiple ng-options="c as c.Text group by c.Group
for c in receiveList track by c.Value" ng-model="Workers"
class="form-control" id="ddlWorkers" size="10">
</select> <!-- Child Drop Down -->
This select dropdown get filled when I select some item from another dropdown.(It's a kind of cascaded dropdown). Filling it like below:
$scope.GetWorkers = function (objA, objB, ddlType) {
$http.post("user/Workers/", { userID: objA.Value })
.success(function (data, status, headers, config) {
if (ddlType == 'sender') {
$scope.sendList = data;
} else {
$scope.receiveList = data;
$('#ddlWorkers optgroup[label="Current Users"] option').prop('disabled', true); // Not working
}
})
.error(function (data, status, headers, config) {
showToast(ToastType.error, "Error occured while fetching workers.", "Failure");
});
}
I want to disable child dropdown's specific group items. So I tried below code but it is not working:
$('#ddlWorkers optgroup[label="Current Users"] option').prop('disabled', true);
I don't know how do I disable specific group items of select whenever its data changes or new data loaded.
Here is HTML output in which I want to disable all optgroup[label="Current Users"] members:
<select multiple="" ng-options="c as c.Text group by c.Group for c in receiveList track by c.Value" ng-model="Workers" class="form-control ng-pristine ng-valid" id="ddlWorkers" size="10">
<optgroup label="Current Users">
<option value="4118">Kevins Numen</option>
<option value="4119">ggdfg fdgdfg</option>
</optgroup>
<optgroup label="New Users">
<option value="1093">Case Worker</option>
</optgroup>
</select>
I don't know much about angularjs or ng-options, but it seems that everybody else used some timeout to let the process populate the received data to the input, and you can try something this like others:
...
} else {
$scope.receiveList = data;
setTimeout(function(){
$('#ddlWorkers optgroup[label="Current Users"] option')
.prop('disabled', true); // Not working
}, 100);
}
...
maybe not similar but good to have a look here:
is there a post render callback for Angular JS directive?
You need to customize your code and also JSON Object
<div ng-controller="AjaxCtrl">
<h1>AJAX - Oriented</h1>
<div>
Country:
<select id="country" ng-model="country" ng-options="country for country in countries">
<option value=''>Select</option>
</select>
</div>
<div>
City: <select id="city" ng-disabled="!cities" ng-model="city"><option value='' ng-repeat="item in cities" ng-disabled="item.disabled">{{item.name}}</option></select>
</div>
<div>
Suburb: <select id="suburb" ng-disabled="!suburbs" ng-model="suburb" ng-options="suburb for suburb in suburbs"><option value='' ng-disabled="item.disabled" ></option></select>
</div>
</div>
Your Angular
function AjaxCtrl($scope) {
$scope.countries = ['usa', 'canada', 'mexico', 'france'];
$scope.$watch('country', function(newVal) {
if (newVal)
$scope.cities = [
{ id: 1, name: '11111'},
{ id: 2, name: '22222', disabled: true },
{ id: 3, name: '33333', disabled: true }
]
});
$scope.$watch('city', function(newVal) {
if (newVal) $scope.suburbs = ['SOMA', 'Richmond', 'Sunset'];
});
}
function AjaxCtrl($scope) {
$scope.countries = ['usa', 'canada', 'mexico', 'france'];
$scope.$watch('country', function(newVal) {
if (newVal)
$scope.cities = [
{ id: 1, name: '11111'},
{ id: 2, name: '22222', disabled: true },
{ id: 3, name: '33333', disabled: true }
]
});
$scope.$watch('city', function(newVal) {
if (newVal) $scope.suburbs = ['SOMA', 'Richmond', 'Sunset'];
});
}
<link href="http://fiddle.jshell.net/css/normalize.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js"></script>
<div ng-controller="AjaxCtrl" class="ng-scope">
<h1>AJAX - Oriented</h1>
<div>
Country:
<select id="country" ng-model="country" ng-options="country for country in countries" class="ng-valid ng-dirty"><option value="" class="">Select</option><option value="0">usa</option><option value="1">canada</option><option value="2">mexico</option><option value="3">france</option></select>
</div>
<div>
City: <select id="city" ng-disabled="!cities" ng-model="city" class="ng-valid ng-dirty"><!-- ngRepeat: item in cities --><option value="" ng-repeat="item in cities" ng-disabled="item.disabled" class="ng-scope ng-binding">11111</option><option value="" ng-repeat="item in cities" ng-disabled="item.disabled" class="ng-scope ng-binding" disabled="disabled">22222</option><option value="" ng-repeat="item in cities" ng-disabled="item.disabled" class="ng-scope ng-binding" disabled="disabled">33333</option></select>
</div>
<div>
Suburb: <select id="suburb" ng-disabled="!suburbs" ng-model="suburb" ng-options="suburb for suburb in suburbs" class="ng-pristine ng-valid" disabled="disabled"><option value="" ng-disabled="item.disabled" class=""></option></select>
</div>
</div>
Reference

Categories

Resources