Reputation: 47
I have an Angular form where I have many select options, these select options are managed by Subscriptions but I am looking there is too much code for every Subscription.
Can someone suggest me a good practice to reduce the amount of code?
Thanks in advance!
Here is an example: https://github.com/jreategui07/testing-subscriptions-form
Mock server runs with: mockserver -p 8080 -m mock
this.colorSC = this.catalogueService.getColors().subscribe(
(response: any) => {
console.log(response.body);
this.colorsData = response.body;
},
error =>
{
console.warn('Error', error);
}
);
this.brandSC = this.catalogueService.getSizes().subscribe(
(response: any) => {
console.log(response.body);
this.brandsData = response.body;
},
error =>
{
console.warn('Error', error);
}
);
this.sizeSC = this.catalogueService.getFruits().subscribe(
(response: any) => {
console.log(response.body);
this.sizesData = response.body;
},
error =>
{
console.warn('Error', error);
}
);
this.fruitSC = this.catalogueService.getColors().subscribe(
(response: any) => {
console.log(response.body);
this.fruitsData = response.body;
},
error =>
{
console.warn('Error', error);
}
);
Upvotes: 1
Views: 123
Reputation: 6527
You can just hook up the observables to the select's option as observable and use the async pipe to bind the data
Declare Observables for options
colorSC$ : Observable<any>;
brandSC$ : Observable<any>;
sizeSC$ : Observable<any>;
fruitSC$ : Observable<any>;
NOTE: Use an interface instead of any I am not sure the data type so for this answer I am using any. You can define your interfaces and use them here to declare these Observable variables
You can now assign these observables, You can do it in
constructor(private fb: FormBuilder, private catalogueService: CatalogueService) {
this.anyForm = this.fb.group({
color: [''],
brand: [''],
size: [''],
fruit: [''],
});
this.colorSC$ = catalogueService.getColors();
this.brandSC$ = catalogueService.getSizes();
this.sizeSC$ = catalogueService.getFruits();
this.fruitSC$ = catalogueService.getColors();
}
and in your template use the async pipe to get the items eg: colorSC$ | async
Note that there are safe checks for code and description so that the template doesn't error out item?.code
and item?.description
<form (ngSubmit)="onSubmit()" [formGroup]="anyForm">
<label>Color:</label>
<select formControlName="color">
<option *ngFor="let item of colorSC$ | async" [value]="item?.code">{{ item?.description }}</option>
</select>
<br><br>
<label>Brand:</label>
<select formControlName="brand">
<option *ngFor="let item of brandSC$ | async" [value]="item?.code">{{ item?.description }}</option>
</select>
<br><br>
<label>Size:</label>
<select formControlName="size">
<option *ngFor="let item of this.sizeSC$ | async" [value]="item?.code">{{ item?.description }}</option>
</select>
<br><br>
<label>Fruit:</label>
<select formControlName="fruit">
<option *ngFor="let item of fruitSC$ | async" [value]="item?.code">{{ item?.description }}</option>
</select>
<br><br>
<button type="submit">Ok</button>
</form>
In your service you do not have to map them as you are doing nothing useful you are returning the same thing
export class CatalogueService {
requestOptions: any;
constructor(public http: HttpClient) {
const headers = new HttpHeaders({
'Content-Type': 'text/plain; charset=utf-8'
});
this.requestOptions = {headers, observe: 'response'};
}
getColors() {
return this.http.get('http://localhost:8080/getColors', this.requestOptions).pipe(map(res => res.body));
}
getBrands() {
return this.http.get('http://localhost:8080/getBrands', this.requestOptions).pipe(map(res => res.body));
}
getSizes() {
return this.http.get('http://localhost:8080/getSizes', this.requestOptions).pipe(map(res => res.body));
}
getFruits() {
return this.http.get('http://localhost:8080/getFruits', this.requestOptions).pipe(map(res => res.body));
}
}
So you can get rid of ngOnInit ngOnDestroy since you won't be needing them for this use case. Its better to hook it up at the constructor level as they can operate at constructor level itself
Upvotes: 2