Angular 6 *ngFor not updating view - angular6

I am using material card to get populated using *ngFor but it does not show binding
it shows this in the DOM ,but when I console my binded variable, it shows the value but still my mat-card not get re populated.
NOTE :-- when I click on any form field box its then changed to <!--bindings={
"ng-reflect-ng-for-of": "[object Object],[object Object"
}--> this and data starts showing
Below is my code
<mat-card class="inner-list-card" *ngFor="let uglist of userGroupViewModel.UserGroupList"
(click)="getDetails(uglist['groupId'],$event)" routerLink="scope">
<div class="inner-card">
<li class="pointer">
<span class="listIcon">
<i class="fa fa-users"></i>
</span>
<a class="textwrap">
{{ uglist.groupName }}
<small class="listDate">Last Updated: {{ uglist.updatedOn*1000 | date }}</small>
</a>
<span>
<a class="protected">
<img alt="" src="assets/protected.png" height="25px" width="23px">
</a>
</span>
</li>
</div>
<div class="desc"> {{ uglist.description }}</div>
<div class="routing-element">Show
More
<i class="fa fa-angle-right alignRight" aria-hidden="true"></i>
</div>
</mat-card>
and I went through the below reference links:
angular2 ngFor not working while fetching data from api on ngOnInit()

Welcome to asynchronous programming :)
You have not included the details of how you're getting the data from your api... what i have done is used observable to which i subscribed. As soon as the data is available, the *ngFor renders the result for us.
relevant service.ts:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root',
})
export class MyService {
apiURL: string = 'https://ddxhruyrrj.execute-api.us-east-2.amazonaws.com/aiProd/projectslazy';
constructor(private http: HttpClient) { }
getData() {
return this.http.get(this.apiURL);
}
}
relevant component.ts:
export class CardFancyExample {
dataSet:any;
constructor(private serv: MyService) {
this.serv.getData().subscribe(
dataa => { console.log(dataa); this.dataSet = dataa; }
, errr => { console.log(errr); }
)
}
}
relevant html:
<div *ngIf="dataSet== ''">
No data loaded
</div>
<div *ngIf="dataSet != ''">
<mat-card class="example-card" *ngFor="let data of dataSet">
<mat-card-header>
<mat-card-title>{{data?.project}}</mat-card-title>
<mat-card-subtitle>{{data?.role}}</mat-card-subtitle>
</mat-card-header>
<img mat-card-image src="https://www.akberiqbal.com/JumboMob.jpg" alt="Akber Iqbal">
<mat-card-content>
<p>
{{data?.description}}
</p>
</mat-card-content>
<mat-card-actions>
<button mat-button>LIKE</button>
<button mat-button>SHARE</button>
</mat-card-actions>
</mat-card>
</div>
complete working stackblitz here

Related

How to pass value from one component to another? (Angular)

I just recently started learning Angular and I have a question. I want to implement a search method to search for a product on my site, I made search.pipe.ts, which works, but the input for input is in the header.component.ts component, and the products array is in the car-list.component.ts component.
car-list.component.html
<div *ngFor="let car of cars | paginate: { itemsPerPage: pageNumber, currentPage: currentPg} | **search:searchStr**" class="col-md-3">
<div class="product box">
<img src="{{'data:image/jpg;base64,' + car.image }}" alt="">
<h3>{{ car.name }}</h3>
<div class="price">{{ car.price | currency:'USD' }}</div>
<button class="btn btn-primary btn-sm">Add to cart</button> <!--(click)="addToCart(tempProduct)"-->
</div>
<br>
</div>
header.component.html
<form class="d-flex me-5">
<input type="text" class="form-control me-2" placeholder="Search cars...">
</form>
header.component.ts
export class HeaderComponent implements OnInit {
searchStr: string = '';
constructor() {
}
ngOnInit(): void {
}
}
search.pipe.ts
#Pipe({
name: 'search'
})
export class SearchPipe implements PipeTransform {
transform(cars: any[], value: any) {
return cars.filter(car => {
return car.name.includes(value);
})
}
}
I want the input values ​​from the header component to be passed to the car-list component so that I can find the product I need.
In this case you can use a shared service where you can pass data from your header component and load that data in your products component.
For further reference - Angular 4 pass data between 2 not related components
use #Input and #Output decorators to communicate between components

ERROR TypeError: Cannot read property 'description' of undefined in Angular template

