Update component when input changes in Angular - html

<div class=" card-body">
<div class="row">
<div class=" font-icon-list col-lg-2 col-md-3 col-sm-4 col-xs-6 col-xs-6" routerLinkActive="active"
*ngFor="let subject of subjects">
<div class=" font-icon-detail">
<div (click)="setSubject(subject.title)" >
<i class="deep-icons {{ subject.icon }}"></i>
<p>{{ subject.title }}</p>
</div>
</div>
</div>
</div>
</div>
<subject-component [setSubjectService]="selectedSubject"></subject-component>
The code above is from my selector.component.html.
export class SubjectComponent implements OnInit {
#Input() public set setSubjectService(_subjectService: ISubjectService) {
this.subjectService = _subjectService;
}
public subjectService: ISubjectService;
constructor() {}
public ngOnInit(): void {
}
}
The code above is from my subject.component.ts
Right now subject-component gets rendered once when the application starts up, but whenever "selectedSubject" changes it does not update or re-render. How can I make this possible?
(Using Angular 8)

I think you want to use two way binding.
If so;
Create an output with eventemitter like;
#Input() myVariable: number;
#Output() myVariableChange = new EventEmitter(); //define it nameofyourvariable+Change
constructor() { }
ngOnInit() {}
changeMyVar(v: number) {
this.myVariable=v;
this.myVariableChange.emit(myVariable);
}
And use two way binding with parantheses. I mean like following:
<subject-component [(setSubjectService)]="selectedSubject"></subject-component>

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

Angular TypeError when the page opens

In my code, I'm displaying a list of elements on the main page. When you click one of those elements, a side-pannel opens and you are able to see the details of that element. Whenever the page first opens I get the error below. The code doesn't crash or anything, but I don't get why the error keeps appearing. What should I do to fix it?
StickerListComponent.html:167 ;
<fuse-sidebar class="sidebar centra details-sidebar fuse-white" name="sticker-preview-sidebar" position="right">
<sticker-preview [StickerData]="previewStickerData"></sticker-preview>
</fuse-sidebar>
sticker-preview-component html ;
<div class="h-100-p" fxLayout="column" fusePerfectScrollbar>
<div class="group mt-32">
<div fxLayoutAlign="start center">
<header class="purple-fg" style="font-size:18.72px"><strong>Sticker Data:</strong></header>
</div>
<p>
<span id="sticker" contenteditable [textContent]="_stickerData?.StickerData" (input)="onStickerDataChange($event.target.innerHTML)">
{{_stickerData?.StickerData}}
</span>
</p>
</div>
<div fxLayout="row" fxLayoutAlign="start center">
<button mat-button matRipple class="purple-500 fuse-white-fg mr-12" (click)="imageSource()"> Display </button>
</div>
<div>
<img [src]="url" *ngIf="hidden" />
</div>
</div>
sticker-preview-component ts:
export class StickerPreviewComponent implements OnInit {
EditIndex: number;
public _stickerData: IStickerData = {};
confirmDialogRef: MatDialogRef<FuseConfirmDialogComponent>;
Filter: IFilter = {};
hidden = false;
url;
#Input()
set StickerData(prm: IStickerData) {
if (this._stickerData != prm) {
this._stickerData = prm;
}
33 this.url = "http://api.labelary.com/v1/printers/8dpmm/labels/4x6/0/" + this._stickerData.StickerData;
}
get StickerData(): IStickerData {
return this._stickerData;
}
dataSource: MatTableDataSource<IStickerData>;
ngOnInit() {
this._productionService.getStickerDataList(this.Filter)
.subscribe((response: any) => this._stickerData = response);
}
imageSource(): void{
this.hidden = !this.hidden;
}
it seems the _stickerData is undefined at the time when it assigns to the url, try to check whether the _stickerData has the proper value and then assign it.
if(this._stickerData)
this.url = "http://api.labelary.com/v1/printers/8dpmm/labels/4x6/0/" + this._stickerData.StickerData;

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!

Native Element toArray() undefined

I'm doing some refactoring on a application that I'm working on and I'm stuck.
On my HTML I have a grid with some cards using ngFor. There is a button that should "show/hide" a specific div.
My solution was to use a local variable to get the specific element and show that specific div. It was working fine, but now, I can't get it to work, I get this error:
ERROR TypeError: "this.travelInfoBox.toArray(...)[index] is undefined"
showBox card.component.ts:40
View_CardComponent_0 CardComponent.html:12
Here is the HTML for the parent component:
<div class="row">
<div class="col s12 m6 l4" *ngFor="let student of students; let i = index">
<app-card [student]="student" [i]="i"></app-card>
</div>
</div>
Here is the HTML for the card component:
<div class="col s12 m4 l2 right">
<hr class="hide-on-med-and-up">
<i class="material-icons">send</i>
<i class="material-icons" (click)="showBox(i)">access_alarm</i>
<span class="hide-on-med-and-up card-travel-info">Tavel Information</span>
<app-travel-info class="card travel-info s12 m6 l4" #travelInfoBox></app-travel-info>
<hr class="hide-on-med-and-up">
</div>
TS for the card component:
import {Component, Input, ViewChildren, QueryList, ElementRef, AfterViewInit } from '#angular/core';
#Component({
selector: 'app-card',
templateUrl: './card.component.html',
styleUrls: ['./card.component.scss']
})
export class CardComponent {
#Input('student') student: any;
#Input('i') i: any;
#ViewChildren('travelInfoBox') private travelInfoBox: QueryList<ElementRef>;
constructor() { }
showBox(index) {
const nativeElement = this.travelInfoBox.toArray()[index].nativeElement;
nativeElement.style.display = nativeElement.style.display === 'none' || !nativeElement.style.display ? 'block' : 'none';
}
}

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 }}.