I have a search form in an Angular app that I've written. You can type in a word or phrase and when you hit search you are given the results with the option to toggle between a standard list view (formatted to look like the results in a google search) or in a card view. The list view is the default setting and the user can toggle to the card view.
I handle the toggling via a mat-button-toggle-group and on (change) of the mat-button-toggle, I set a boolean variable that is used by my markup to determine which view to show. This all works brilliantly if I'm switching from the default view (list) to the card view. But when I want to switch back to the list view from the card view, nothing happens. I've added logging to the (change) event so I know the variable is being reset but nothing is happening. Below is my code:
search.component.html
<div class="search-fields page-content">
<mat-form-field appearance="outline" disabled="false">
<mat-label>Keyword(s)</mat-label>
<input #txtSearch matInput type="search" placeholder="Search Phrase" [(ngModel)]="this.searchPhrase" (keyup.enter)="onSearchClick()">
<button mat-button *ngIf="value" matSuffix mat-icon-button aria-label="Clear" (click)="this.searchPhrase=''">
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
<button mat-raised-button color="primary" (click)="onSearchClick()">Search</button>
</div>
<div *ngIf="dataModel" class="container page-content">
<div class="full-width">
<mat-button-toggle-group value="false">
<mat-button-toggle selected="true" value="false" (change)="onToggleClick($event.value)" matTooltip="View List">
<mat-icon>view_list</mat-icon>
</mat-button-toggle>
<mat-button-toggle value="true" (change)="onToggleClick($event.value)" matTooltip="View Tiles">
<mat-icon>view_comfy</mat-icon>
</mat-button-toggle>
</mat-button-toggle-group>
</div>
<span class="query-info">Showing {{ getBeginHits() }} - {{ getEndHits() }} of {{ this.dataModel.totalHits }} results </span>
<div *ngIf="!isTile" class="result-detail">
<div *ngFor="let page of dataModel.pages">
<div class="full-width">
<h3>{{ page.title }}</h3>
</div>
<div class="full-width matches-detail">
<span>{{ page.matchesString }}</span>
</div>
<div class="full-width result-snippet" [innerHTML]="page.textSnippet">
</div>
</div>
</div>
<mat-grid-list cols="5" *ngIf="isTile">
<mat-grid-tile *ngFor="let page of dataModel.pages" [colspan]="1" [rowspan]="1">
<mat-card >
<mat-card-header>
<mat-card-title><strong>{{ page.title }}</strong></mat-card-title>
</mat-card-header>
<img mat-card-image src="{{ this.imageBase + page.id.trim() + '.jpg' }}" alt="Photo of page" />
<mat-card-content>
<ul>
<li *ngFor="let s of page.matchesArray">{{ s }}</li>
</ul>
</mat-card-content>
<mat-card-actions>
<button mat-raised-button color="primary" routerLink="{{ '/page/' + page.id.trim() }}">VIEW</button>
</mat-card-actions>
</mat-card>
</mat-grid-tile>
</mat-grid-list>
<div class="align-center">
<button mat-icon-button [disabled]="this.currentIndex == 1" (click)="onNavClick(this.currentIndex - 1)" >
<mat-icon aria-label="Previous">navigate_before</mat-icon>
</button>
<ng-container *ngFor="let item of this.getPageArray(); let i = index;">
<button mat-mini-fab color="primary" (click)="onNavClick(i + 1)">{{ i + 1 }}</button>
</ng-container>
<button mat-icon-button [disabled]="this.currentIndex == this.dataModel.totalPageResults" (click)="onNavClick(this.currentIndex + 1)">
<mat-icon aria-label="Next">navigate_next</mat-icon>
</button>
</div>
</div>
search.component.ts
import {Component, ElementRef, OnInit, ViewChild} from '#angular/core';
import {ActivatedRoute} from '#angular/router';
import {PageService} from '../service/page.service';
import {PageResult} from '../../shared/models/page-result.model';
import { environment } from '../../../environments/environment';
import * as _ from 'lodash';
import {_sanitizeHtml} from '#angular/core/src/sanitization/html_sanitizer';
#Component({
selector: 'app-search',
templateUrl: './search.component.html',
styleUrls: ['./search.component.scss']
})
export class SearchComponent implements OnInit {
searchPhrase: string;
isTile: boolean;
dataModel: PageResult;
imageBase: string = environment.imageUrl + 'thumbs/';
currentIndex: number;
constructor(private route: ActivatedRoute, private pageService: PageService) { }
ngOnInit() {
}
getPageArray(): any[] {
return new Array(this.dataModel.totalPageResults);
}
getBeginHits(): number {
return ((this.dataModel.currentIndex * this.dataModel.resultLimit) - this.dataModel.resultLimit + 1);
}
getEndHits(): number {
return (this.dataModel.currentIndex * this.dataModel.resultLimit);
}
onNavClick(index: number) {
this.currentIndex = index;
this.pageService.search(this.searchPhrase, index, 20)
.subscribe(
data => this.dataModel = data,
error => console.log(error),
);
}
onToggleClick(value: boolean) {
this.isTile = value;
console.log('isTile: ' + this.isTile.toString());
}
onSearchClick() {
this.currentIndex = 1;
this.isTile = false;
this.pageService.search(this.searchPhrase, 1, 20)
.subscribe(
data => this.dataModel = data,
error => console.log(error),
);
}
}
If you look at the onToggleClick() method, the logging there tells me that the isTile variable is definitely being changed. It's just not triggering a change to the variable. Furthermore, if I try to use the paging buttons, the view stays in Tile mode even if I've flipped tile mode off. Can anyone spot what I'm doing wrong?
Did you try wrapping
<mat-grid-list cols="5" *ngIf="isTile">
in a <div *ngIf="isTile">
as you do for full width ? maybe *ngIf is not working in mat-grid-list, but should work
Related
My task: The received value from service 1 or 2, depending on the value, change the styles in the html component. Using the ng-switch directive, I expect to compare the value received from the object with the specified template value. Styles are not applied. The main problem is that the styles are not applied
My component:
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, Self } from '#angular/core';
import { ConfigService, IProductListItem, SliderValueOptions } from 'common-modules';
import { Router } from '#angular/router';
import { debounceTime, filter, takeUntil, tap } from 'rxjs/operators';
import { NgOnDestroy } from '../../core/services/destroy.service';
import { BehaviorSubject } from 'rxjs';
import { OnInit, OnDestroy } from '#angular/core';
#Component({
selector: 'app-product-item',
templateUrl: './product-item.component.html',
styleUrls: ['./product-item.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [NgOnDestroy],
})
export class ProductItemComponent implements OnInit, OnDestroy {
#Input() product: IProductListItem;
#Input() doubleClickEnabled: boolean;
// tslint:disable-next-line:no-output-on-prefix
#Output() onAddToCart = new EventEmitter<any>();
themeProperties: number;
visibility: boolean = false;
constructor(private _router: Router, private _config: ConfigService, #Self() private onDestroy$: NgOnDestroy) {}
ngOnInit() {
this._config.marketplaceConfig.pipe(takeUntil(this.onDestroy$)).subscribe((config) => {
this.themeProperties = config.structure?.theme;
});
}
}
My html:
<mat-card class="card">
<div [ngSwitch]="themeProperties">
<ng-template *ngswitchcase="2">
{{ themeProperties }}
<mat-card id="cardbackground">
<a class="card__link" (click)="redirectToProduct(product)" (dblclick)="doubleClicked(product)">
<div
style="display: none"
class="card__promo"
*ngIf="product.priceWithDiscount && product?.promoIndividual?.percentDiscount"
>
-{{ product?.promoIndividual?.percentDiscount }}%
</div>
<!-- <b2b-modern-image mat-card-image [src]="product.photos[0]"></b2b-modern-image> -->
<img class="mat-card-image" [src]="product.photos" alt="" />
<mat-card-content class="card__content" [class.has-padding-top]="product && product['discount']">
<div class="super-sale">супер скидка</div>
<div *ngIf="product && product['discount']" class="discount">-{{ product['discount'] }}%</div>
<div
*ngIf="product && product.promoIndividual && product?.promoIndividual?.percentDiscount"
class="discount"
>
-{{ product?.promoIndividual?.percentDiscount }}
%
</div>
<ng-template #defaultPrice>
<!-- <span class="price">{{product.price}} ₽</span>-->
</ng-template>
<div class="rating">
<ngx-stars color="#ffd700" [readonly]="true" [initialStars]="product.rating || 0"> </ngx-stars>
<span class="ml-3">{{ product.views || 0 }} отзывов</span>
</div>
<h3 class="name wrap-two-line" [matTooltip]="product?.feed" matTooltipPosition="above">
{{ product | localize: 'name' }}
</h3>
<div class="а">
<div
class="d-flex align-items-center"
*ngIf="product.priceWithDiscount && product.priceWithDiscount != product.price; else defaultPrice"
>
<span class="price old">{{ product.price }}₽</span>
</div>
<p class="price">{{ (product.priceWithDiscount || 0).toFixed(1) }}₽</p>
</div>
</mat-card-content>
</a>
<mat-card-footer class="card__footer">
<a routerLink="/confirm-order" [queryParams]="{ products: product.id }" mat-raised-button color="primary">
Купить сейчас
</a>
<button mat-button color="primary" (click)="addToCart(product)">
<mat-icon>add_shopping_cart</mat-icon>
</button>
</mat-card-footer>
</mat-card>
</ng-template>
</div>
</mat-card>
change component to:
themeProperties$: Observable<number>;
...
ngOnInit() {
this.themeProperties$ = this._config.marketplaceConfig.pipe(map((config) => config.structure?.theme))
}
change template to:
<div [ngSwitch]="themeProperties$ | async">
<ng-container *ngSwitchCase="2">
....
</ng-container>
issues:
casing wrong on ngSwitchCase directive
OnPush change detection not running correctly, async pipe resolves
ng-template doesn't render to DOM. it's a template for use elsewhere. use ng-container instead.
The spelling of the template switch statement is case sensitive, should be *ngSwitchCase.
Also the ng-template does not work in this case, you need to use ng-container
So please change your statements to this layout, and all should work:
<ng-container *ngSwitchCase="2">
also because you are using ChangeDetectionStrategy.OnPush you need to trigger the change detection manually for the ngSwitch.
Here is a working example.
constructor(private _router: Router, private _config: ConfigService, #Self() private onDestroy$: NgOnDestroy, private cdr: ChangeDetectorRef) {}
ngOnInit() {
this._config.marketplaceConfig.pipe(takeUntil(this.onDestroy$)).subscribe((config) => {
this.themeProperties = config.structure?.theme;
this.cdr.detectChanges(); // <=== here, after setting the value
});
}
I am building an Angular Movie DB, the two components I am working with are the "search-movie" component and the "single-view" component. The "search-movie" component displays a list of movies, the "single-view" component shows further details of a given movie.
I would like it so that when I click on a movie from the list in the "search-movie" component, the page renders the "single-view" component for that movie.
I have managed to set this up so that navigating to the "single-view" path with a movie id (i.e. /movie/tt4425200) directly from the URL, correctly loads up the info for that movie by it's ID, so that's all correctly set up.
I just can't seem to connect the clicking of a movie in the "search-movie" component, to then successfully navigate to the correct "single-view" path. So using the example above, clicking on the relevant movie loads up the "single-view" component with the URL path /movie/tt4425200.
I'm sure that this is a case of using #Input to communicate between the two components, but I just can't figure it out.
search-movie.component.ts:
import { Component, OnInit, Input } from '#angular/core';
import { FormControl } from '#angular/forms';
import { DataService } from '../data.service';
#Component({
selector: 'app-search-movie',
templateUrl: './search-movie.component.html',
styleUrls: ['./search-movie.component.css']
})
export class SearchMovieComponent implements OnInit {
searchControl = new FormControl('');
movieResults: any = [];
constructor(private data: DataService) { }
ngOnInit() {}
/* GET request */
getData(event) {
const film = event.target.value;
this.data.searchFilm(film)
.subscribe( (res) => {
res = this.movieResults = res;
console.log(res);
});
}
}
search-movie.component.html:
<div class="container">
<h1 class="is-size-3">Find your favorite movies...</h1>
<form (ngSubmit)="onSubmit()">
<input
type="text"
(keyup)="getData($event)"
placeholder="Start typing..."
[formControl]="searchControl" />
</form>
{{searchControl.value}}
</div>
<div class="container">
<ul>
<li id="list-item" *ngFor="let x of movieResults; let i = index;">
<img [src]="x.Poster" onerror="this.src='../assets/images/default-poster.jpg'">
<p id="movie-title" class="is-size-5">{{x.Title}} ({{x.Year}}) </p>
</li>
</ul>
</div>
single-view.component.ts:
import { Component, OnInit, Input } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { DataService } from '../data.service';
#Component({
selector: 'app-single-view',
templateUrl: './single-view.component.html',
styleUrls: ['./single-view.component.css']
})
export class SingleViewComponent implements OnInit {
singleDetails: any = {};
constructor(
private data: DataService,
private route: ActivatedRoute) { }
ngOnInit() {
/** GENERATE FILM DETAILS BY movie/id where id is the imdbID */
const id = this.route.snapshot.paramMap.get('id');
this.data.moviebyID(id)
.subscribe( (res) => {
res = this.singleDetails = res;
});
}
}
single-view.component.html:
<div class="container singleDisplayContainer">
<div class="columns is-mobile">
<div class="column" id="poster-column">
<img id="poster" [src]="singleDetails.Poster" [alt]="singleDetails.Title" onerror="this.src='../assets/images/default-poster.jpg'">
</div>
<div id="info-column" class="column">
<div class="columns">
<div class="column is-three-fifths">
<h1 class="is-size-3">{{singleDetails.Title}} <span class="is-size-4">({{singleDetails.Year}})</span> </h1>
<p style="font-size: 14px"> {{singleDetails.Rated}} | {{singleDetails.Runtime}} | {{singleDetails.Genre}} | {{singleDetails.Released}} </p>
</div>
<div class="column">
<div class="columns">
<div class="column is-one-third">
<img id="star-rating" width="50px" src="../../assets/images/star-rating.png" alt="{{singleDetails.Title}}">
</div>
<div class="column">
<p style="font-size: 2em"> {{singleDetails.imdbRating}}<span style="font-size: 12px">/10</span></p>
<p>{{singleDetails.imdbVotes}}</p>
</div>
</div>
</div>
</div>
<br>
<p>Plot: {{singleDetails.Plot}} </p>
<br>
<p>Directed by: {{singleDetails.Director}} </p>
<br>
<p>Writers: {{singleDetails.Writer}} </p>
<br>
<p>Actors: {{singleDetails.Actors}} </p>
<br>
<p onerror="this.style.display='none'">Box office: {{singleDetails.BoxOffice}} </p>
</div>
</div>
</div>
In Your UL -> LI, add routerLink property and pass the movie id as the second param
[routerLink]="['/movie/,x.movieId]">
<ul>
<li id="list-item" *ngFor="let x of movieResults; let i = index;" [routerLink]="['/movie/,x.movieId]">
<img [src]="x.Poster" onerror="this.src='../assets/images/default-poster.jpg'">
<p
id="movie-title"
class="is-size-5">{{x.Title}} ({{x.Year}}) </p>
</li>
</ul>
Looks like you have everything set up, all you need to do is navigate to single-view on click of the movie.
search-movie.component.ts
showDetails(id : string) {
this.router.navigate(['movie',{ id }]);
}
search-movie.component.html
<!-- in showDetails you will pass id of the current movie while looping -->
<li id="list-item" *ngFor="let x of movieResults; let i = index;" (click)="showDetails(x.id)">
<img
[src]="x.Poster"
onerror="this.src='../assets/images/default-poster.jpg'"
>
<p
id="movie-title"
class="is-size-5">{{x.Title}} ({{x.Year}}) </p>
</li>
This will navigate to single-view page. is this what you want right?
or you need to load single-view in the same page as the list?
I am building an accordion based on array of data fetched from the server but the clicks are not working.
The accordion is working fine on hard-coded data but not on the data fetched using HttpClient. I even tried using button instead of anchor tags but to no avail.
<div class="accordion" id="chalsAccordion">
<div class="card rounded-0" *ngFor="let chal of chals">
<a data-toggle="collapse" href="#{{ chal._id }}"
><div class="card-header text-dark">
<p class="mb-0">
{{ chal.title }}<small> - {{ chal.author }}</small>
</p>
<p class="mb-0 ml-auto">{{ chal.points }}</p>
</div></a
>
<div id="{{ chal._id }}" class="collapse">
<div class="card-body" [innerHTML]="chal.desc"></div>
<div class="card-footer" *ngIf="!userService.admin">
<form [formGroup]="flagForm" style="width: 100%">
<div class="input-group">
<input type="text" class="form-control rounded-0" placeholder="Flag" formControlName="flag" />
<div class="input-group-append">
<button class="btn btn-primary rounded-0" [disabled]="!flagForm.valid" (click)="submitFlag(chal._id)">Submit</button>
</div>
</div>
</form>
</div>
<div class="card-footer" *ngIf="userService.admin">
<div class="ml-auto">
<button class="btn btn-danger rounded-0"><fa-icon [icon]="['fas', 'trash']"></fa-icon></button>
</div>
</div>
</div>
</div>
</div>
<button class="btn btn-primary add-chal-btn" routerLink="/chals/add" *ngIf="userService.admin"><fa-icon [icon]="['fas', 'plus']"></fa-icon></button>
import { Component, OnInit } from "#angular/core";
import { FormGroup, FormControl, Validators } from "#angular/forms";
import { ToastrService } from "ngx-toastr";
import { UserService } from "../services/user.service";
import { ChalService } from "../services/chal.service";
import { Response } from "../interfaces/response";
import { $ } from "protractor";
#Component({
selector: "app-chals",
templateUrl: "./chals.component.html",
styleUrls: ["./chals.component.scss"]
})
export class ChalsComponent implements OnInit {
chals = [];
flagForm = new FormGroup({
flag: new FormControl("", [Validators.required])
});
constructor(private toast: ToastrService, public userService: UserService, private chalService: ChalService) {}
ngOnInit() {
this.chalService.getAll().subscribe(
(data: Response) => {
this.chals = data.data["chals"];
},
err => {
this.toast.error(err.error.msg, "Oops!!!");
}
);
}
submitFlag(id: string) {}
}
Edit - The response is coming fine and the UI is also rendered correctly, just the problem is that the click does not expand the accordion.
Arrays are Index based so to access an element in an Array you have to provide an Index. To get the results from the response simply try to set the response to your array.
`ngOnInit() {
this.chalService.getAll().subscribe(
(data: Response) => {
this.chals = data.data; // -- updated the code here
},
err => {
this.toast.error(err.error.msg, "Oops!!!");
});
}`
Also please debug chalService to see if you are getting the response of your http call to the api.
I found the problem, it was that the _id field was starting with an int which was the root of the problem.
I solved it by adding an _ to every _id field.
I am trying to hide a button when clicked.
component.ts:
import { Component, Input, OnInit, ChangeDetectionStrategy, ChangeDetectorRef } from '#angular/core';
import { DataService } from '../../shared/service/data.service';
import { TreeNode } from '../../shared/dto/TreeNode';
import html from './rightside.component.html';
import css from './rightside.component.css';
#Component({
selector: 'rightside-component',
template: html,
providers: [DataService],
styles: [css],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class RightSideComponent implements OnInit {
selections: string[];
#Input() treeNode: TreeNode<string>[];
hide: boolean = false;
constructor(private cd: ChangeDetectorRef) {}
ngOnInit() {
}
getSelections() : TreeNode<string>[] {
if (typeof(this.treeNode) == "undefined" || (this.treeNode) === null) {
return [];
}
return this.treeNode;
}
deselect(item: TreeNode<string>): void {
this.hide = true;
if((item.children) !== null) {
item.children.forEach(element => {
this.deselect(element);
});
}
item.selected = false;
}
}
component.html:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<div>
<ul class="selection-list">
<li *ngFor="let item of getSelections()">
<button class="btn" (click)="deselect(item)" *ngIf="!hide">
<i class="fa fa-close"> {{ item.displayName }} </i>
</button>
</li>
</ul>
</div>
When I click on any button, all of the items are disappearing. I want just the clicked item to disappear. When I select the checkbox again, the item should re-appear. I want to implement something similar to this plunkr I found but for my data structure :
http://next.plnkr.co/edit/1Fr83XHkY0bWd9IzOwuT?p=preview&utm_source=legacy&utm_medium=worker&utm_campaign=next&preview
How can I fix this? Let me know if any other code is required.
Instead of using the common hide variable, use selected attribute in each item since you are making it false when you deselect.
<button class="btn" (click)="deselect(item)" *ngIf="item.selected">
<i class="fa fa-close"> {{ item.displayName }} </i>
</button>
Try This
hide= [];
<div>
<ul class="selection-list">
<li *ngFor="let item of getSelections();let i=index">
<button class="btn" (click)="deselect(item:
TreeNode<string>);hide[i]=!hide[i]" *ngIf="hide[i]">
<i class="fa fa-close"> {{ item.displayName }} </i>
</button>
</li>
</ul>
</div>
All your items disappears because they all share the same "hide" attribute so when you click on One of them it will change for all the items. What you should have is an attribute for each item (this attribute should be initialized at true)
component.html
<div>
<ul class="selection-list">
<li *ngFor="let item of getSelections()">
<button class="btn" (click)="deselect(item)" *ngIf="item.selected">
<i class="fa fa-close"> {{ item.displayName }} </i>
</button>
</li>
</ul>
</div>
component.ts
deselect(item: TreeNode<string>): void {
item.selected = false;
if((item.children) !== null) {
item.children.forEach(element => {
this.deselect(element);
});
}
}
So I'm sending a movie object from a parent to a child component. I use #Input from the core module to do this.
Now, I am able to use this data inside the template to display my view, but when I try to access it in for example editReview() then this.movie returns undefined.
Does anyone know how to fix this?
Child Component: the console.log returns undefined
export class ReviewListComponent implements OnInit {
#Input('movie') movie;
constructor(private reviewService: ReviewService){}
editReview() {
console.log(this.movie);
}
}
Child Component Template: I am able to display my name and the content of the review. What's not working unfortunately is when I click the button it says that this.movie is undefined
<ul class="list-group">
<li *ngFor="let review of movie?.reviews" class="list-group-item">
<p>{{review.user.firstName}} {{ review.user.lastName}}</p>
<hr>
<p>{{review.content}}</p>
</li>
</ul>
<button (click)="editReview()">Button</button>
Edit: I added the components and templates of the two parent components: The first one is the movie component where I retrieve the movie object asynchronously, the second is the review component which contains the review-list component that I had already posted above
movie.component.ts
import {Component, OnInit} from "#angular/core";
import { MovieService} from "./movie.service";
import {ActivatedRoute} from "#angular/router";
#Component({
selector: 'app-movie',
templateUrl: './movie.component.html',
styleUrls: ['./movie.component.css']
})
export class MovieComponent implements OnInit{
rating;
movie;
movieId;
constructor(private movieService: MovieService, private route: ActivatedRoute){
}
checkRating(){
return typeof this.rating === 'number';
}
ngOnInit(){
this.route.params.subscribe(params => {
this.movieId = params.id;
var userId = localStorage.getItem('userId');
this.movieService.getMovie(this.movieId)
.subscribe(movie => {
this.movie = movie.obj;
this.movie.averageRating = Number(this.movie.averageRating);
console.log(this.movie);
});
if (userId){
this.movieService.getRating(userId, this.movieId)
.subscribe( result => {
this.rating = result.rating;
})
}
});
}
addRating(score) {
var userId = localStorage.getItem('userId');
this.movieService.addRating(score, userId, this.movie._id)
.subscribe(data => {
this.rating = data.obj.rating;
});
}
}
movie.component.html: this gives movie object to review.component through element app-review
<div class="container">
<div class="col col-md-4 col-md-offset-2">
<img [src]="movie?.pictureUrl" class="img-responsive" alt="hello">
</div>
<div class=" col col-md-6">
<h2>{{movie?.title}} ({{movie?.year}})</h2>
<h3>Average Score:</h3>
<span class="glyphicon glyphicon-star"></span><span><h1>{{movie?.averageRating}}</h1></span><br>
<h3>My Score:</h3>
<div class="row">
<div class="col">
<div *ngIf="!checkRating()" ngbDropdown class="d-inline-block">
<button class="btn btn-outline-primary" ngbDropdownToggle>Rate The Movie!</button>
<div ngbDropdownMenu aria-labelledby="dropdownBasic1">
<button value="1" (click)="addRating(1)" class="dropdown-item">1</button>
<button value="2" (click)="addRating(2)" class="dropdown-item">2</button>
<button value="3" (click)="addRating(3)" class="dropdown-item">3</button>
<button value="4" (click)="addRating(4)" class="dropdown-item">4</button>
<button value="5" (click)="addRating(5)" class="dropdown-item">5</button>
</div>
</div>
<div *ngIf="checkRating()">
<h1>{{rating}}</h1>
</div>
</div>
</div>
<h4>{{movie?.director}}</h4>
<h4>{{movie?.actors}}</h4>
<h4>{{movie?.country}}</h4>
<p> {{movie?.description}}</p>
</div>
</div>
<hr class="col-md-8 col-md-offset-2">
<div class="col-md-8 col-md-offset-2">
<ng-container *ngIf="!!movie">
<app-review [movie]="movie"></app-review>
</ng-container>
</div>
review.component.ts
import {Component, ElementRef, Input, OnInit, ViewChild} from "#angular/core";
import {NgForm} from "#angular/forms";
import {ReviewService} from "./review.service";
import {ActivatedRoute} from "#angular/router";
#Component({
selector: 'app-review',
templateUrl: './review.component.html',
styleUrls: ['./review.component.css']
})
export class ReviewComponent implements OnInit{
movieId;
#Input('movie') movie;
#ViewChild('textArea') textArea;
constructor(private reviewService: ReviewService, private route: ActivatedRoute){}
ngOnInit(){
this.route.params.subscribe( params => {
this.movieId = params.id
});
};
onClear(form: NgForm) {
form.resetForm()
}
onSubmit(form: NgForm) {
let content = form.value.review;
let userId = localStorage.getItem('userId');
let movieId = this.movieId;
this.reviewService.addReview(content, userId, movieId)
.subscribe( result => {
console.log(result.obj);
this.movie.reviews.unshift(result.obj);
form.resetForm()
})
}
}
review.component.html: this gives movie object to the review-list component through app-review-list
<ng-container *ngIf="!!movie">
<app-review-list [movie]="movie"></app-review-list>
</ng-container>
<hr>
<form ngNativeValidate (ngSubmit)="onSubmit(f)" #f="ngForm">
<div class="form-group">
<label for="review">Write A Review:</label>
<textarea
#textArea
(keyup.enter)="onSubmit(f)"
rows="4"
cols="50"
type="text"
id="review"
ngModel
class="form-control"
name="review"
required
></textarea>
</div>
<button type="button" class="btn btn-danger" (click)="onClear(f)">Clear</button>
<button class="btn btn-primary" type="submit">Save</button>
</form>
you can access it in ngOnInit() just implement OnInit interface