Angular show fetched data after click dropdown button - html

I use angular at the frontend and .net core at the backend. I have a trivial problem and figure it with a video below. I spend hours and hours and still couldn't solve the problem.
I simply fetch data from my web api. And use *ngFor for display the data. But the problem is data is not shown before click any dropdown button.
Btw, dropdown button does not have any click event. It's simple language selector.
When I click language selector dropdown button, it expands and at the same time my data display on the screen.
I get my data ngOnInit. I check the data on debug mode and it's ok. I really spend hours and hours...still couldn't find any solution.
My html code :
<div class="container">
<div *ngFor="let d of devices"> --> I put here breakpoint and it run when I click dropdown btn
<span>{{d.name}}</span>
</div>
</div>
My .ts code:
import { ChangeDetectorRef, Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { ToastrService } from 'ngx-toastr';
import { Observable } from 'rxjs';
import { Pagination } from '../../../_helpers/Pagination';
import { deviceListModel, NewDeviceModel } from '../../admin-panel.model';
import { AdminPanelService } from '../../admin-panel.service';
#Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.scss']
})
export class TestComponent implements OnInit {
devices : Array<deviceListModel>; ---> I also try with public deviceList : deviceListModel[]; nothing change
constructor(private service : PanelService, private router : Router) { }
ngOnInit(): void {
this.getDeviceList();
}
getDeviceList() {
this.service.getDeviceList().subscribe(res => {
this.devices = res;
})
} ---> This part works fine. Data comes true from the backend before click the dropdown button.(I checked in debug mode with breakpoints)
}
Visiulize my problem with below link;
https://media.giphy.com/media/jYGHN1Ndxyeqhtn0ZR/giphy.gif
fullscreen with low res : https://giphy.com/gifs/jYGHN1Ndxyeqhtn0ZR/fullscreen
Edit :
When I try just display one of the data like devices[0].name;
<div>
<span> {{devices[0}.name}} </span>
</div>
I get data on page but with an error in console.
The error comes three times and the error is ;
core.js:4352 ERROR TypeError: Cannot read property '0' of undefined

Try putting the this.devices = res; into the NgZone
constructor(..., private _ngZone: NgZone)
this._ngZone.run(() => { this.devices = res; });

Can you try to put an *ngIf in the container? Like so:
<div class="container" *ngIf="devices?.length">
<div *ngFor="let d of devices">
<span>{{d.name}}</span>
</div>
</div>
In this case, the template will only be rendered when the backend call has completed successfully (and has results).

Related

'trim' of undefined TypeError in Angular app when I don't call trim anywhere

I'm currently trying to learn Angular and as I'm working through a couple of ideas I had, I ran into the following error in the dev console of Chrome:
ERROR TypeError: Cannot read property 'trim' of undefined
at Function.addMultipleClasses (primeng-dom.js:19)
at ButtonDirective.createIconEl (primeng-button.js:59)
at ButtonDirective.setIconClass (primeng-button.js:78)
at ButtonDirective.set label [as label] (primeng-button.js:92)
at setInputsForProperty (core.js:10940)
at elementPropertyInternal (core.js:9984)
at ɵɵpropertyInterpolate1 (core.js:15551)
at Module.ɵɵpropertyInterpolate (core.js:15514)
at CmsComponent_Template (cms.component.html:12)
at executeTemplate (core.js:9579)
Here is my HTML:
<h1>Angular Router App</h1>
<!-- This nav gives you links to click, which tells the router which route to use (defined in the routes constant in AppRoutingModule) -->
<nav>
<ul>
<li><a routerLink="/login" routerLinkActive="active">/login</a></li>
<li><a routerLink="/" routerLinkActive="active">/</a></li>
</ul>
</nav>
<button type="button"
pButton
label="{{word}}"
(click)="buttonPress()">
</button>
<!-- The routed views render in the <router-outlet>-->
<router-outlet></router-outlet>
And here is my TS:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-landing',
templateUrl: './cms.component.html',
styleUrls: ['./cms.component.scss']
})
export class CmsComponent implements OnInit {
private onWord: number = 0;
private words: Array<string> = ["One","Two","Three","Two"];
public word: string = this.words[this.onWord];
constructor(
) { }
ngOnInit(): void {
}
public buttonPress(): void {
// Bumps the index
this.onWord++;
// Keeps the value in the proper range
if (this.onWord >= this.words.length) {
this.onWord = 0;
}
// Updates the word to the new index
this.word = this.words[this.onWord];
console.log("The button is now on " + this.word);
}
}
I'm using Angular and Typescript. Any thoughts on what's going wrong? The button functions as intended except for that error whenever I click it.
Thanks!
was the same problem, I found a solution on the official forum, in order to fix the error you need to add -> icon = "pi"
<button
icon="pi"
type="button"
pButton
[label]="(documentsCount$ | async)?.toString()"
></button>
This is not an error generated from your code but in the package you're using (in this case PrimeNg). The method seems to be expecting some input which it's not getting. Try passing '$event' in the click method and handle it in the ts file.
(click)="buttonPress($event)"
As an ideal implementation, use the predefined button element provided by PrimeNg.
Turns out having {{word}} as the button label broke things!
Apparently I can't have dynamically changing button labels...
Use p-button instead:
<p-button
label="{{word}}"
(onClick)="buttonPress()">
</p-button>
See the forum entry

