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"/>
|
||||
|
||||
<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"/>
|
||||
|
||||
<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
|
||||
|
||||
|
||||
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 { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { ListComponent, ListTemplateDirective } from './list.component';
|
||||
import { PersonComponent, PersonDirective } from './person.component';
|
||||
import { ListComponent } from './list.component';
|
||||
import { PersonComponent } from './person.component';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [
|
||||
NgTemplateOutlet,
|
||||
PersonDirective,
|
||||
PersonComponent,
|
||||
ListTemplateDirective,
|
||||
ListComponent,
|
||||
],
|
||||
imports: [NgTemplateOutlet, PersonComponent, ListComponent],
|
||||
selector: 'app-root',
|
||||
template: `
|
||||
<person [person]="person">
|
||||
<ng-template person let-name let-age="age">
|
||||
<ng-template #personRef let-name let-age="age">
|
||||
{{ name }}: {{ age }}
|
||||
</ng-template>
|
||||
</person>
|
||||
|
||||
<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 }}
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
</list>
|
||||
|
||||
<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 }}
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
</list>
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
|
||||
@@ -3,32 +3,10 @@ import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
ContentChild,
|
||||
Directive,
|
||||
Input,
|
||||
TemplateRef,
|
||||
} 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({
|
||||
selector: 'list',
|
||||
standalone: true,
|
||||
@@ -50,6 +28,6 @@ export class ListTemplateDirective<TItem extends object> {
|
||||
export class ListComponent<TItem extends object> {
|
||||
@Input() list!: TItem[];
|
||||
|
||||
@ContentChild(ListTemplateDirective, { read: TemplateRef })
|
||||
@ContentChild('listRef', { read: TemplateRef })
|
||||
listTemplateRef!: TemplateRef<TItem[]>;
|
||||
}
|
||||
|
||||
@@ -1,35 +1,11 @@
|
||||
import { NgTemplateOutlet } from '@angular/common';
|
||||
import {
|
||||
Component,
|
||||
ContentChild,
|
||||
Directive,
|
||||
Input,
|
||||
TemplateRef,
|
||||
} from '@angular/core';
|
||||
import { Component, ContentChild, Input, TemplateRef } from '@angular/core';
|
||||
|
||||
interface Person {
|
||||
name: string;
|
||||
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({
|
||||
standalone: true,
|
||||
imports: [NgTemplateOutlet],
|
||||
@@ -47,6 +23,6 @@ export class PersonDirective {
|
||||
export class PersonComponent {
|
||||
@Input() person!: Person;
|
||||
|
||||
@ContentChild(PersonDirective, { read: TemplateRef })
|
||||
personTemplateRef!: TemplateRef<PersonContext>;
|
||||
@ContentChild('#personRef', { read: TemplateRef })
|
||||
personTemplateRef!: TemplateRef<unknown>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user