'Angular Http Observable doesn't update
Hey Guys i wrote a little backend which returns some data. Now i want to fetch this data with Angular Http and show new values when i post them in the backend. So the first thing that came to my mind were Observables but currently i can fetch the data onInit but when Posting new Data to the Backend (currently just via Postman) the Fetched data wont update. So if this is the wrong approach tell me how to do this please. Below is my code i used so far:
App Component:
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import 'rxjs/add/operator/map';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import {WeaponServiceService} from './weapon-service.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
weaponTypesarr: IweaponsTypes [] = [
{name: 'Nahkampf', value: 'melee'},
{name: 'Fernkampf', value: 'ranged'},
{name: 'Spezial', value: 'special'},
];
meleeTypesarr: IweaponsTypes [] = [
{name: 'Klingenwaffen', value: 'klinge'},
{name: 'Messer', value: 'messer'},
{name: 'Dolche', value: 'dolch'},
{name: 'Äxte/Beile', value: 'axt'},
{name: 'Speere/Stäbe', value: 'speer'},
{name: 'Stumpfe Hiebwaffen', value: 'stumpf'}
];
rangedTypesarr: IweaponsTypes [] = [
{name: 'Bogen', value: 'bogen'},
{name: 'Armbrust', value: 'armbrust'},
{name: 'Wurfwaffe', value: 'wurfwaffe'},
{name: 'kleine Schusswaffe', value: 'gun-litte'},
{name: 'große Schusswaffe', value: 'gun-big'}
];
specialTypesarr: IweaponsTypes [] = [
{name: 'Exotische Waffen', value: 'exotics'},
{name: 'Granaten und Exoplosive', value: 'grenade'}
];
rForm: FormGroup;
post: any;
weaponName = '';
weaponType= '';
impairment= '';
special= '';
results: Observable<any>;
constructor(private fb: FormBuilder , private weaponService: WeaponServiceService) {
this.rForm = fb.group({
'weaponName' : [null, Validators.required],
'weaponType': [null, Validators.required],
'impairment': [null, Validators.required],
'special': [null, Validators.required]
});
}
ngOnInit() {
this.results = this.weaponService.getWeapons();
this.results.subscribe(data => {console.log(data); });
}
generateWeapon(weaponData) {
this.weaponName = weaponData.weaponName;
this.weaponType = weaponData.weaponType;
this.impairment = weaponData.impairment;
this.special = weaponData.special;
console.log(weaponData);
}
}
export interface IweaponsTypes {
name: string;
value: string;
}
WeaponServiceService (didnt knew it calls it service by its own :D):
import { Injectable } from '@angular/core';
import {HttpClient} from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
@Injectable()
export class WeaponServiceService {
constructor( private http: HttpClient) { }
getWeapons() {
return this.http.get('http://192.168.178.48:3000/getWeapons').map(data => {
return(data);
},
err => {
console.log(err);
}
);
}
createWeapon(weaponData2: any) {
return this.http.post('http://192.168.178.48:3000/createWeapon', weaponData2)
.map(
res => {
console.log(res);
},
err => {
console.log(err);
}
);
}
}
Module:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import {NgbModule} from '@ng-bootstrap/ng-bootstrap';
import { AppComponent } from './app.component';
import { HttpClientModule } from '@angular/common/http';
import {WeaponServiceService} from './weapon-service.service';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
HttpClientModule,
ReactiveFormsModule,
NgbModule.forRoot()
],
providers: [WeaponServiceService],
bootstrap: [AppComponent]
})
export class AppModule { }
and last but not least the corresponding HTML but currently i just try to log all the values.
<div *ngIf="!name; else forminfo">
<form [formGroup]="rForm" (ngSubmit)="generateWeapon(rForm.value)">
<h1>Generate Weapon</h1>
<label for="WeaponName">WeaponName</label>
<input class="form-control" type="text" name="weaponName" formControlName="weaponName" id="WeaponName">
<div class="form-group">
<label for="WeaponGroup">Weapon Group</label>
<select class="form-control" #weaponTypeSelektor formControlName="weaponType" id="WeaponGroup">
<option> Select a Type</option>
<option *ngFor="let types of weaponTypesarr" [value]="types.value">{{types.name}}</option>
</select>
</div>
<div class="form-group" *ngIf="weaponTypeSelektor.value == 'melee'">
<label for="WeaponTypeMelee">Weapon Type</label>
<select class="form-control" formControlName="weaponType" id="WeaponTypeMelee">
<option *ngFor="let types of meleeTypesarr" [value]="types.value">{{types.name}}</option>
</select>
</div>
<div class="form-group" *ngIf="weaponTypeSelektor.value == 'ranged'">
<label for="WeaponTypeRanged">Weapon Type</label>
<select class="form-control" formControlName="weaponType" id="WeaponTypeRanged">
<option *ngFor="let types of rangedTypesarr" [value]="types.value">{{types.name}}</option>
</select>
</div>
<div class="form-group" *ngIf="weaponTypeSelektor.value == 'special'">
<label for="WeaponTypeSpecial">Weapon Type</label>
<select class="form-control" formControlName="weaponType" id="WeaponTypeSpecial">
<option *ngFor="let types of specialTypesarr" [value]="types.value">{{types.name}}</option>
</select>
</div>
<label for="impairment">Beeinträchtigung</label>
<input class="form-control" type="text" name="Beeinträchtigung" formControlName="impairment" value="" id="impairment">
<label for="special">Spezial</label>
<input class="form-control" type="text" name="Spezial" formControlName="special" value="" id="special">
<br><br>
<input type="submit" class="btn btn-primary" value="Submit Form" [disabled]="!rForm.valid">
</form>
<div *ngFor="let item of results | async"> {{item.weaponName}} </div>
</div>
<ng-template #forminfo>
<div class="form-container">
<div class="row columns">
<h1>{{ name }}</h1>
<p>{{ weaponType }}</p>
</div>
</div>
</ng-template>
So just to be clear. AppComponent starts and fetched initial data. I post Data into the Db with postman. App Component doesn't recognize new Value.
Solution 1:[1]
Change these lines in the Service.
getWeapons() {
return this.http.get('http://192.168.178.48:3000/getWeapons').map(data => {
return data.json();
}
And Change these lines in the AppComponent
ngOnInit() {
this.results = this.weaponService.getWeapons();
//delete this line ---> this.results.subscribe(data => {console.log(data); });
}
since you are using the async pipe you dont need to subscribe. Hope this helps.
Solution 2:[2]
Tested on:
- Angular CLI: 13.1.4
- Node: 17.3.0
- Package Manager: npm 8.3.0
I know I don't have the best answer but one which works (at least). Maybe it helps someone to continue further ;-)
I fetched the data once in the component's ngOnInit as well as in a function which pulls the database data after a defined interval/time has passed.
data: any;
baseUrl = 'foo_URL';
fetchInterval: 3000; // 3 Seconds
constructor(private http: HttpClient) {
}
// First Fetch
ngOnInit(): void {
this.http.get(this.baseUrl)
.pipe(takeUntil(this.destroy$))
.subscribe((fooData) => {
this.data = fooData;
})
}
// Interval Fetch
setInterval: any = setInterval(() => {
this.http.get(this.baseUrl)
.pipe(takeUntil(this.destroy$))
.subscribe((fooData) => {
// Compare for view update
if (JSON.stringify(fooData) !== JSON.stringify(this.data)) {
console.log('New stuff -> update view')
this.data = fooData;
}
});
}, fetchInterval)
// Shield Memory Leakeage after Component is deleted
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
This is generally a bad approach as ..
- .. data is pulled although there is no update the data.
- .. data may be pushed towards the view without any need (fixed).
- .. the interval is (mostly) barely adjustable to fit the need of both sides, Client & Server.
It is recommended to at least check changes in the data before updating the view with the same data. Maybe even offer an API which only serves a checksum before pulling the whole dataset.
Last: The more advanced solution would be a two-way-binding. Once an API is called which changes the data, angular gets an update for the backend. For this kind of solution you might want to take a look at socket.io ... ;-)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | PRAKASH THOMAS VARGHESE |
| Solution 2 |
