Angular - Update data automatically without Refresh the Page - javascript

In my application there is a table that get rows from database.
This is the AJAX CALL (SERVICE)
getPosts(): Observable<Posts[]> {
return this.http.post<Posts[]>(this.myAppUrl + this.myApiPostsUrl, this.authService.getLoggedUserFromSessionStorage())
.pipe(
retry(1),
catchError(this.errorHandler)
);
}
All work perfectly, but my datas dont update automatically, and the user need to refresh the page to see the new rows, how can i do this?
I would like do that the new rows are added in the table dynamically ... without update the page.
This is the table
COMPONENT HTML
<table *ngIf="(posts$ | async)?.length>0" class="table align-items-center table-flush">
.....
<tr *ngFor="let post of (posts$ | async) | filter:authService.filter | paginate: config | orderBy: key : reverse">
<!-- <td>{{posts.id}}</td>-->
<td>{{post.title}}</td>
<td>{{post.body}}</td>
.....
</table>
COMPONENT TS
ngOnInit() {
this.loadPosts();
}
loadPosts() {
this.message = 'Loading....';
this.posts$ = this.postService.getPosts();
if (this.posts$ == null) {
this.message = 'No Posts found';
}
}
Thanks so much.

There are several options. Here is a reactive way of handling this. Any time getPosts is successful, you'll need to refetch the initial data.
To fetch your initial data you will need to wrap your posts$ observable in an action stream:
// create a stream for your post request
private readonly postAction$ = new Subject();
posts$ = this.postAction$.pipe(
startWith(''),
concatMap(()=> {
return this.postService.getPosts(); // this will be your http get request
}),
)
The startWith operator will cause your get request to fire initially without an observable been passed to your postAction observable.
Your getPosts method now will call this.postAction$.next() on success, that will trigger the refetch of your posts$ observable.
getPosts(): Observable<Posts[]> {
return this.http.post<Posts[]>(this.myAppUrl + this.myApiPostsUrl, this.authService.getLoggedUserFromSessionStorage())
.pipe(
retry(1),
catchError(this.errorHandler),
tap(() => this.postAction$.next())
);
}
You can see a demo of this. Check the console, you'll see that the get request is fired every time the button is clicked.
With Interval
posts$ = interval(30000)
.pipe(
startWith(''),
switchMap(() => return this.postService.getPosts();)
)
Interval demo

in Angular, when you want to update your html, you need to use ngFor to update your data automatically.
<table *ngIf="(posts$ | async)?.length>0" class="table align-items-center table-flush">
<div *ngFor="let data of datas; let i = index>
{{ data }}
</div>
</table>
ngFor will loop on datas and update it when it changes

Related

Saving Values to Backend from TextBoxes using React Flux Pattern

I have several text boxes and a save button
Each text box value is loaded using the following approach
{
this.getElement('test3lowerrangethreshold', 'iaSampling.iaGlobalConfiguration.test3lowerrangethreshold',
enums.IASamplingGlobalParameters.ModerationTest3LowerThreshold)
}
private getElement(elementid: string, label: string, globalparameter: enums.IASamplingGlobalParameters): JSX.Element {
let globalParameterElement =
<div className='row setting-field-row' id={elementid}><
span className='label'>{localeHelper.translate(label)}</span>
<div className="input-wrapper small">
<input className='input-field' placeholder='text' value={this.globalparameterhelper.getDataCellContent(globalparameter, this.state.globalParameterData)} />
</div>
</div>;
return globalParameterElement;
}
Helper Class
class IAGlobalParametesrHelper {
public getDataCellContent = (globalparameter: enums.IASamplingGlobalParameters, configdata: Immutable.List<ConfigurationConstant>) => {
return configdata?.find(x => x.key === globalparameter)?.value;
}
}
This works fine. Now the user is allowed to update these text values.And on click of save the changes should be reflected by calling a web api .
I have added an onlick event like this
<a href='#' className='button primary default-size' onClick={this.saveGlobalParameterData}>Save</a>
Now inorder to save the data i need a way to identify the text element which has changed.For that i have added an update method within the Helper class
public updateCellValue = (globalparameter: enums.IASamplingGlobalParameters, configdata: Immutable.List<ConfigurationConstant>,updatedvalue:string) => {
let itemIndex = configdata.findIndex(x => x.key === globalparameter);
configdata[itemIndex] = updatedvalue;
return configdata;
}
and return the updated configdata ,and i plan to call this method in the onchange event of every text box like this
<input className='input-field' placeholder='text' onchange={this.setState({ globalParameterData: this.globalparameterhelper.updateCellValue(globalparameter, this.state.globalParameterData, (document.getElementById(elementid) as HTMLInputElement).value})}
But this does not seem like a correct approach as there are number of syntactical errors. I initially got the data using an actioncreator like this.Please advice.
samplingModerationActionCreator.getGlobalParameters();
samplingModerationStore.instance.addListener(samplingModerationStore.SamplingModerationStore
.IA_GLOBAL_PARAMETER_DATA_GET_EVENT,
this.getGlobalParameterData);
}

