Skip to content

Commit

Permalink
Add directive
Browse files Browse the repository at this point in the history
  • Loading branch information
nigrosimone committed Aug 23, 2022
1 parent 2c753b1 commit f2a93fc
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 6 deletions.
37 changes: 35 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# NgAs [![Build Status](https://app.travis-ci.com/nigrosimone/ng-as.svg?branch=main)](https://app.travis-ci.com/nigrosimone/ng-as) [![Coverage Status](https://coveralls.io/repos/github/nigrosimone/ng-as/badge.svg?branch=main)](https://coveralls.io/github/nigrosimone/ng-as?branch=main) [![NPM version](https://img.shields.io/npm/v/ng-as.svg)](https://www.npmjs.com/package/ng-as)

Angular pipe for type casting template variables.
Angular pipe and directive for type casting template variables.

## Description

Expand Down Expand Up @@ -42,7 +42,40 @@ import { NgAsModule } from 'ng-as';
export class AppModule { }
```

*Step 3*: usage, eg.:
*Step 3*: usage:

The directirve eg.:

```ts
import { Component } from '@angular/core';

// your interface, but also work with any typescript type (class, type, etc.)
interface Person {
name: string;
}

@Component({
selector: 'app-root',
template: `
<ng-container *ngTemplateOutlet="personTemplate; context: {$implicit: person}"></ng-container>
<ng-template #personTemplate [ngAs]="Person" let-person>
<span>Hello {{ person.name }}!</span>
</ng-template>
`,
})
export class AppComponent {

// NOTE: If you have "strictPropertyInitialization" enabled,
// you will need to add a non-null assertion (!)
public Person!: Person; // publish your interface into html template

person: Person = { name: 'Simone' }; // the data

}
```

The pipe eg.:

```ts
import { Component } from '@angular/core';
Expand Down
5 changes: 5 additions & 0 deletions projects/ng-as-demo/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ interface Person {
template: `
<ng-container *ngTemplateOutlet="personTemplate1; context: {$implicit: person}"></ng-container>
<ng-container *ngTemplateOutlet="personTemplate2; context: {$implicit: person}"></ng-container>
<ng-container *ngTemplateOutlet="personTemplate3; context: {$implicit: person}"></ng-container>
<ng-template #personTemplate1 let-person>
<p>Hello {{ (person | as: Person).name }}!</p>
Expand All @@ -20,6 +21,10 @@ interface Person {
<ng-template #personTemplate2 let-person>
<p>Hello {{ $ngAs(person, Person).name }}!</p>
</ng-template>
<ng-template #personTemplate3 [ngAs]="Person" let-person>
<p>Hello {{ person.name }}!</p>
</ng-template>
`,
})
export class AppComponent {
Expand Down
34 changes: 34 additions & 0 deletions projects/ng-as/src/lib/ng-as.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Directive, Input } from "@angular/core";

interface NgAsContext<T> {
ngLet: T;
$implicit: T;
}

@Directive({ selector: '[ngAs]' })
export class NgAsDirective<T> {
@Input() ngAs!: T;

/** @internal */
public static ngLetUseIfTypeGuard: void;

/**
* Assert the correct type of the expression bound to the `NgLet` input within the template.
*
* The presence of this static field is a signal to the Ivy template type check compiler that
* when the `NgLet` structural directive renders its template, the type of the expression bound
* to `NgLet` should be narrowed in some way. For `NgLet`, the binding expression itself is used to
* narrow its type, which allows the strictNullChecks feature of TypeScript to work with `NgLet`.
*/
static ngTemplateGuard_ngLet: 'binding';

/**
* Asserts the correct type of the context for the template that `NgLet` will render.
*
* The presence of this method is a signal to the Ivy template type-check compiler that the
* `NgLet` structural directive renders its template with a specific context type.
*/
static ngTemplateContextGuard<T>(dir: NgAsDirective<T>, ctx: any): ctx is NgAsContext<Exclude<T, false | 0 | '' | null | undefined>> {
return true;
}
}
5 changes: 3 additions & 2 deletions projects/ng-as/src/lib/ng-as.module.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { NgModule } from '@angular/core';
import { NgAsDirective } from './ng-as.directive';
import { NgAsPipe } from './ng-as.pipe';

@NgModule({
declarations: [NgAsPipe],
declarations: [NgAsPipe, NgAsDirective],
imports: [],
exports: [NgAsPipe],
exports: [NgAsPipe, NgAsDirective],
providers: []
})
export class NgAsModule { }
51 changes: 49 additions & 2 deletions projects/ng-as/src/lib/ng-as.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@

import { CommonModule } from '@angular/common';
import { Component, DebugElement } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NgAsDirective } from './ng-as.directive';
import { NgAsModule } from './ng-as.module';
import { NgAsPipe } from './ng-as.pipe';
import { ngAs } from './ng-as.utils';
Expand All @@ -11,8 +15,51 @@ describe('NgAsPipe', () => {
it('transform', () => {
let Test!: Test;
const pipe = new NgAsPipe();
expect(pipe.transform({x: true}, Test)).toEqual({x: true});
expect(ngAs({x: true}, Test)).toEqual({x: true});
expect(pipe.transform({ x: true }, Test)).toEqual({ x: true });
});
});

describe('NgAsMethod', () => {
it('ngAs', () => {
let Test!: Test;
expect(ngAs({ x: true }, Test)).toEqual({ x: true });
});
});

@Component({
template: `<ng-container *ngTemplateOutlet="testTemplate; context: {$implicit: test}"></ng-container>
<ng-template #testTemplate [ngAs]="Test" let-test><div>{{test.x}}</div></ng-template>`
})
class TestComponent {
public Test!: Test;
test: Test = { x: true };
}
describe('NgAsDirective', () => {
let fixture: ComponentFixture<TestComponent>;
let debugElement: DebugElement;
let element: HTMLElement;

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [TestComponent],
imports: [CommonModule, NgAsModule]
});
fixture = TestBed.createComponent(TestComponent);
debugElement = fixture.debugElement;
element = debugElement.nativeElement;
});

afterEach(() => {
document.body.removeChild(element);
});

it('test', () => {
fixture.detectChanges();
expect(element.textContent).toBe('true');
});

it('ngTemplateContextGuard', () => {
expect(NgAsDirective.ngTemplateContextGuard(null as any, null)).toBeTrue();
});
});

Expand Down
1 change: 1 addition & 0 deletions projects/ng-as/src/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
*/
export * from './lib/ng-as.module';
export * from './lib/ng-as.pipe';
export * from './lib/ng-as.directive';
export * from './lib/ng-as.utils';

0 comments on commit f2a93fc

Please sign in to comment.