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>
Related
<div class="col-lg-3 col-md-6 shopItem Yellow XL Puma">
<div class="products-item ">
<div class="product-img">
<img class='img-fluid' src="img/kamizelka2.jpg" alt="kamizelka zółta">
<div class="overlay">
Kup teraz
</div>
</div>
<div class="product-content">
<div class="product-price">
<span class="new-price">11zł</span>
<span class="old-price">23zł</span>
</div>
<h5 class='product-name'>Kamizelka Dwa</h5>
</div>
</div>
</div>
I have a problem that is hard for me to explain.
I tried to make this simple filter for shop items.
Presently this function only takes one last selected option (I mean I cannot find green xl Adidas item, but only one of these options, which change when I use the assigned select element.
Any ideas on how to fix it?
function filterAction () {
const types = document.querySelectorAll('.form-control');
const storeProducts = document.querySelectorAll('.shopItem');
types.forEach((type)=>{
type.addEventListener('change',(e)=>{
storeProducts.forEach((product)=> {
product.style.display='none';
if(type.value =="All" ) {
product.style.display ="block"
} else {
if (product.classList.contains(type.value)) {
product.style.display = 'block'
} else {
product.style.display = "none"
}
}
})
})
})
}
filterAction();
<div class="search-item d-flex text-center">
<div class="form-group col-sm-4">
<label for="exampleFormControlSelect1">Color</label>
<select class="form-control" id="colorLabel">
<option data-filter="all">All</option>
<option data-filter="yellow">Yellow</option>
<option data-filter="green">Green</option>
<option data-filter="orange">Orange</option>
</select>
</div>
<div class="form-group col-sm-4">
<label for="exampleFormControlSelect1">Size</label>
<select class="form-control" id="exampleFormControlSelect1">
<option data-filter="all">All</option>
<option data-filter="s">S</option>
<option data-filter="m">M</option>
<option data-filter="l">L</option>
<option data-filter="xl">XL</option>
</select>
</div>
<div class="form-group col-sm-4">
<label for="exampleFormControlSelect1">Brand</label>
<select class="form-control" id="exampleFormControlSelect1">
<option data-filter="all">All</option>
<option class='options' data-filter="adidas">Adidas</option>
<option class='options' data-filter="puma">Puma</option>
<option class='options' data-filter="gucci">Gucci</option>
</select>
</div>
</div>
<!-- begin snippet: js hide: false console: true babel: false -->
Your code has some flaws, if you select some filter, go back to a previous one and select "All", the entire store will be displayed, even with filters saying to not. Of course, there are many ways to make a filter with vanilla JavaScript (and some with jQuery too), I made one trying to not change a lot of your code.
First, change your product's HTML and add a data attribute to each filter, because using class to it can be a little messy:
<div class="shopItem" data-color="green" data-size="xl" data-brand="adidas">
<div class="products-item ">
<div class="product-img">
<div class="overlay">
Green XL Adidas
</div>
</div>
<div class="product-content">
<h5 class='product-name'>Green XL Adidas</h5>
</div>
</div>
</div>
We will have a dict with all your currently filter desires:
const appliedFilters = {};
It will be populated every time a filter is changed, being like:
{
size: "S",
color: "All",
brand: "Puma"
}
And finally, the filterAction function:
function filterAction () {
const types = document.querySelectorAll('.form-control');
const storeProducts = document.querySelectorAll('.shopItem');
const appliedFilters = {};
types.forEach((type)=>{
type.addEventListener('change', function(e) {
appliedFilters[this.id] = this.value;
console.log(appliedFilters);
storeProducts.forEach(function(product) {
let canShow = true;
// Iterate all applied filters to determine if the product can be displayed or not
Object.keys(appliedFilters).forEach(function (key) {
// If it is 'All' option, we still can display the product
if (appliedFilters[key].toLowerCase() !== 'all' && product.getAttribute('data-' + key).toLowerCase() !== appliedFilters[key].toLowerCase()) {
canShow = false;
}
})
if (canShow) {
product.style.display = 'block';
}else{
product.style.display = 'none';
}
})
})
})
}
DEMO:
function filterAction() {
const types = document.querySelectorAll('.form-control');
const storeProducts = document.querySelectorAll('.shopItem');
const appliedFilters = {};
types.forEach((type) => {
type.addEventListener('change', function(e) {
appliedFilters[this.id] = this.value;
console.log(appliedFilters);
storeProducts.forEach(function(product) {
let canShow = true;
// Iterate all applied filters to determine if the product can be displayed or not
Object.keys(appliedFilters).forEach(function(key) {
// If it is 'All' option, we still can display the product
if (appliedFilters[key].toLowerCase() !== 'all' && product.getAttribute('data-' + key).toLowerCase() !== appliedFilters[key].toLowerCase()) {
canShow = false;
}
})
if (canShow) {
product.style.display = 'block';
} else {
product.style.display = 'none';
}
})
})
})
}
filterAction();
.search-item {
margin-bottom: 10px;
}
<div class="search-item d-flex text-center">
<div class="form-group col-sm-4">
<label for="exampleFormControlSelect1">Color</label>
<select class="form-control" id="color">
<option data-filter="all">All</option>
<option data-filter="yellow">Yellow</option>
<option data-filter="green">Green</option>
<option data-filter="orange">Orange</option>
</select>
</div>
<div class="form-group col-sm-4">
<label for="exampleFormControlSelect1">Size</label>
<select class="form-control" id="size">
<option data-filter="all">All</option>
<option data-filter="s">S</option>
<option data-filter="m">M</option>
<option data-filter="l">L</option>
<option data-filter="xl">XL</option>
</select>
</div>
<div class="form-group col-sm-4">
<label for="exampleFormControlSelect1">Brand</label>
<select class="form-control" id="brand">
<option data-filter="all">All</option>
<option class='options' data-filter="adidas">Adidas</option>
<option class='options' data-filter="puma">Puma</option>
<option class='options' data-filter="gucci">Gucci</option>
</select>
</div>
</div>
<div class="shopItem" data-color="green" data-size="s" data-brand="puma">
<div class="products-item ">
<div class="product-img">
<div class="overlay">
Green S Puma
</div>
</div>
<div class="product-content">
<h5 class='product-name'>Green S Puma</h5>
</div>
</div>
</div>
<div class="shopItem" data-color="green" data-size="xl" data-brand="adidas">
<div class="products-item ">
<div class="product-img">
<div class="overlay">
Green XL Adidas
</div>
</div>
<div class="product-content">
<h5 class='product-name'>Green XL Adidas</h5>
</div>
</div>
</div>
I have 3 drop downs primary, secondary,ternary categories, each dependent on each other, Initially I have 3 drop downs below that I have one button " add more" , after clicking "add more" again drops will come below the ones earlier have, now the question is first row drop down selection is working fine , after clicking "add more" the second row drop down selection is not working , means it changes the value of already selected first row of the second category same with the ternary category. first I all load all the primary category, based on primary id i will fetch secondary categories, based on the secondary category id i will fetch ternary category. please me with this.
HTML CODE
<div class="row Space_2">
<div class="col-md-4">
<select class="Textfield_2" id="primary_category_id" formControlName="primary_category_id" (change)="getSecondCategory($event.target.value)" name="primaryServices" required>
<option value="">Primary Service</option>
<option *ngFor="let primaryCat of primaryCategory" [value]="primaryCat.id">{{primaryCat.name}}</option>
</select>
</div>
<div class="col-md-4">
<select class="Textfield_2" id="secondary_category_id" formControlName="secondary_category_id" (change)="getTernaryCategory($event.target.value)" name="secondaryServices" required>
<option value="">Secondary Service</option>
<option *ngFor="let secondCat of secondCategory" [value]="secondCat.id">{{secondCat.name}}</option>
</select>
</div>
<div class="col-md-4">
<select class="Textfield_2" id="ternary_category_id" formControlName="ternary_category_id" name="secondaryServices" required>
<option value="">Ternary Service</option>
<option *ngFor="let ternaryCat of ternaryCategory" [value]="ternaryCat.id">{{ternaryCat?.name}}</option>
</select>
</div>
</div>
<div *ngFor="let k of addmoreServices let i = index">
<div class="row Space_2">
<div class="col-md-4">
<select class="Textfield_2" id="primary_category" (change)="getSecondCategory($event.target.value)" name="{{k.primary_category}}" required>
<option value="">Primary Service</option>
<option *ngFor="let a of primaryCategory" [value]="a.id">{{a?.name}}</option>
</select>
</div>
<div class="col-md-4">
<select class="Textfield_2" id="secondary_category" (change)="getTernaryCategory($event.target.value)" name="{{k.secondary_category}}" required>
<option value="">Secondary Service</option>
<option *ngFor="let b of secondCategory" [value]="b.id">{{b?.name}}</option>
</select>
</div>
<div class="col-md-4">
<select class="Textfield_2" id="secondary_category" (change)="getTerId($event.target.value)" name="{{k.ternary_category}}" required>
<option value="">Ternary Service</option>
<option *ngFor="let c of ternaryCategory" [value]="c.id">{{c?.name}}</option>
</select>
</div>
</div>
</div>
TypeScript Code:
getPrimaryCategory() {
this.http.get('http://localhost:3000/api/getPrimaryCategory' ,{
})
.subscribe(
res => {
this.primaryCategory = res['data'];
console.log(this.primaryCategory);
},
err => {
}
);
}
getSecondCategory(id,i) {
this.primcatId = id;
this.http.get('http://localhost:3000/api/getsecondarycatdataforternary/'+id ,{
})
.subscribe(
res => {
this.secondCategory = res['data'];
console.log(this.secondCategory);
},
err => {
}
);
}
getTernaryCategory(id) {
console.log("The ternary ID is",id);
this.secondId = id;
this.http.get('http://localhost:3000/api/getternaryCatforServices/'+id ,{
})
.subscribe(
res => {
this.ternaryCategory = res['data'];
console.log(this.ternaryCategory);
},
err => {
}
);
}
getTerId(id){
this.terid = id;
console.log("THE TERNARY ID IS",this.terid);
}
addMoreServices() {
this.addmoreServices.push({ primary_category:this.primcatId , secondary_category:this.secondId ,ternary_category: this.terid });
console.log("the add more services",this.addmoreServices);
}
You need to add trackBy to your *ngFor directives. You can track by id and thanks you this Angular won't treat values after refreshing as new values.
I have a select drop-down that i populate from an api, i want to be able to populate a second select drop-down based on the user's first choice and subsequently populate a second drop-down based on the user's second choice.
Say i have my input fields so
form.component.html
<div class="form-group col-sm-6">
<label> Category</label>
<select class="form-control" [(ngModel)]="product.productCategory" [formControl]="productForm.controls['productCategory']" require>
<option *ngFor="let item of categorys" [value]="item.slug">{{item.name}}</option>
</select>
</div>
<div class="form-group col-sm-6">
<label> Product Type</label>
<select class="form-control" [(ngModel)]="product.productType" [formControl]="productForm.controls['productType']" require>
<option *ngFor="let item of productTypes" [value]="item.slug">{{item.name}}</option>
</select>
</div>
<div class="form-group col-md-6">
<label>Sub-Category</label>
<select class="form-control" [(ngModel)]="product.subCategory" [formControl]="productForm.controls['subCategory']" require>
<option *ngFor="let item of subs" [value]="item.slug">{{item.name}}</option>
</select>
</div>
As it is i am binding the whole list to each individual select drop-down but i want the subCategory to be only those under the selected category and same then productType based on the subCategory selected.
This is how i retrieve the category as it is the parent selection
form.component.ts
fetchCategorys() {
this.categorySrv.fetchCategories().then((response: any) => {
this.categorys = response;
console.log(this.categorys);
})
.catch(error => this.error = error)
}
I am using same method to get the subCategory and productType respectively. As you can see it brings all the items in each section from db but i want to be able to bind subCategory based on the choice of category and also bind productType based on the choice of subCategory.
Note that console.log(this.categorys) displays the category with their respective subCategory and productType but i can't figure out how to make the binding correspond.
Template
<div class="form-group col-sm-6">
<label> Category</label>
<select class="form-control" (change)="categoryChange($event)" [(ngModel)]="product.productCategory" [formControl]="productForm.controls['productCategory']" require>
<option *ngFor="let item of categorys" [value]="item.slug">{{item.name}}</option>
</select>
</div>
<div class="form-group col-sm-6">
<label> Product Type</label>
<select class="form-control" (change)="productTypeChanged($event)" [(ngModel)]="product.productType" [formControl]="productForm.controls['productType']" require>
<option *ngFor="let item of productTypes" [value]="item.slug">{{item.name}}</option>
</select>
</div>
<div class="form-group col-md-6">
<label>Sub-Category</label>
<select class="form-control" [(ngModel)]="product.subCategory" [formControl]="productForm.controls['subCategory']" require>
<option *ngFor="let item of subs" [value]="item.slug">{{item.name}}</option>
</select>
</div>
Component
public allProductTypes: ProductType[];
public allSubs: Category[];
public categoryChange( $event: Category ) {
this.productTypes = this.allProductTypes.filter( _productType => _productType.belongsTo( $event));
}
public productTypeChanged( $event: ProductType ) {
this.subs = this.allSubs.filter( _sub => _sub.belongsTo( $event ) );
}
So you bind your to the dropdown change events. Then, each time a category or product type is chosen, we filter the data that the dropdowns have available to show.
You will also probably have to reset the downstream choices, aka. when changing category, then reset product type and sub cat, because the new top level category might not allow for the old type and subcat values to exist.
I'm trying to bind selected value in dropdown to my article which has property of same type as dropdowns source is. But somehow when I console log my article there is no value in my property which should hold an selected dropdown value.
Here is my code:
In typescript I have :
article: Article;
mainGroups: Group[];
subGroups: Group[];
On init I'm filling mainGroups and subGroups with data, like this:
ngOnInit() {
this._groupService.getAll().subscribe(groups => this.mainGroups = groups);
this._groupService.getAllSubGroups().subscribe(subgroups => this.subGroups = subgroups);
}
Later in html I'm looping values from my mainGroups and subGroups like this:
<div class="form-group">
<label class="control-label dash-control-label col-xs-3">Main group:</label>
<div class="col-xs-9">
<select class="form-control dash-form-control select2" style="width: 100%;"
data-minimum-results-for-search="Infinity" name="articleGroups" required [(ngModel)]="article.mainGroup">
<option disabled [ngValue]="null">-/-</option>
<option [ngValue]="group" *ngFor="let group of mainGroups">{{group.title}}</option>
</select>
</div>
</div>
<!--Sub group-->
<div class="form-group">
<label class="control-label dash-control-label col-xs-3">Sub group:</label>
<div class="col-xs-9">
<select class="form-control dash-form-control select2" style="width: 100%;" name="subGroup" required [(ngModel)]="article.subGroup">
<option disabled [ngValue]="null">-/-</option>
<option [ngValue]="subgroup" *ngFor="let subgroup of subGroups">{{subgroup.title}}</option>
</select>
</div>
</div>
As you can see guys I also wrote : [(ngModel)]="article.mainGroup" on first dropdown, and I said also: [ngValue]="group" *ngFor="let group of mainGroups"
So basically this [ngValue]="group" should get value out of *ngFor and store it into article.mainGroup?
But when I do an console log I can not see article.mainGroup property at all, even if it's defined there in article.ts model.. so that means article.mainGroup property is empty ( because is not visible in console.log(article)?
I guess you need something like this:
<select class="form-control" [(ngModel)]="size" name="sizeValue" #sizeValue="ngModel">
<option *ngFor="let size of sizes" [value]="size">{{size}}</option>
</select>
Note the #sizeValue="ngModel"
See my original answer here:
https://stackoverflow.com/a/46866804/1546042
I am using AngularJS and I need to set a selected option of a drop-down list control when a record is selected for editing and loaded to a form. The dropdown should still have the other values available if the user wants to select a different one.
When there is no record selected, the form should still have the list of values available if the user wants to create a new record.
Down below is the screen shot of my form when it first loads. You can see in the screen shot that the Instruments drop-down-list shows all the instruments available. This list is loaded through a web service that is called when the form loads.
It puts all the contents into an array named: $scope.instruments
Code:
//Get Instruments
$http.get('/api/Instrument/GetAllInstruments').success(function (data, status, headers, config) {
$scope.instruments = data;
}).error(function (data, status, headers, config) {
$scope.error = "An error has occurred while fetching Instruments." + data;
});
Below the form, there is a grid with all the records:
When a user clicks on the edit icon, the record loads on the form:
The record has values for the instrument, style, and scoring but I do not know how to use those values from the record, to set the selected value on the drop down.
This is the code that I am using to load the selected record and assign instrument id, style id, scoring id. Is not the most elegant code, but this is as far as I have been able to get:
//Edit Store Page
$scope.edit = function () {
if (this.page.SPPreambleID === null || this.page.SPPreambleID === 0)
{
this.page.SPPreambleID = -1;
}
$http.get('/api/StorePage/GetStorePage?StorePageID=' + this.page.StorePageID + '&SPPreambleID=' + this.page.SPPreambleID).success(function (data) {
$scope.updateShow = true;
$scope.addShow = false;
$scope.newpage = data;
angular.forEach($scope.newpage.SPAttributeRefID, function (attribute, index) {
if (attribute == 1) {
$scope.recordInstrument = $scope.newpage.AttributeID[0];
}
if (attribute == 2) {
$scope.recordStyle = $scope.newpage.AttributeID[1];
}
if (attribute == 3) {
$scope.recordScoring = $scope.newpage.AttributeID[2];
}
});
}).error(function () {
$scope.error = "An Error has occured while Editing this Store Page!" + data;
});
}
And this is the HTML for the drop downs:
#* Instrument *#
<div class="form-group">
<div class="col-sm-10 space">
<select class="form-control" name="instrumentfilter" data-ng-model="instrumentfilter" required
data-ng-options="i.ID as i.Description for i in instruments track by i.ID" data-ng-change="">
<option value="">-- Choose a Style --</option>
</select>
</div>
</div>
#* Style *#
<div class="form-group">
<div class="col-sm-10 space">
<select class="form-control" name="stylefilter" data-ng-model="stylefilter" required
data-ng-options="st.ID as st.Description for st in styles track by st.ID" data-ng-change="">
<option value="">-- Choose a Style --</option>
</select>
</div>
</div>
#* Scoring *#
<div class="form-group">
<div class="col-sm-10 space">
<select class="form-control" name="scoringfilter" data-ng-model="scoringfilter" required
data-ng-options="sc.ID as sc.Description for sc in scorings track by sc.ID">
<option value="">-- Choose a Scoring --</option>
</select>
</div>
</div>
Please any help I can get in this forum would be great. I have been struggling with this all day, and I am sure it should not be too complicated.
Update
This is the updated HTML after recommendation to modified data-ng-model to use the scope variables recordInstrument, recordStyle, and recordScoring.
#* Instrument *#
<div class="form-group">
<div class="col-sm-10 space">
<select class="form-control" name="instrumentfilter" data-ng-model="recordInstrument" required
data-ng-options="i.ID as i.Description for i in instruments track by i.ID" data-ng-change="">
<option value="">-- Choose a Style --</option>
</select>
</div>
</div>
#* Style *#
<div class="form-group">
<div class="col-sm-10 space">
<select class="form-control" name="stylefilter" data-ng-model="recordStyle" required
data-ng-options="st.ID as st.Description for st in styles track by st.ID" data-ng-change="">
<option value="">-- Choose a Style --</option>
</select>
</div>
</div>
assuming that JS code
angular.forEach($scope.newpage.SPAttributeRefID, function (attribute, index) {
if (attribute == 1) {
$scope.recordInstrument = getRecordInstrument($scope.newpage.AttributeID[0]);
}
if (attribute == 2) {
$scope.recordStyle = getRecordStyle($scope.newpage.AttributeID[1]);
}
if (attribute == 3) {
$scope.recordScoring = getRecordScoring($scope.newpage.AttributeID[2]);
}
});
get correct IDs, and there are 3 functions getRecordInstrument, getRecordStyle and getRecordScoring which fetch instrument, style or scoring objects by its ID, try to assign these IDs to selects via ngModel directive:
#* Style *#
<div class="form-group">
<div class="col-sm-10 space">
<select class="form-control" name="instrumentfilter" data-ng-model="recordInstrument" required
data-ng-options="i.ID as i.Description for i in instruments track by i.ID">
<option value="">-- Choose a Style --</option>
</select>
</div>
</div>
#* Style *#
<div class="form-group">
<div class="col-sm-10 space">
<select class="form-control" name="stylefilter" data-ng-model="recordStyle" required
data-ng-options="st.ID as st.Description for st in styles track by st.ID">
<option value="">-- Choose a Style --</option>
</select>
</div>
</div>
#* Scoring *#
<div class="form-group">
<div class="col-sm-10 space">
<select class="form-control" name="scoringfilter" data-ng-model="recordScoring" required
data-ng-options="sc.ID as sc.Description for sc in scorings track by sc.ID">
<option value="">-- Choose a Scoring --</option>
</select>
</div>
</div>