Create pages with Angular
Create pages with Angular 1Create pages with Angular 2
Create pages with Angular 3
What I did
- getting/setting data for select tag
- transfer data between parent component and child components
getting/setting data for select tag
I got list items from service and added options to selectbox.favorite.ts
export interface Favorite {
displayValue: string;
favoriteId: number;
}
favorite.service.ts
import {Injectable} from '@angular/core';
import {Favorite} from './favorite';
@Injectable({
providedIn: 'root'
})
export class FavoriteService {
public static favorites: Array<Favorite> = [
{ displayValue: '☆', favoriteId: 1},
{ displayValue: '☆☆', favoriteId: 2},
{ displayValue: '☆☆☆', favoriteId: 3},
];
constructor() { }
}
product-cell.component.ts
import { Component, OnInit, Input } from '@angular/core';
import {CellComponent} from '../cell.component';
import {Product} from '../../../models/product';
import {ProductCategory} from '../../../models/product-category';
import {Favorite} from '../favorite';
import {FavoriteService} from '../favorite.service';
@Component({
selector: 'app-product-cell',
templateUrl: './product-cell.component.html',
styleUrls: ['./product-cell.component.css']
})
export class ProductCellComponent implements OnInit, CellComponent {
@Input() products: Product[];
@Input() category: ProductCategory;
public favorites = new Array<Favorite>();
constructor() {
this.favorites = FavoriteService.favorites;
}
ngOnInit() {
}
}
product-cell.component.html
<div class="product-cell-frame">
<div class="product-cell-left">
<div class="product-cell-ranking">
{{products[0].rankingNo}}
</div>
<div class="product-cell-favorite">
<select>
<option *ngFor="let favorite of favorites" [value]="favorite.favoriteId">
{{favorite.displayValue}}
</option>
</select>
</div>
</div>
<div class="product-cell-right">
<div class="product-cell-item-name">
{{products[0].name}}
</div>
<div class="product-cell-category">
{{category.name}}
</div>
</div>
</div>
I could get selectbox value from ngModel.
product-cell.component.ts
...
export class ProductCellComponent implements OnInit, CellComponent {
@Input() products: Product[];
@Input() category: ProductCategory;
public favorites = new Array<Favorite>();
public selectedFavoriteId: number;
constructor() {
this.favorites = FavoriteService.favorites;
}
...
}
product-cell.component.html
<div class="product-cell-frame">
<div class="product-cell-left">
<div class="product-cell-ranking">
{{products[0].rankingNo}}
</div>
<div class="product-cell-favorite" [(ngModel)]="selectedFavoriteId">
<select>
<option *ngFor="let favorite of favorites" [value]="favorite.favoriteId">
{{favorite.displayValue}}
</option>
</select>
</div>
</div>
<div class="product-cell-right">
<div class="product-cell-item-name">
{{products[0].name}}
</div>
<div class="product-cell-category">
{{category.name}}
</div>
</div>
</div>
When I changed the selectbox value, "selectedFavoriteId" got "favorite.favoriteId".
And I could only get basic type value as ngModel.
When I changed the option value like "[value]="favorite"", I couldn't get the value of "Favorite" type.
Selecting index
How about selecting the index by script?It only needed to set value to "selectedFavoriteId".
product-cell.component.ts
...
export class ProductCellComponent implements OnInit, CellComponent {
@Input() products: Product[];
@Input() category: ProductCategory;
public favorites = new Array<Favorite>();
public selectedFavoriteId: number;
constructor() {
this.favorites = FavoriteService.favorites;
this.selectedFavoriteId = this.favorites[1].favoriteId;
}
...
}
How about the value what wasn't included in the options?
The value of "selectedFavoriteId" kept value, but the selectbox was empty.
Transfer data between parent component and child components
Sometimes, I wanted to set values for child components after their constructors and ngOnInits fired.If I used the values only at html templates, I just needed to set values.
But I had to generate values after setting values, I must call methods of child components.
ngOnChanges
Because I couldn't call methods through @Input(), I tried to use ngOnChanges.product-ranking-block.component.ts
import {Component, OnInit, Input, ComponentFactoryResolver, ViewChild, OnChanges, SimpleChanges} from '@angular/core';
import {ProductCellDirective} from '../../../product-cell.directive';
import {ProductCellItem} from '../../product-cell-item';
import {CellComponent} from '../cell.component';
@Component({
selector: 'app-product-ranking-block',
templateUrl: './product-ranking-block.component.html',
styleUrls: ['./product-ranking-block.component.css']
})
export class ProductRankingBlockComponent implements OnInit, OnChanges {
@Input() cells: ProductCellItem[];
@ViewChild(ProductCellDirective, {static: true}) cellHost: ProductCellDirective;
private components = new Array<CellComponent>();
constructor(private componentFactoryResolver: ComponentFactoryResolver) {
}
ngOnInit() {
this.load();
}
ngOnChanges(changes: SimpleChanges): void {
console.log(changes);
}
...
When I had changed the "cells", "ngOnChanges" was called.
product-ranking.component.ts
import {Component, OnInit} from '@angular/core';
import {Product} from 'src/app/models/product';
import {ProductCellService} from './product-cell.service';
import {ProductCellItem} from '../product-cell-item';
@Component({
selector: 'app-product-ranking',
templateUrl: './product-ranking.component.html',
styleUrls: ['./product-ranking.component.css']
})
export class ProductRankingComponent implements OnInit {
rankingFirst: ProductCellItem[];
constructor(private cellService: ProductCellService) {
this.rankingFirst = cellService.generateProductCellItems(
[
{id: 1, rankingNo: 1, name: 'Pixel 4', categoryId: 1} as Product,
{id: 2, rankingNo: 1, name: 'Pixel 4 XL', categoryId: 1} as Product,
{id: 3, rankingNo: 2, name: 'ng-book', categoryId: 2} as Product,
{id: 4, rankingNo: 2, name: 'RxJs in Action', categoryId: 2} as Product,
{id: 5, rankingNo: 3, name: 'Surface 6', categoryId: 0} as Product,
{id: 6, rankingNo: 4, name: 'Adaptive Code', categoryId: 2} as Product,
],
[
{ id: 0, name: 'computer'},
{ id: 1, name: 'phone'},
{ id: 2, name: 'e-book'}
]
);
}
...
changeRanking() {
this.rankingFirst = this.cellService.generateProductCellItems(
[
{id: 1, rankingNo: 1, name: 'Pixel 4', categoryId: 1} as Product
],
[
{ id: 0, name: 'computer'}
]
);
}
}
The arcument of "ngOnChanges" was like below.{…} cells: {…} currentValue: (1) […] 0: Object { component: ProductCellComponent(), products: (1) […], category: undefined } length: 1 <prototype>: Array [] firstChange: false previousValue: (5) […] 0: Object { component: ProductCellComponent(), products: (1) […], category: {…} } 1: Object { component: ProductCellComponent(), products: (1) […], category: {…} } 2: Object { component: ProductMergedCellComponent(), products: (2) […], category: {…} } 3: Object { component: ProductCellComponent(), products: (1) […], category: {…} } 4: Object { component: ProductMergedCellComponent(), products: (1) […], category: {…} } length: 5 <prototype>: Array [] <prototype>: Object { isFirstChange: isFirstChange(), … } <prototype>: Object { … }Because the input value had been updated and I hadn't needed comparing with previous values, I could use "cells" directoly.
Component Interaction - Angular
use #
I could get child components data with #.product-ranking.component.html
<div id="product-ranking-area">
<div id="product-ranking-frame">
<h3 id="product-ranking-title">
My ranking
</h3>
<div id="product-ranking-group">
<app-product-ranking-block [cells]="rankingFirst" #ranking ></app-product-ranking-block>
</div>
</div>
</div>
<button (click)="changeRanking(ranking.message)">ranking button</button>
product-ranking-block.component.ts
import {Component, OnInit, Input, ComponentFactoryResolver, ViewChild, OnChanges, SimpleChanges} from '@angular/core';
import {ProductCellDirective} from '../../../product-cell.directive';
import {ProductCellItem} from '../../product-cell-item';
import {CellComponent} from '../cell.component';
@Component({
selector: 'app-product-ranking-block',
templateUrl: './product-ranking-block.component.html',
styleUrls: ['./product-ranking-block.component.css']
})
export class ProductRankingBlockComponent implements OnInit, OnChanges {
@Input() cells: ProductCellItem[];
@ViewChild(ProductCellDirective, {static: true}) cellHost: ProductCellDirective;
public message = 'hello';
private components = new Array<CellComponent>();
constructor(private componentFactoryResolver: ComponentFactoryResolver) {
}
...
product-ranking.component.ts
import {Component, OnInit} from '@angular/core';
import {Product} from 'src/app/models/product';
import {ProductCellService} from './product-cell.service';
import {ProductCellItem} from '../product-cell-item';
@Component({
selector: 'app-product-ranking',
templateUrl: './product-ranking.component.html',
styleUrls: ['./product-ranking.component.css']
})
export class ProductRankingComponent implements OnInit {
...
changeRanking(message: string) {
console.log(message); // got 'hello'
...
}
}
Get data from child components what had been generated dynamically
Last time, I generated components with "ComponentFactoryResolver".After generating, how I could get data from child components?
I couldn't use #.
So I kept child components instances when they had been created.
product-ranking-block.component.ts
import {Component, OnInit, Input, ComponentFactoryResolver, ViewChild, OnChanges, SimpleChanges} from '@angular/core';
import {ProductCellDirective} from '../../../product-cell.directive';
import {ProductCellItem} from '../../product-cell-item';
import {CellComponent} from '../cell.component';
@Component({
selector: 'app-product-ranking-block',
templateUrl: './product-ranking-block.component.html',
styleUrls: ['./product-ranking-block.component.css']
})
export class ProductRankingBlockComponent implements OnInit, OnChanges {
@Input() cells: ProductCellItem[];
@ViewChild(ProductCellDirective, {static: true}) cellHost: ProductCellDirective;
private components = new Array<CellComponent>();
constructor(private componentFactoryResolver: ComponentFactoryResolver) {
}
...
public load() {
const viewContainerRef = this.cellHost.viewContainerRef;
viewContainerRef.clear();
this.components = [];
for (const cell of this.cells) {
const factory = this.componentFactoryResolver.resolveComponentFactory(cell.component);
const componentRef = viewContainerRef.createComponent(factory);
const component = componentRef.instance as CellComponent;
this.components.push(component);
component.products = cell.products;
component.category = cell.category;
}
}
public changeSample() {
for (const cell of this.components) {
// I could get child components data.
}
}
}
Though I could get access to child components in this way, I didn't know I should do this or not.
If the data wouldn't be changed by child components, I could keep the data on parent component.
Next time, I will try using events.
コメント
コメントを投稿