mirror of
https://github.com/Raghu-Ch/angular-challenges.git
synced 2026-02-10 12:53:03 -05:00
challenge 4 complete
This commit is contained in:
@@ -17,11 +17,12 @@ This goal of this project is to help you get better at Angular and NgRx buy reso
|
|||||||
<img src="https://img.shields.io/badge/Angular--red?logo=angular" alt="Angular"/>
|
<img src="https://img.shields.io/badge/Angular--red?logo=angular" alt="Angular"/>
|
||||||
|
|
||||||
<a href="./apps/projection/README.md"><img src="https://img.shields.io/badge/1-Projection-red" alt="Projection"/></a>
|
<a href="./apps/projection/README.md"><img src="https://img.shields.io/badge/1-Projection-red" alt="Projection"/></a>
|
||||||
<a href="./apps/ngfor-enhancement/README.md"><img src="https://img.shields.io/badge/2-Directive enhancement-red" alt="Directive enhancemen"/></a>
|
<a href="./apps/ngfor-enhancement/README.md"><img src="https://img.shields.io/badge/3-Directive enhancement-red" alt="Directive enhancement"/></a>
|
||||||
|
<a href="./apps/context-outlet-type/README.md"><img src="https://img.shields.io/badge/4-ContextOutlet Typed-red" alt="Directive enhancemen"/></a>
|
||||||
|
|
||||||
<img src="https://img.shields.io/badge/NgRx--blueviolet" alt="NgRx"/>
|
<img src="https://img.shields.io/badge/NgRx--blueviolet" alt="NgRx"/>
|
||||||
|
|
||||||
<a href="./apps/ngrx-1/README.md"><img src="https://img.shields.io/badge/1-effect vs selector-blueviolet" alt="Effect vs Selector"/></a>
|
<a href="./apps/ngrx-1/README.md"><img src="https://img.shields.io/badge/2-Effect vs Selector-blueviolet" alt="Effect vs Selector"/></a>
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
45
apps/context-outlet-type/README.md
Normal file
45
apps/context-outlet-type/README.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<h1>NgTemplateOutlet Strongly Typed</h1>
|
||||||
|
|
||||||
|
## Information
|
||||||
|
|
||||||
|
Angular offer the static function **ngTemplateContextGuard** to strongly type structural directive.
|
||||||
|
|
||||||
|
However the context of **NgTemplateOutlet** type is **Object**. But which the help of the above guard, we can improve that behavior.
|
||||||
|
|
||||||
|
## Statement
|
||||||
|
|
||||||
|
In this exercice, we want to learn how to strongly typed our ng-template in our AppComponent.
|
||||||
|
|
||||||
|
This exercice has two level of complexity.
|
||||||
|
|
||||||
|
### Level 1: known Interface
|
||||||
|
|
||||||
|
Currently we have the following piece of code.
|
||||||
|
|
||||||
|
<img src="./img/unknown-person.png" height=120px alt="Unkown Person"/>
|
||||||
|
|
||||||
|
As we can see, name is of type "any". We want to infer the correct type.
|
||||||
|
|
||||||
|
### Level 2: generic Interface
|
||||||
|
|
||||||
|
Currently we have the following piece of code.
|
||||||
|
|
||||||
|
<img src="./img/unknown-student.png" height=120px alt="Unkown Person"/>
|
||||||
|
|
||||||
|
As we can see, student is of type "any". We want to infer the correct type.
|
||||||
|
|
||||||
|
But in this part, we can pass to ListComponent, a list of any types. And we still want the correct type to be infered.
|
||||||
|
|
||||||
|
## Submitting your work
|
||||||
|
|
||||||
|
1. Fork the project
|
||||||
|
2. clone it
|
||||||
|
3. npm install
|
||||||
|
4. **nx serve context-outlet-type**
|
||||||
|
5. _...work On it_
|
||||||
|
6. Commit your work
|
||||||
|
7. Submit a PR with a title beginning with **Answer:4** that I will review and other dev can review.
|
||||||
|
|
||||||
|
<a href="https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A4+label%3Aanswer" target="_blank"><img src="https://img.shields.io/badge/-Solutions-green" alt="NgTemplateOutlet Strongly Typed"/></a>
|
||||||
|
|
||||||
|
_You can ask any question on_ <a href="https://twitter.com/laforge_toma" target="_blank"><img src="./../../logo/twitter.svg" height=20px alt="Twitter"/></a>
|
||||||
BIN
apps/context-outlet-type/img/unknown-person.png
Normal file
BIN
apps/context-outlet-type/img/unknown-person.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
BIN
apps/context-outlet-type/img/unknown-student.png
Normal file
BIN
apps/context-outlet-type/img/unknown-student.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 45 KiB |
@@ -1,35 +1,29 @@
|
|||||||
import { NgTemplateOutlet } from '@angular/common';
|
import { NgTemplateOutlet } from '@angular/common';
|
||||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||||
import { ListComponent, ListTemplateDirective } from './list.component';
|
import { ListComponent } from './list.component';
|
||||||
import { PersonComponent, PersonDirective } from './person.component';
|
import { PersonComponent } from './person.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [
|
imports: [NgTemplateOutlet, PersonComponent, ListComponent],
|
||||||
NgTemplateOutlet,
|
|
||||||
PersonDirective,
|
|
||||||
PersonComponent,
|
|
||||||
ListTemplateDirective,
|
|
||||||
ListComponent,
|
|
||||||
],
|
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
template: `
|
template: `
|
||||||
<person [person]="person">
|
<person [person]="person">
|
||||||
<ng-template person let-name let-age="age">
|
<ng-template #personRef let-name let-age="age">
|
||||||
{{ name }}: {{ age }}
|
{{ name }}: {{ age }}
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</person>
|
</person>
|
||||||
|
|
||||||
<list [list]="students">
|
<list [list]="students">
|
||||||
<ng-container *appList="students as student; index as i">
|
<ng-template #listRef let-student let-i="index">
|
||||||
{{ student.name }}: {{ student.age }} - {{ i }}
|
{{ student.name }}: {{ student.age }} - {{ i }}
|
||||||
</ng-container>
|
</ng-template>
|
||||||
</list>
|
</list>
|
||||||
|
|
||||||
<list [list]="cities">
|
<list [list]="cities">
|
||||||
<ng-container *appList="cities as city; index as i">
|
<ng-template #listRef let-city let-i="index">
|
||||||
{{ city.name }}: {{ city.country }} - {{ i }}
|
{{ city.name }}: {{ city.country }} - {{ i }}
|
||||||
</ng-container>
|
</ng-template>
|
||||||
</list>
|
</list>
|
||||||
`,
|
`,
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
|||||||
@@ -3,32 +3,10 @@ import {
|
|||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
Component,
|
Component,
|
||||||
ContentChild,
|
ContentChild,
|
||||||
Directive,
|
|
||||||
Input,
|
Input,
|
||||||
TemplateRef,
|
TemplateRef,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
interface ListTemplateContext<TItem extends object> {
|
|
||||||
$implicit: TItem;
|
|
||||||
appList: TItem;
|
|
||||||
index: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Directive({
|
|
||||||
selector: 'ng-template[appList]',
|
|
||||||
standalone: true,
|
|
||||||
})
|
|
||||||
export class ListTemplateDirective<TItem extends object> {
|
|
||||||
@Input('appList') data!: TItem[];
|
|
||||||
|
|
||||||
static ngTemplateContextGuard<TContextItem extends object>(
|
|
||||||
dir: ListTemplateDirective<TContextItem>,
|
|
||||||
ctx: unknown
|
|
||||||
): ctx is ListTemplateContext<TContextItem> {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'list',
|
selector: 'list',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
@@ -50,6 +28,6 @@ export class ListTemplateDirective<TItem extends object> {
|
|||||||
export class ListComponent<TItem extends object> {
|
export class ListComponent<TItem extends object> {
|
||||||
@Input() list!: TItem[];
|
@Input() list!: TItem[];
|
||||||
|
|
||||||
@ContentChild(ListTemplateDirective, { read: TemplateRef })
|
@ContentChild('listRef', { read: TemplateRef })
|
||||||
listTemplateRef!: TemplateRef<TItem[]>;
|
listTemplateRef!: TemplateRef<TItem[]>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,11 @@
|
|||||||
import { NgTemplateOutlet } from '@angular/common';
|
import { NgTemplateOutlet } from '@angular/common';
|
||||||
import {
|
import { Component, ContentChild, Input, TemplateRef } from '@angular/core';
|
||||||
Component,
|
|
||||||
ContentChild,
|
|
||||||
Directive,
|
|
||||||
Input,
|
|
||||||
TemplateRef,
|
|
||||||
} from '@angular/core';
|
|
||||||
|
|
||||||
interface Person {
|
interface Person {
|
||||||
name: string;
|
name: string;
|
||||||
age: number;
|
age: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PersonContext {
|
|
||||||
$implicit: string;
|
|
||||||
age: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Directive({
|
|
||||||
selector: 'ng-template[person]',
|
|
||||||
standalone: true,
|
|
||||||
})
|
|
||||||
export class PersonDirective {
|
|
||||||
static ngTemplateContextGuard(
|
|
||||||
dir: PersonDirective,
|
|
||||||
ctx: unknown
|
|
||||||
): ctx is PersonContext {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [NgTemplateOutlet],
|
imports: [NgTemplateOutlet],
|
||||||
@@ -47,6 +23,6 @@ export class PersonDirective {
|
|||||||
export class PersonComponent {
|
export class PersonComponent {
|
||||||
@Input() person!: Person;
|
@Input() person!: Person;
|
||||||
|
|
||||||
@ContentChild(PersonDirective, { read: TemplateRef })
|
@ContentChild('#personRef', { read: TemplateRef })
|
||||||
personTemplateRef!: TemplateRef<PersonContext>;
|
personTemplateRef!: TemplateRef<unknown>;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user