This is my service
getCADataService() : Observable<Currentaffairs[]>{
return this.httpClient.get<Currentaffairs[]>(`${this.url}/sports`);
}
This is my component:
getCAdata() {
this.dataservice.getCADataService()
.subscribe(currentAffairs =>
this.currentAffairs = currentAffairs
)
This is my Template:
<ul>
<li *ngFor="let ca of currentAffairs | keyvalue">
<md-card class="example-card">
<md-card-header>
{{ca.articles.description}}
</md-card-header>
<img md-card-image src="">
<md-card-content>
<p>
</p>
</md-card-content>
<md-card-actions>
<button md-button>Approve</button>
<button md-button>Reject</button>
</md-card-actions>
</md-card>
</li>
</ul>
While displaying data in UI i am getting below error:
For this, you can use ? value in the html template to define it as a safe navigation operator as when the component loaded the observable is still ongoing hence it returns as undefined
<ul>
<li *ngFor="let ca of currentAffairs | keyvalue">
<md-card class="example-card">
<md-card-header>
{{ca.articles?.description}}
</md-card-header>
<img md-card-image src="">
<md-card-content>
<p>
</p>
</md-card-content>
<md-card-actions>
<button md-button>Approve</button>
<button md-button>Reject</button>
</md-card-actions>
</md-card>
</li>
</ul>
Based on the http response you provided it seems that you should iterate over the articles array or map your GET response to articles at first.
Right now you are trying to access here {{ca.articles.description}} a property description of an Array which doesn't make much sense.
One viable approach would be to adjust this method
getCAdata() {
this.dataservice.getCADataService()
.pipe(map((response) => response.articles));
.subscribe(currentAffairs =>
this.currentAffairs = currentAffairs
)
}
so that this.currentAffairs is an array aricles itself and then you would remove keyvalue pipe and change the template in this way
<md-card-header>
{{ca.description}}
</md-card-header>

ngx bootstrap dropdown not showing items with ngFor Angular 9

As the title says I can make the dropdown items to be display when using ngFor
visual results
Inspected element
Html
<li class="nav-item" *ngFor="let item of menu">
<button
class="btn btn-warning btn-flat"
[routerLink]="['/', item.name]"
*ngIf="item.submenu.length === 0"
>
{{ item.name | link | titlecase }}
</button>
<div
class="btn-group"
dropdown
[autoClose]="true"
*ngIf="item.submenu.length > 0"
>
<button
id="button-animated-menu"
dropdownToggle
type="button"
class="btn btn-warning btn-flat dropdown-toggle"
aria-controls="dropdown-animated-menu dropdown"
>
{{ item.name | titlecase }}
<span class="caret"></span>
</button>
<div
id="dropdown-animated-menu"
*dropdownMenu
class="dropdown-menu"
aria-labelledby="button-animated-menu"
>
<li *ngFor="let k of item.submenu">
<a class="dropdown-item" [routerLink]="['/', k.name]">
{{ k.name | link | titlecase }}
</a>
</li>
</div>
</div>
</li>
Component.ts
export class NavbarComponent implements OnInit {
constructor(
private authService: AuthService,
private router: Router,
private alert: AlertService
) {}
menu: Menu[] = this.authService.menus;
ngOnInit(): void {
console.log(this.menu);
}
}
UPDATED
AuthService
export class AuthService {
baseUrl = environment.apiUrlLogIn;
private USER: UserDetails;
jwthelper = new JwtHelperService();
constructor(private http: HttpClient) {}
get roles(): string[] {
return this.USER.authorities;
}
get menus(): Menu[] {
return this.USER.menu;
}
login(data: any) {
return this.http
.post(this.baseUrl, data)
.pipe(
map((response: Token) => {
if (response) {
localStorage.setItem('token', response.access_token);
localStorage.setItem(
'_token',response.refresh_token);
this.USER = this.jwthelper.decodeToken(response.access_token);
}
})
);
}
I've tried different approache, but what I can see, it's a bug of some sort, due to the elements are rendered in the html, but the dropdown is not taking their heights.
Any ideas?
Thanks before hand...
As suggested by MikeOne I started making a stackblitz project, and after finishing it everything worked just fine, but then I realised that the project in stackblitz had the ngx-bootstrap#5.6.0 version while mine was ngx-bootstrap#5.5.0 I thought it was a small issue but I tried anyways and surprisedly it worked.
So the changes were
Updating the packages
from
to
Changing the imports
The import way from the left side of the previous picture for some reason didn't work anymore, after checking the modules folder, it seems the ngx-bootstrap.ts main file was delete in the lastest version, that's why I had to change the import to the way of the right side of the picture

Angular 8 ERROR TypeError: Cannot read property 'name' of undefined

I am playing around with angular and I get this error: ERROR TypeError: Cannot read property 'name' of undefined
My code is
recipe-list.component.ts
import { Component, OnInit } from '#angular/core';
import { Recipe } from '../recipe.model'
#Component({
selector: 'app-recipe-list',
templateUrl: './recipe-list.component.html',
styleUrls: ['./recipe-list.component.css']
})
export class RecipeListComponent implements OnInit {
recipes: Recipe[] = [
new Recipe('Test', 'Test Code', 'https://cdn.pixabay.com/photo/2016/06/15/19/09/food-1459693_960_720.jpg'),
new Recipe('Test 2', 'Test Code', 'https://cdn.pixabay.com/photo/2016/06/15/19/09/food-1459693_960_720.jpg')
];
constructor() { }
ngOnInit() {
}
}
recipe-list.component.html
<div class="row">
<div class="div col-xs-12">
<button class="btn btn-success">New Recipe</button>
</div>
</div>
<hr>
<div class="row">
<div class="col-xs-12">
<app-recipe-item
*ngFor="let recipeEl of recipes"
[recipe]="recipeEl"></app-recipe-item>
</div>
</div>
<app-recipe-item></app-recipe-item>
recipe-item.compoent.html
<a href="#" class="list-group-item clearfix">
<div class="pull-left">
<h4 class="list-group-item-heading">{{ recipe.name }}</h4>
<p class="list-group-item-text">{{ recipe.description }}</p>
</div>
<span class="pull-right">
<img [src]="recipe.imagePath" alt="{{ recipe.name }}" class="img-responsive" style="max-height:50px">
</span>
</a>
recipe-item.component.ts
import {Component, Input, OnInit} from '#angular/core';
import {Recipe} from '../../recipe.model';
#Component({
selector: 'app-recipe-item',
templateUrl: './recipe-item.component.html',
styleUrls: ['./recipe-item.component.css']
})
export class RecipeItemComponent implements OnInit {
#Input() recipe: Recipe;
constructor() {
console.log('Recipe is' + this.recipe);
}
ngOnInit() {
}
}
I can't seem to find the problem with my code. Why is it adding a empty element shown in the screenshot
You can simply solve this by using the "safe navigation operator".
When you use the interpolation, it is recommended to use ? ("safe navigation") when the object may be undefined.
<a href="#" class="list-group-item clearfix">
<div class="pull-left">
<h4 class="list-group-item-heading">{{ recipe?.name }}</h4>
<p class="list-group-item-text">{{ recipe?.description }}</p>
</div>
<span class="pull-right">
<img [src]="recipe.imagePath" [alt]="recipe.name" class="img-responsive" style="max-height:50px">
</span>
</a>
This will clear your console problems, but you may need to *ngFor in a div that surrounds the component:
<div *ngFor="let recipeEl of recipes">
<app-recipe-item [recipe]="recipeEl"></app-recipe-item>
</div>
And a plus: when you are working inside a HTML tag, don't use interpolation, use property binding instead. (example [alt]="recipe.name")
I think I cracked this case: in your recipe-list component template you have <app-recipe-item></app-recipe-item> added at the end for some reason, seems like some leftover code.
The errors are likely being thrown by that component because there is no any input value provided to it. This also explains the empty element you have at the bottom of the screenshot.
Remove that and that should solve the console error you mentioned, and get rid of the empty element. Good luck!