Angular 10. How to navigate to a component's sections via a different component's anchor link tag?

app.component.html
<!--I wish to keep this structure like this, because menu.component has position sticky at top: 0-->
<app-home></app-home>
<app-menu></app-menu>
<app-about></app-about>
<app-projects></app-projects>
<app-contact></app-contact>
<app-footer></app-footer>
menu.component.html
<!--On anchor link click, navigate to corresponding component-->
<nav>
<a>Home</a>
<a>About</a>
<a>Projects</a>
<a>Contact</a>
</nav>
home.component.html, about.component.html, projects.component.html, contact.component.html
<!--Basic structure-->
<section>
<p>component works!</p>
</section>
A Link to the basic structure of the web-site
https://stackblitz.com/edit/angular-ivy-ufce4g
I would have shared a link to the actual web-site(my personal web-site), but i don't know if i am allowed.
One solution would be to create a shared service between you menu component and your app component.
It would work like this:
Dispatch a notification when a navigation item is clicked in you menu component;
Listen for notification in your app component. When a new notification is received, scroll to the corresponding component on your page.
Something like this:
#Injectable({
providedIn: 'root'
})
export class NavigationService {
private _navigation$ = new BehaviorSubject<string>('home');
navigation$ = this._navigation$.asObservable();
constructor() { }
updateNavigation(item) {
this._navigation$.next(item);
}
}
In your menu component, call the updateNavigation method of NavigationService whenever a navigation item is clicked.
#Component({
selector: 'app-menu',
// ...
})
export class MenuComponent {
constructor(private navigationService: NavigationService) {}
// Call this method whenever one of your navigation items is clicked
// in your template.
//
// navigationItem could, for example, be a string/enum corresponding to
// the component that must be scrolled into view
handleNavigationItemClick(navigationItem: string) {
this.navigationService.updateNavigation(navigationItem);
}
}
Finally, in your app component, listen for a navigation item update and scroll to the component.
#Component({
// ...
})
export class AppComponent implements OnInit {
constructor(private navigationService: NavigationService) {}
ngOnInit() {
this.navigationService.navigation$.pipe(
distinctUntilChanged(),
).subscribe(navigationItem => this.scrollIntoView(navigationItem))
}
private scrollIntoView(component) {
// Scrolling logic ...
}
}

Adding to component constructor in Angular makes the entire page return blank?