Angular directive ngIf is not working as expected

We are trying to pass data from one component to another and below is the approach we are taking. When there is no data we want to show the error message
<div *ngIf="showGlobalError">
<h6>The reporting project doesn't have any Shippable Items</h6>
</div>
and the component.ts is like
showGlobalError = true;
constructor(private psService: ProjectShipmentService, private pdComp: ProjectDetailsComponent) {
this.psService.tDate.subscribe(x => this.cachedResults = x);
}
ngOnInit() { }
ngDoCheck() {
if (this.cachedResults.length > 0 && this.count <= 1) {
this.showGlobalError = false;
this.populateArrays();
this.count++;
}
}
populateArrays() {
this.reportingProject = [this.pdComp.rProjectNumber];
this.projectSalesOrder = this.pdComp.rSalesOrder;
this.clearFilter();
........
The issue is Even though there is data in the this.cachedResults that is this.cachedResults.length not equal to '0' for few seconds 'The reporting project doesn't have any Shippable Items' is shown in the page and then shows the data I am not sure if this something with the ngDoCheck() is causing this. Any help is greatly appreciated
Since, the default value of showGlobalError is true, the page load shows the error message.
Please make it by default false and make it true when this.cachedResults.length is 0 or this.cachedResults is undefined or this.cachedResults is null.
Hope this solves your problem.
Rather than subscribing in the code you can use the async pipe in your template
items$ = this.psService.tDate;
showGlobalError$ = this.items$.pipe(map(results => !results || !results.length));
constructor(private psService: ProjectShipmentService, private pdComp: ProjectDetailsComponent) { }
and in your template
<div *ngIf="showGlobalError$ | async">
<h6>The reporting project doesn't have any Shippable Items</h6>
</div>
<ng-template *ngFor="let item of items$ | async">
Do stuff with {{item | json}}
</ng-template>
This manages your subscription for you and fixes the memory leak you have in your code with the subscription you don't unsubscribe from.
Take a look at alibrary I wrote for this sort of thing, make caching data a lot easier. https://medium.com/#adrianbrand/angular-state-management-with-rxcache-468a865fc3fb

Parsing data from service to component in angular

In my service I make a call:
this.potentialOrganizations(currentNode.id)
.subscribe(data => {
console.log('consoling the org data!!!!!!! ' + JSON.stringify(data))
this.potentialOrgData = [];
this.potentialOrgData = data;
this._potentialOrgs.onNext(true);
})
The data consoles fine, as you can see, I tried using an observable method but it's not working for some reason!
I may need a new way to be able to call the data, as I said in my components html I have this: although it doesn't work:
<ul *ngIf="this.engagementService.potentialOrgData.length > 0">
<li *ngFor="let org of this.engagementService.potentialOrgData">
<p class="listedOrgs">{{ org.name }}</p>
</li>
</ul>
In my component I had this:
ngOnInit(): void {
this.engagementService.potentialOrgs$.subscribe(
(data) => {
if (data) {
console.log('are we hitting here inside the potentialORG DATA!??!?!?!!?!?')
this.potentialOrganizations = this.engagementService.potentialOrgData;
}
}
)
this.potentialOrganizations = this.engagementService.potentialOrgData;
}
it doesnt console, even though in my service i have the observable thing set:
private _potentialOrgs = new BehaviorSubject<boolean>(false);
public potentialOrgs$ = this._potentialOrgs.asObservable();
I was thinking maybe I need to use #input instead? but how to do that properly?
You could make this a little more simple here by trying the following. If potentialOrgData is set from a subscription in the service which it is, it will stay fresh as subscriptions stay open. You will be able to use the variable in the service directly.
public requestedData = [];
public ngOnInit(): void {
this.requestedData = this.engagementService.potentialOrgData;
}
In your template.
<ul *ngIf="requestedData.length">
<li *ngFor="let org of requestedData">
<p class="listedOrgs">{{ org.name }}</p>
</li>
</ul>
Behaviour Subjects and Observable's are very powerful but not always necessary.

ForkJoin() issue on Angular

Hello I am currently working on an UI and I need to print for each 'digId' a value that I retrieved in an unique JSON response.
In the case below, I have 3 incidents and I did a fork to have access to the 3 JSON response.
digId='4149';
digId2='4719';
digId3='4309';
ngOnInit(): void {
const parallel$ = Observable.forkJoin(
this.http.get('http://ninjaopsapi?tablename=REF_OPS_ALARM&babylonref=' + this.digId),
this.http.get('http://ninjaopsapi?tablename=REF_OPS_ALARM&babylonref=' + this.digId2),
this.http.get('http://ninjaopsapi?tablename=REF_OPS_ALARM&babylonref=' + this.digId3)
);
parallel$.subscribe( data => {
this.alarms = data, console.log(data);
})
}
My goal is to print the element circled in blue for example: Capture
But with this code below in my html, I retrieve the element [0] for the three incidents and I can't put an additionnal [0] to select only one.
<div *ngFor= "let alarm of alarms">
<div [(ngModel)]="digId" ngDefaultControl>
<div>{{alarm[0].alertMessage}}</div>
</div>
</div>
Is it possible to print the first element of the first element in an array when the .subscribe is done with a "forkJoin()" ?
Thank you
UPDATE
I only changed {{alarm[0][0].alertMessage}} by {{alarms[0][0].alertMessage}} and delete the loop *ngFor="let alarm of alarms
and it works well ! Thank you
You could simply do
parallel$.subscribe( data => {
this.alarms = data.map(x => x[0]);
});
<div>{{alarm.alertMessage}}</div>

Load data before rendering subcomponent

I have a parentComponent called dashboardComponent. This contains a table.
When selecting a row in that table I display it's subcomponent, which is companyAdmin, and I pass the rowID to it.
CompanyAdmin on it's turn contains 4 subComponents. The task of the companyAdminComponent is to do a GET request with the rowId it received from it's parentComponent, the dashboardComponent. Then send distribute that data to it's 4 subComponents.
The problem I am having is, this is all on 1 page and so can't use a resolver. So when I am rendering the subComponent of companyAdminComponent it crashes as it didn't receive the data back yet from the GET request. and getting the error
Cannot read property 'name' of undefined
So basically how do I show the subComponent of companyAdmin, only after the data has been loaded.
The code
dashboard.html (Parent component)
Only showing the relevant code for brevity
// ap-company-admin only is being shown when a row is selected in the table
<div class="row extraRowSpace" *ngIf="companySelected">
<div class="col-xl-12">
<ap-company-admin [companyId]="company.id"></ap-company-admin>
</div>
</div>
CompanyAdmin HTML (subComponent of dashboardComponent)
<ap-company-details [companyDetails]="companyDetails"></ap-company-details>
CompanyAdminComponent TS (subComponent of dashboardComponent)
ngOnInit() {
this.companyService.getAllCompanyDetails(this.companyId).subscribe(
response => {
this.allCompanyDetails = response;
this.companyDetails = {
cardsName: this.allCompanyDetails.cardsName,
name: this.allCompanyDetails.name
};
}
);
}
CompanyDetailsComponent (subComponent of companyAdminComponent)
#Input() companyDetails;
companyName: FormControl;
cardsName: FormControl;
ngOnInit() {
this.companyName = new FormControl(this.companyDetails.name);
this.cardsName = new FormControl(this.companyDetails.cardsName);
// Basically it is crashing here because on init of this component the data of companyDetails hasn't come back yet from the GET call
}
All the things I found are things to do with resolver, but as I am not navigating but only show and hiding the companyAdminComponent when selecting a row, I don't think I can do this with a resolver. Any advice?

Categories