How do I use the index value of an array and pass it to a HTML modal so I can show the data there without using a loop in angular 7

How do I use the index value of an array and pass it to a HTML modal so I can show the data there without using a loop in angular 7
import { Component, OnInit } from '#angular/core';
import { ApiService } from '../services/api.service';
import { movieModel } from '../models/movie';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.less']
})
export class HomeComponent implements OnInit {
movies:movieModel[];
constructor(public api:ApiService) { }
ngOnInit() {
this.loadMovies();
}
loadMovies(): void {
this.movies = [];
this.api.getMovies().subscribe(
data =>
{
this.movies = data.results;
this.movies = this.movies.slice(0 , 5);
console.log(this.movies);
}
);
}
}
<h1>Top 5 Movies by the New York Times</h1>
<div class="uk-child-width-1-3#s uk-grid-match" uk-grid>
<div *ngFor="let movie of movies; let i = index">
<div class="uk-card uk-card-hover uk-card-body">
<h3 class="uk-card-title">{{movie.display_title}}</h3>
<span>Headline: {{movie.headline}}</span><br/>
<span>Summary: {{movie.summary_short | characters:150 }}</span><br/><button class="uk-button uk-button-default" uk-toggle="target: #my-id">Read More</button><br/>
<p>By: {{movie.byline}}<br/>Rating:{{mpaa_rating || NA}}<br/>Date of Release: {{movie.publication_date | date: 'dd/MM/yyyy'}}</p>
</div>
</div>
</div>
<div id="my-id" uk-modal>
<div class="uk-modal-dialog uk-modal-body">
<h2 class="uk-modal-title">Summary</h2>
{{movie.summary_short}}
<button class="uk-modal-close uk-button uk-button-default" type="button">Close</button>
</div>
</div>
Can someone please explain to me how i get the value for movie.summary_short to work in the dialog box I have the for loop index done but cant figure out how to pass it to the other HTML element
Declare another property like summary_short in component.ts.
bind on (click) of 'Read More' button to assign movie.summary_short to summary_short.
component.html
<button (click)="saveSummary(movie.summary_short)" class="uk-button uk-button-default" uk-toggle="target: #my-id">
Read More
</button>
...
<div id="my-id" uk-modal>
<div class="uk-modal-dialog uk-modal-body">
<h2 class="uk-modal-title">Summary</h2>
{{summary_short}}
<button class="uk-modal-close uk-button uk-button-default" type="button">Close</button>
</div>
</div>
...
component.ts
...
summary_short
...
saveSummary(summary_short) {
this.summary_short = summary_short
}
...
Add a function to Read More button, something like this:
<button class="uk-button uk-button-default" uk-toggle="target: #my-id" (click)="readMore(movie.summary_short)">Read More</button>
Then in .ts, declare a var modalText and each time this button is clicked:
readMore(text: string){
this.modalText = text;
}
Finally, in modal, call {{ modalText }}.