Create pages with Angular
Create pages with Angular 1Create pages with Angular 2
Data bindings with Pikaday
Last time, I didn't know how to use Two-way data bindings to the element what was set date by Pikaday.According to the document, I could separate [(ngModel)]="dateText" like below.
Before
<input type="text" id="datepicker" [(ngModel)]="dateText">
After
<input type="text" id="datepicker" [value]="dateText"
(input)="dateText=$event.target.value">
And because when Pikaday set text, the input event wasn't fired, thus I got nothing.
So I changed the event.
<input type="text" id="datepicker" [value]="dateText"
(change)="dateText=$event.target.value">
Template Syntax - AngularBackground color for IE11
When I had set background-color like "background-color:#000000AA", the color wasn't shown by IE11.So I had to write like below.
#frame-background{
background-color: rgba(17, 17, 17, 0.67);
}
When I had used PostCSS, the color was automatically converted to the rgba(r, g, b, a) format.
So I fogot this :P
Use multiple providers
I had already used the provider to load the config file like this.app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, APP_INITIALIZER } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
import { TopPageComponent } from './top-page/top-page.component';
import { AppConfig } from './app-config.service';
export function initializeApp(settings: AppConfig) {
return () => settings.load();
}
@NgModule({
declarations: [
AppComponent,
TopPageComponent
],
imports: [
BrowserModule,
HttpClientModule
],
providers: [
{
provide: APP_INITIALIZER,
useFactory: initializeApp,
deps: [AppConfig], multi: true }
],
bootstrap: [AppComponent]
})
export class AppModule { }
If I wanted to use multiple providers, what should I do?
I just added the new one.
app.module.ts
...
export function initializeApp(settings: AppConfig) {
return () => settings.load();
}
export function initializeApp2(itemLoader: ItemLoader) {
return () => itemLoader.load();
}
@NgModule({
...
providers: [
{
provide: APP_INITIALIZER,
useFactory: initializeApp,
deps: [AppConfig], multi: true
},
{
provide: APP_INITIALIZER,
useFactory: initializeApp2,
deps: [ItemLoader], multi: true
},
],
bootstrap: [AppComponent]
})
export class AppModule { }
If both of the classes were independ each other, there was no problem.
But if ItemLoader class's load() had to wait for finishing the AppConfig's load(), the exception would be occurred.
So I had to call in order.
app.module.ts
...
export function initializeApp(settings: AppConfig,
itemLoader: ItemLoader) {
return () => {
settings.load()
.then(_ => itemLoader.load());
}
}
@NgModule({
...
providers: [
{
provide: APP_INITIALIZER,
useFactory: initializeApp,
deps: [AppConfig, ItemLoader], multi: true
},
],
bootstrap: [AppComponent]
})
export class AppModule { }
Dynamically switch child components
Resources
Dynamic Component Loader - AngularI wanted to switch components by data conditions.
For example, I had these data of products.
product.ts
import {ProductCategory} from './product-category';
export class Product {
public id: number;
public rankingNo: number;
public name: string;
public categoryId: number;
}
product-category.ts
export interface ProductCategory {
id: number;
name: string;
}
products
[
{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,
]
categories
[
{ id: 0, name: 'computer'},
{ id: 1, name: 'phone'},
{ id: 2, name: 'e-book'}
]
I wanted displaying like this.
These were the rule of display.
- Show the items that had the ranking number from 1 to 4.
- If the items' categories were "e-book", and the ranking numbers were same, the items would be merged.
- The merged items would be set to "merged cell".
Create components 1
First, I created the components for outside of the cells.product-ranking.component.html
<div id="product-ranking-area">
<div id="product-ranking-frame">
<div id="product-ranking-group">
<app-product-ranking-block></app-product-ranking-block>
</div>
</div>
</div>
product-ranking.component.css
#product-ranking-area{
background-color: blue;
display: flex;
align-items: center;
justify-content: center;
height: 90%;
width: 100%;
}
#product-ranking-frame{
background-color: aliceblue;
border-radius: 5px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 96%;
width: 96%;
}
#product-ranking-group{
display: flex;
flex-direction: row;
height: 90%;
width: 98%;
}
app-product-ranking-block{
height: 98%;
width: 30%;
}
import {Component, OnInit} from '@angular/core';
@Component({
selector: 'app-product-ranking',
templateUrl: './product-ranking.component.html',
styleUrls: ['./product-ranking.component.css']
})
export class ProductRankingComponent implements OnInit {
constructor() { }
ngOnInit() { }
}
product-ranking-block.component.html
<div class="product-ranking-block">
<div class="product-cell-title-frame">
<div class="product-cell-title"></div>
<div class="product-cell-edit-button">
<button>Edit</button>
</div>
</div>
</div>>
product-ranking-block.component.css
.product-ranking-block{
height: 100%;
width: 100%;
}
.product-cell-title-frame{
background-color: cadetblue;
border: solid 1px black;
border-radius: 5px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
height: 10%;
width: 100%;
}
.product-cell-title{
color: white;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
width: 50%;
}
.product-cell-edit-button{
display: flex;
align-items: center;
justify-content: center;
height: 100%;
width: 50%;
}
.product-cell{
height: 100%;
width: 100%;
}
product-ranking-block.component.ts
import {Component, OnInit, Input} from '@angular/core';
@Component({
selector: 'app-product-ranking-block',
templateUrl: './product-ranking-block.component.html',
styleUrls: ['./product-ranking-block.component.css']
})
export class ProductRankingBlockComponent implements OnInit {
constructor() { }
ngOnInit() { }
}
Create components 2
Next, I created the cell components and the interface to set data to cells.product-cell.component.html
<div class="product-cell-frame">
<div class="product-cell-left">
{{products[0].rankingNo}}
</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>
product-cell.component.css
.product-cell-frame{
border: black solid 1px;
border-radius: 5px;
display: flex;
flex-direction: row;
min-height: 60px;
width: 100%;
}
.product-cell-left{
display: flex;
align-items: center;
justify-content: center;
min-height: 100%;
width: 40%;
}
.product-cell-right{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100%;
width: 60%;
}
.product-cell-item-name{
display: flex;
align-items: center;
justify-content: center;
min-height: 50%;
width: 100%;
}
.product-cell-category{
display: flex;
align-items: center;
justify-content: center;
min-height: 50%;
width: 100%;
}
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';
@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;
constructor() { }
ngOnInit() { }
}
product-merged-cell.component.html
<div class="product-merged-cell-frame">
<div class="product-cell-left">
{{product.rankingNo}}
</div>
<div class="product-cell-right">
<div class="product-cell-item-name">
{{product.name}}
</div>
<div class="product-cell-category">
{{category.name}}
</div>
</div>
</div>
product-merged-cell.component.css
.product-merged-cell-frame{
border: black solid 1px;
border-radius: 5px;
background-color: aquamarine;
display: flex;
flex-direction: row;
min-height: 60px;
width: 100%;
}
.product-cell-left{
display: flex;
align-items: center;
justify-content: center;
min-height: 100%;
width: 40%;
}
.product-cell-right{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100%;
width: 60%;
}
.product-cell-item-name{
display: flex;
align-items: center;
justify-content: center;
min-height: 50%;
width: 100%;
}
.product-cell-category{
display: flex;
align-items: center;
justify-content: center;
min-height: 50%;
width: 100%;
}
product-merged-cell.ts
import {Component, Input, OnInit} from '@angular/core';
import {CellComponent} from '../cell.component';
import {Product} from '../../../models/product';
import {ProductCategory} from '../../../models/product-category';
@Component({
selector: 'app-product-merged-cell',
templateUrl: './product-merged-cell.component.html',
styleUrls: ['./product-merged-cell.component.css']
})
export class ProductMergedCellComponent implements OnInit, CellComponent {
@Input() products: Product[];
@Input() category: ProductCategory;
product = new Product();
constructor() {
}
ngOnInit() {
this.product.rankingNo = this.products[0].rankingNo;
// merge all product names.
this.product.name = this.products.map(p => p.name).join(',');
}
}
cell.component.ts
import {Product} from '../../models/product';
import {ProductCategory} from '../../models/product-category';
export interface CellComponent {
products: Product[];
category: ProductCategory;
}
Generate data for loading components dynamically
For loading components dynamically, add entryComponents to app.module.tsapp.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, APP_INITIALIZER } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
import { TopPageComponent } from './top-page/top-page.component';
import { ApplicationSettingsService } from './application-settings.service';
import { ProductRankingComponent } from './top-page/product-ranking/product-ranking.component';
import { ProductCellComponent } from './top-page/product-ranking/product-cell/product-cell.component';
import { ProductRankingBlockComponent } from './top-page/product-ranking/product-ranking-block/product-ranking-block.component';
import { ProductMergedCellComponent } from './top-page/product-ranking/product-merged-cell/product-merged-cell.component';
...
@NgModule({
declarations: [
AppComponent,
TopPageComponent,
ProductRankingComponent,
ProductCellComponent,
ProductRankingBlockComponent,
ProductMergedCellComponent,
ProductCellDirective
],
...
bootstrap: [AppComponent],
entryComponents: [ProductCellComponent, ProductMergedCellComponent],
})
export class AppModule { }
And I added Directive to create entry point.
ng g directive product-cell
product-cell.directive.ts
import { Directive, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[product-cell]'
})
export class ProductCellDirective {
constructor(public viewContainerRef: ViewContainerRef) { }
}
For loading the component and set data, I used a class.
product-cell-item.ts
import {Type} from '@angular/core';
import {CellComponent} from './product-ranking/cell.component';
import {Product} from '../models/product';
import {ProductCategory} from '../models/product-category';
export class ProductCellItem {
constructor(public component: Type<CellComponent>, public products: Product[], public category: ProductCategory) {}
}
I created the service for creating "ProductCellItem".
ng g s top-page/product-ranking/product-cell
product-cell.service.ts
import {Injectable} from '@angular/core';
import {ProductCellItem} from '../product-cell-item';
import {ProductCellComponent} from './product-cell/product-cell.component';
import {Product} from '../../models/product';
import {ProductCategory} from '../../models/product-category';
import {ProductMergedCellComponent} from './product-merged-cell/product-merged-cell.component';
/** set product and category for generating ProductCellItem */
interface ProductItem {
product: Product;
category: ProductCategory;
}
@Injectable({
providedIn: 'root'
})
export class ProductCellService {
constructor() { }
public generateProductCellItems(products: Product[], categories: ProductCategory[]): ProductCellItem[] {
const cellItems = new Array<ProductCellItem>();
const rankingNumbers = products.map(p => p.rankingNo);
const rankingFrom = Math.min.apply(null, rankingNumbers);
const rankingTo = Math.max.apply(null, rankingNumbers);
for (let i = rankingFrom; i <= rankingTo; i++) {
// group the products by ranking numbers.
const sameRankingProducts = products.filter(p => p.rankingNo === i);
if (sameRankingProducts.length <= 0) {
continue;
}
// group the products by categories.
const eBookProducts = new Array<ProductItem>();
const otherProducts = new Array<ProductItem>();
for (const product of sameRankingProducts) {
if (product.categoryId === 2) {
eBookProducts.push({product, category: this.getCategory(product.categoryId, categories)});
} else {
otherProducts.push({product, category: this.getCategory(product.categoryId, categories)});
}
}
if (eBookProducts.length > 0) {
cellItems.push(...this.generateMergedCellItems(eBookProducts));
}
if (otherProducts.length > 0) {
cellItems.push(...this.generateDefaultCellItems(otherProducts));
}
}
return cellItems;
}
/** find category */
private getCategory(targetId: number, categories: ProductCategory[]): ProductCategory {
for (const c of categories) {
if (c.id === targetId) {
return c;
}
}
}
/** generate cells for any categories exclude e-book */
private generateDefaultCellItems(products: ProductItem[]): Array<ProductCellItem> {
return products.map(p => new ProductCellItem(ProductCellComponent, [p.product], p.category));
}
/** generate cell for e-book */
private generateMergedCellItems(products: ProductItem[]): Array<ProductCellItem> {
const category = products[0].category;
return [new ProductCellItem(ProductMergedCellComponent, products.map(p => p.product), category)];
}
}
Generating ProductCellItems.
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';
...
export class ProductRankingComponent implements OnInit {
rankingFirst: ProductCellItem[];
constructor(private cellService: ProductCellService) {
// create ranking data and set ProductCellItems.
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'}
]
);
}
...
product-ranking.component.html
<div id="product-ranking-area">
<div id="product-ranking-frame">
<div id="product-ranking-group">
<app-product-ranking-block [cells]="rankingFirst"></app-product-ranking-block>
</div>
</div>
</div>
Loading components dynamically
For using selector what had been declared by product-cell.directive.ts, I used the "ng-template" element.product-ranking-block.component.html
<div class="product-ranking-block">
...
<ng-template product-cell></ng-template>
</div>
product-ranking-block.component.ts
import {Component, OnInit, Input, ComponentFactoryResolver, ViewChild} 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 {
@Input() cells: ProductCellItem[];
@ViewChild(ProductCellDirective, {static: true}) cellHost: ProductCellDirective;
constructor(private componentFactoryResolver: ComponentFactoryResolver) {
}
ngOnInit() {
this.load();
}
public load() {
const viewContainerRef = this.cellHost.viewContainerRef;
// clear the components what were added last time.
viewContainerRef.clear();
for (const cell of this.cells) {
// create a component from component's type.
const factory = this.componentFactoryResolver.resolveComponentFactory(cell.component);
const componentRef = viewContainerRef.createComponent(factory);
// get instance and set data.
const component = componentRef.instance as CellComponent;
component.products = cell.products;
component.category = cell.category;
}
}
}
Finally, I could load the components:)
コメント
コメントを投稿