Angular directive ngIf is not working as expected - javascript

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

Related

Populate Input Field using FormControl Angular

I have an input field where I need to populate array data inside it which is coming
from API, I have used FormControl to populate the data but not able to achieve the same.I am getting the response on console but not able to populate it on UI. Below is my code if any anyone could guide me as I have spent 2 entire days and new in Angular. Can anyone please help me here.
HTML Code:
<div formArrayName="ints" *ngFor="let inCompany of insurance.controls; let i = index">
<div [formGroupName] = "i">
<ion-card *ngFor="let eq of ef;let i=index;">
<ion-item>
<ion-input formControlName="iCompany"></ion-input>
</ion-item>
</ion-card>
</div>
</div>
TS Code:
ionViewWillEnter(){
this.loadData();
}
ngOnInit() {
this.sForm = this.formBuilder.group({
ints: this.formBuilder.array([]),
})
}
get ints(): FormArray {
return this.sForm.get('ints') as FormArray;
}
get formGroup(): FormGroup {
return this.formBuilder.group({
name: ['justTest'],
});
}
loadData(){
this.service.getefDetails(data).subscribe((response: any) => {
this.ef= response.data;
var formArray = this.sForm.get('ints') as FormArray;
for (let i = 0; i < this.ef.length; i++) {
console.log(this.ef.length, this.ef[i].percentage)
var chec=this.ef[i].percentage
formArray.push(this.formGroup);
formArray.controls[i].patchValue(chec);
}
)}
}
Array Type:
[{name:"test", percentage: "29"},{name:"abc", percentage: "45"}, {name:"def", percentage: "63"}]
First of all, I suggest you re-think your approach as mentioned in the earlier comment it seems you have made it unnecessarily complicated.
Also, I would think of renaming your variables it is quite confusing and will be a pain to maintain later on.
To answer your question and get the "ion-input" printed on the screen do the following changes to the HTML.
You can not assign <div [formGroupName] = "i"> i to the formGroup since it is not of type formGroup as it is assigned to the index.
The solution is to assign <div [formGroupName] = "insuranceCompany[0]">
So that a form group will be assigned.
Again I suggest that you rename the variable "insuranceCompany" for clarity purposes as there is a control named "insuranceCompany" as well.
Here is a working example of your code minus the ionic tags.
https://stackblitz.com/edit/angular-ivy-b3gtly?file=src/app/app.component.ts
Hope I made myself clear, and hope it helps.
You can use patchValue directly to an form control in order to do this.
Your current setup seems too complicated unless there are a bunch of other values in the form that's not displayed here.
However, when you get a response from API, you can simply get the reactive form element, and set value.
this.suitablityForm.insurance.insuranceCompany.patchValue('VAL_YOU_WANT');

Angular - Update data automatically without Refresh the Page

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

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.

Angular 4 ngIf not toggled after variable being updated in ngOnInit

I am using Angular v4 and have a *ngIf in my template:
<div class="product-list row" *ngIf="products.length > 0">
<div *ngFor="let product of products" class="product-container">
...
</div>
</div>
and in my component file I have:
public products = [];
....
public ngOnInit(): void {
this.productsService.all().toPromise().then( (data: Product[]) => {
this.products = data;
});
}
However the ngIf will not be toggled after products is set. When I add a button and set the variables manually the ngIf will be toggled!
I tried changing the if statement to products?.length > 0 but it doesn't work as well.
Found my answer from this post:
Triggering change detection manually in Angular
According to Angular's documents https://angular.io/api/core/ChangeDetectorRef
detectChanges(): Checks the change detector and its children.
So by applying detectChanges Angular will manually check and update the node.

Ionic 2 not updating UI

I have problem with Ionic 2 not updating my UI when I change a variable.
html:
<ion-card *ngFor="let event of mEvents (click)="onEventCardClick(event)">
<ion-card-content ion-item>
<span class="eventTitle">{{ event.title }}</span> <br/>
<span class="relativeDate">{{ event.getRelativeTimeString() }}</span>
<ion-badge item-end>{{ event.reservationsCount }}</ion-badge>
<ion-badge *ngIf="event.hasUnreadMessages" color="danger" item-end>{{ event.unreadMessagesCount }}</ion-badge>
</ion-card-content>
</ion-card>
end from ts file:
this.fcm.onNotification().subscribe((notification:NotificationData) => {
if(!this.navCtrl.isActive(this.viewCtrl))
return;
notification.event = JSON.parse(notification.event);
notification.reservation = JSON.parse(notification.reservation);
notification.reservation_message = JSON.parse(notification.reservation_message);
let eventId: number = notification.event.id;
for(let i=0; i<this.mEvents.length; i++) {
if(this.mEvents[i].id == eventId) {
this.mEvents[i].unreadMessagesCount++;
this.mEvents[i].hasUnreadMessages = true;
return;
}
}
});
The problem is, I send a push notification from my server. i receive message successfully and update corresponding object (Event). But this last ion-badge element in ion-card does not shows up. It is still "hidden". However if I interact with UI, it suddenly shows up.
How can I achieve instant UI update? I read in some articles about NgZone but half of them says that is should not be used and the other half says that I should use it ...
Use the ChangeDetectorRef. It detects changes in variables and updates the UI. Create the private ref:ChangeDetectorRef in the constructor then call this.ref.detectChanges() whenever you need to update the UI when your variable changes.

Categories