I am trying to add a basic MatDialog to my project. In the project I have 2 components, a header for the page and another called "CardBox", which basically just holds cardboxes of links to different websites.
When you click on the "i" icon, I would like to open a dialog box with more information.
See image below.
Initially, my understanding was that I just add a MatDialog field in the constructor of Cardbox component. Like so:
cardboxes.component.html
<mat-card id="CARDBOX">
<img class="info" src="path/image.jpg" alt="image" height=25px (click)="openDialog()"/>
</mat-card>
cardboxes.component.ts
#Component({
selector: 'app-cardbox',
templateUrl: './cardbox.component.html',
styleUrls: ['./cardbox.component.scss']
})
export class CardboxComponent implements OnInit {
constructor(private dialog: MatDialog) { }
ngOnInit(): void {}
openDialog() {
this.dialog.open(CardBoxComponent);
}
}
(I'm aware that this is calling its own component, and would just open the same thing again. I am just trying to get it to work first.)
app.component.html
<div id="bg">
<app-header></app-header>
<br>
<app-cardbox></app-cardbox>
</div>
However, in doing so, it removes EVERYTHING from the page except the background, including the header component. This is what it looks like when the program is run when there is SOMETHING in the constructor of Cardbox.
As you can see, having something in the constructor gets rid of everything on the page, which does not make sense to me as it removes the header, which is a completely separate component from the cardbox. I have tried everything to make it work but still it is not working.
Why is touching the constructor makes the entire project blank? Is there something I forgot to add to another file? And how can I add a MatDialog popup feature to the project in a way that works?
TLDR: When I put anything in the constructor of one of my components, the entire page disappears. How do I resolve this?
Still seeking answer to this :(
You are using it wrong.
I am surprised your app compiles when doing this.dialog.open(CardBoxComponent)
What you need to do is, first create your dialog component.
To make things simple you can create it in the same file as you CardBox component, but make sure you put it outside CardBox class:
cardboxes.component.ts
#Component({
selector: 'dialog-overview-example-dialog',
templateUrl: 'dialog-overview-example-dialog.html',
})
export class DialogOverviewExampleDialog {
constructor(
public dialogRef: MatDialogRef<DialogOverviewExampleDialog>,
// data is gonna be the data you pass to dialog when you open it from CardBox
#Inject(MAT_DIALOG_DATA) public data: DialogData) {}
onNoClick(): void {
this.dialogRef.close();
}
}
then you create a template for the dialog component:
dialog-overview-example-dialog.html
<h1 mat-dialog-title>more info</h1>
<div mat-dialog-content>
<p>{{data.info}}</p>
</div>
finally you add openDialog(myInfo) function to your ts file, inside CardBox component:
cardboxes.component.ts
openDialog(myInfo): void {
const dialogRef = this.dialog.open(DialogOverviewExampleDialog, {
width: '250px',
// data you pass to your dialog
data: {info: myInfo}
});
dialogRef.afterClosed().subscribe(result => {
console.log('The dialog was closed');
this.animal = result;
});
}
and add it to your template too:
cardboxes.component.ts
<mat-card id="CARDBOX">
<img class="info" src="path/image.jpg" alt="image" height=25px (click)="openDialog('info about first site')"/>
</mat-card>
in this example I pass the info as a text, but it can be an object too.
Here is a demo to make things easier for you: link

How to Show Placeholder if *ngFor Let Object of Objects from HTML Binding returns an empty array

I display data from a template that I create in another component (team-announcements.component)... in the main team.component.ts I use the selector for team-announcements, and add the [announcement]="announcements" and ngFor="let announcement of announcements" to load the data. If the array returns no data IE there are no announcements, how can I display a placeholder like "No Announcements"?
This is where I load the announcements in team.component.html. The data is served through an API service and is retrieved in "team.component.ts", the HTML for the objects in question is below.
team.component.ts (get announcement functions):
getAnnouncements() {
this.teamsService.getTeamAnnouncements(this.team.slug)
.subscribe(announcements => this.announcements = announcements);
console.log("announcements", this.announcements);
}
team.component.html
<div class="team-announcement">
<div class="announcement-title">Message of the Day</div>
<app-team-announcements
[announcement]="announcement"
*ngFor="let announcement of announcements">
</app-team-announcements>
</div>
This is how "app-team-announcements" above is templated in a separate file, "team-announcement.component.html" and is exported, and then used in the above code...
team-announcements.component.ts
import { Component, EventEmitter, Input, Output, OnInit, OnDestroy } from '#angular/core';
import { Team, Announcement, User, UserService } from '../core';
import { Subscription } from 'rxjs';
#Component({
selector: 'app-team-announcements',
templateUrl: './team-announcement.component.html'
})
export class TeamAnnouncementComponent implements OnInit, OnDestroy {
constructor(
private userService: UserService
) {}
private subscription: Subscription;
#Input() announcement: Announcement;
#Output() deleteAnnouncement = new EventEmitter<boolean>();
canModify: boolean;
ngOnInit() {
// Load the current user's data
this.subscription = this.userService.currentUser.subscribe(
(userData: User) => {
this.canModify = (userData.username === this.announcement.author.username);
}
);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
team-announcements.component.html
<div class="announcement-text">
{{announcement.body}}
</div>
I am unsure of how or where to "If" check the array length to display a placeholder. Can anyone help?
If you want to hide it and display something else instead you can use the else property from *ngIf:
<div class="team-announcement">
<div class="announcement-title">Message of the Day</div>
<ng-container *ngIf="announcements.length != 0; else emptyArray">
<app-team-announcements
[announcement]="announcement"
*ngFor="let announcement of announcements">
</app-team-announcements>
</ng-container>
</div>
<ng-template #emptyArray>No announcements...</ng-template>
When you want an element with *ngFor to depend on a condition (*ngIf), a good alternative is to nest the element with an *ngFor in a <ng-container> with an *ngIf. A good thing about <ng-container> is that it wont actually be part of the DOM but will obey the *ngIf.
You could insert a div wich is only displayed when your array is empty:
<div class="team-announcement">
<div class="announcement-title">Message of the Day</div>
<app-team-announcements
[announcement]="announcement"
*ngFor="let announcement of announcements">
</app-team-announcements>
<div *ngIf="announcements.length===0"> No announcements </div>
</div>
Edit: Corrected the errors

how can i flash image by hovering on button with the help of mouseover option in angularjs2?

What I want to do is when I hover on the 'click me' button then it should show an image on the web page and when i unhover it should not show any image with the help of mouseover option
here is what i tried to do in app.component.ts and my.component.ts files
here is the code for app.component.ts :
import { Component } from '#angular/core'; //importing components from angular
import { MyComponent } from './my.component'; //importing components from my.component
#Component({
selector: 'my-app',
template: `<h1> Hi Buddy!! </h1>
<mytag></mytag>`,
directives: [MyComponent] //adding directives from mycomponents
})
export class AppComponent { }
and here is the code for my.component.ts:
import { Component } from "#angular/core";
#Component({
selector:'mytag',
template: `<button (mouseover)="<img [src]="image"> " >click me</button>` // here i tried to flash image by hovering
})
export class MyComponent{
public image="http://lorempixel.com/400/200";
myclick(klm){
console.log(klm);
}
}
so what changes should i make in the class or meta data of my.component.ts in order to do so
You can use Angular Animations module to achieve the same.
Make the below changes to your MyComponent:
import { Component } from '#angular/core'
import { trigger, state, style, transition, animate, keyframes, group } from '#angular/animations';
import { BrowserAnimationsModule } from '#angular/platform-browser/animations';
#Component({
selector:'mytag',
template: `<button (mouseover)="toggleOnOff()">click me</button>
<img [src]="image" [#switchImageDisplay]="showImage"/>
`
,
animations: [
trigger("switchImageDisplay",[
state("show", style({
display : 'block'
})),
state("hide", style({
display : 'none'
})),
transition('show <-> hide',[animate('0s')]),
])
]
})
export class SwitchDisplayComponent {
public image="http://lorempixel.com/400/200";
public showImage : string;
toggleOnOff(){
console.log("Previous display value is",this.showImage);
this.showImage = (this.showImage === "show") ? "hide" : "show";
console.log("Current display value is",this.showImage);
}
}
Explanation:
toggleOnOff() function sets a string variable showImage as show and hide.
In Animations we create a trigger and give it a name. In our case we have named it as "switchImageDisplay". We declared two states in the animation trigger that is "show" and "hide". In those states we defined what CSS to be used. Finally we defined a transition, which is 2 ways binded and is performed in 0 seconds. If you want the image to be hidden over a period of time increase the time.
In template code, we have binded the img tag to the animation using the code [#switchImageDisplay]="showImage". Based on the "showImage" value, the animation "switchImageDisplay"'s state is defined.
Import the import { BrowserAnimationsModule } from '#angular/platform-browser/animations'; in your app.module.ts and in the imports array as well.