Skip to content
This repository has been archived by the owner on Dec 10, 2022. It is now read-only.

Commit

Permalink
Merge pull request #2 from gund/fix-aot
Browse files Browse the repository at this point in the history
[Fix] AOT + Add decorators
  • Loading branch information
gund committed Jan 24, 2018
2 parents 2a3e6a8 + ce340da commit 0cebc3d
Show file tree
Hide file tree
Showing 34 changed files with 725 additions and 148 deletions.
4 changes: 3 additions & 1 deletion .codeclimate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ engines:
ratings:
paths:
- "src/**/*.ts"
exclude_paths: []
exclude_paths: [
"src/**/*.spec.ts"
]
30 changes: 18 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,14 @@ $ npm install --save ng-preset
First decide which parts of your component you want to make extendable
via presets (suppose it's header, content and footer).

Once decided - create an interface that will represent this
Once decided - create an abstract class that will represent this
requirement as a preset:

```ts
// my-preset.ts
export interface MyPreset {
import { PresetType } from 'ng-preset';

export abstract class MyPreset extends PresetType {
headerTpl: TemplateRef<any>;
contentTpl: TemplateRef<any>;
footerTpl: TemplateRef<any>;
Expand All @@ -62,6 +64,7 @@ export interface MyPreset {

_NOTE_: that each property you define should be of type `TemplateRef`,
because that is what will be stamped inside of your component.
And it is required to extend your class from `PresetType` for further validations.

You can also optionally provide some context information
that a template will receive once instantiated as a generic argument
Expand All @@ -73,18 +76,15 @@ Now in your component you can get preset component:

```ts
// my.component.ts
import { Preset } from 'ng-preset';
import { MyPreset } from './my-preset';

@Component({...})
export class MyComponent {
presetComp = this.presetService.getPreset<MyPreset>();
constructor(public presetService: PresetService) { }
@Preset() presetComp: MyPreset;
}
```

_NOTE_: You can annotate your preset type here by setting generic argument
while calling `PresetService.getPreset`.

### Stamp preset templates in your component

Once you get preset component you can now render it in your template:
Expand Down Expand Up @@ -124,13 +124,15 @@ import { MyPreset } from '../my-preset';
<ng-template #footerTpl>Footer default preset</ng-template>
`
})
export class MyPresetDefaultComponent implements MyPreset {
export class MyPresetDefaultComponent extends MyPreset {
@ViewChild('headerTpl') headerTpl: TemplateRef<any>;
@ViewChild('contentTpl') contentTpl: TemplateRef<any>;
@ViewChild('footerTpl') footerTpl: TemplateRef<any>;
}
```

_NOTE_: Make sure you extend your preset class so that it can ba validated later.

That is all you need to have in your preset component!

Now it's time to glue them together in the module.
Expand Down Expand Up @@ -164,14 +166,15 @@ already setup and ready to roll:
// my.module.ts
import { PresetDefaultModule } from 'ng-preset';

import { MyComponent } from './my.component';
import { MyComponentModule } from './my-component.module';
import { MyPresetDefaultComponent } from './my-preset-default.component';

@NgModule({
...
imports: [
MyComponentModule,
PresetDefaultModule.withPreset(MyPresetDefaultComponent),
PresetDefaultModule.forComponent(MyComponent, MyPresetDefaultComponent),
],
exports: [MyComponentModule],
declarations: [MyPresetDefaultComponent],
Expand All @@ -189,21 +192,24 @@ Create another module that will allow user to pass it's own preset component.

```ts
// my-custom.module.ts
import { PresetDefaultModule } from 'ng-preset';
import { PresetModule, providePresetFor } from 'ng-preset';

import { MyPreset } from './my-preset';
import { MyComponent } from './my.component';
import { MyComponentModule } from './my-component.module';

@NgModule({
...
imports: [
MyComponentModule,
PresetModule,
PresetModule.forComponent(MyComponent),
],
exports: [MyComponentModule],
})
export class MyCustomModule {
static withPreset = createWithPresetMethodFor<MyPreset>(MyCustomModule);
static withPreset(presetType: Type<MyPreset>): ModuleWithProviders {
return providePresetFor(MyCustomModule, presetType);
}
}
```

Expand Down
9 changes: 3 additions & 6 deletions src/demo/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,14 @@ import { MyCustomModule } from './my/my-custom.module';
import { MyModule } from './my/my.module';

@NgModule({
declarations: [
AppComponent,
MyPresetOneComponent,
],
declarations: [AppComponent, MyPresetOneComponent],
imports: [
BrowserModule,
MyModule, // This is normal use-case
// MyCustomModule.withPreset(MyPresetOneComponent), // Uncomment this
// to override default preset
],
providers: [],
bootstrap: [AppComponent]
bootstrap: [AppComponent],
})
export class AppModule { }
export class AppModule {}
4 changes: 2 additions & 2 deletions src/demo/my-preset-one/my-preset-one.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { MyPreset } from '../my/my-preset';
@Component({
selector: 'prst-my-preset-one',
templateUrl: './my-preset-one.component.html',
styleUrls: ['./my-preset-one.component.css']
styleUrls: ['./my-preset-one.component.css'],
})
export class MyPresetOneComponent implements MyPreset {
export class MyPresetOneComponent extends MyPreset {
@ViewChild('headerTpl') headerTpl: TemplateRef<any>;
@ViewChild('contentTpl') contentTpl: TemplateRef<any>;
@ViewChild('footerTpl') footerTpl: TemplateRef<any>;
Expand Down
14 changes: 7 additions & 7 deletions src/demo/my/my-custom.module.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { NgModule } from '@angular/core';
import { ModuleWithProviders, NgModule, Type } from '@angular/core';

import { createWithPresetMethodFor, PresetModule } from '../../preset';
import { PresetModule, providePresetFor } from '../../preset';
import { MyComponentModule } from './my-component.module';
import { MyPreset } from './my-preset';
import { MyComponent } from './my.component';

@NgModule({
imports: [
PresetModule,
MyComponentModule,
],
imports: [MyComponentModule, PresetModule.forComponent(MyComponent)],
exports: [MyComponentModule],
})
export class MyCustomModule {
static withPreset = createWithPresetMethodFor<MyPreset>(MyCustomModule);
static withPreset(presetType: Type<MyPreset>): ModuleWithProviders {
return providePresetFor(MyCustomModule, presetType);
}
}
4 changes: 2 additions & 2 deletions src/demo/my/my-preset-default/my-preset-default.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { MyPreset } from '../my-preset';
@Component({
selector: 'prst-my-preset-default',
templateUrl: './my-preset-default.component.html',
styleUrls: ['./my-preset-default.component.css']
styleUrls: ['./my-preset-default.component.css'],
})
export class MyPresetDefaultComponent implements MyPreset {
export class MyPresetDefaultComponent extends MyPreset {
@ViewChild('headerTpl') headerTpl: TemplateRef<any>;
@ViewChild('contentTpl') contentTpl: TemplateRef<any>;
@ViewChild('footerTpl') footerTpl: TemplateRef<any>;
Expand Down
4 changes: 3 additions & 1 deletion src/demo/my/my-preset.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { TemplateRef } from '@angular/core';

export interface MyPreset {
import { PresetType } from '../../preset';

export abstract class MyPreset extends PresetType {
headerTpl: TemplateRef<any>;
contentTpl: TemplateRef<any>;
footerTpl: TemplateRef<any>;
Expand Down
2 changes: 1 addition & 1 deletion src/demo/my/my.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ describe('MyComponent', () => {

beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [PresetDefaultModule.withPreset(MockPresetComponent)],
imports: [PresetDefaultModule.forComponent(MyComponent, MockPresetComponent)],
declarations: [MyComponent, MockPresetComponent],
})
.compileComponents();
Expand Down
12 changes: 3 additions & 9 deletions src/demo/my/my.component.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
import { Component } from '@angular/core';

import { PresetService } from '../../preset';
import { Preset } from '../../preset';
import { MyPreset } from './my-preset';

@Component({
selector: 'prst-my',
templateUrl: './my.component.html',
styleUrls: ['./my.component.css']
styleUrls: ['./my.component.css'],
})
export class MyComponent {

presetComp = this.presetService.getPreset<MyPreset>();

constructor(
private presetService: PresetService,
) { }

@Preset() presetComp: MyPreset;
}
4 changes: 3 additions & 1 deletion src/demo/my/my.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import { NgModule } from '@angular/core';
import { PresetDefaultModule } from '../../preset';
import { MyComponentModule } from './my-component.module';
import { MyPresetDefaultComponent } from './my-preset-default/my-preset-default.component';
import { MyComponent } from './my.component';

@NgModule({
imports: [
MyComponentModule,
PresetDefaultModule.withPreset(MyPresetDefaultComponent),
PresetDefaultModule
.forComponent(MyComponent, MyPresetDefaultComponent),
],
exports: [MyComponentModule],
declarations: [MyPresetDefaultComponent],
Expand Down
3 changes: 2 additions & 1 deletion src/preset/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { PresetType } from './preset-token';
export * from './preset-method';
export * from './provide';
export * from './metadata';
export * from './preset.service';
export * from './preset.module';
export * from './preset-default.module';
2 changes: 2 additions & 0 deletions src/preset/metadata/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './preset';
export * from './preset-decorator';
18 changes: 18 additions & 0 deletions src/preset/metadata/preset-decorator.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { PresetMetadata, setPresetMetadataOn } from './preset';
import { Preset } from './preset-decorator';

jest.mock('./preset');

describe('Preset decorator', () => {
it('should return prop decorator and call `setPresetMetadataOn` with target and metadata obj', () => {
const options = { __options: true };
const propName = 'propertyName';
const t = { constructor: {} } as any;
Preset(options)(t, propName);

expect(setPresetMetadataOn).toHaveBeenCalledWith(t, <PresetMetadata>{
propName,
options,
});
});
});
9 changes: 9 additions & 0 deletions src/preset/metadata/preset-decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { PresetMetadataOptions, setPresetMetadataOn } from './preset';

export function Preset(options?: PresetMetadataOptions): PropertyDecorator {
return (target, propName) =>
setPresetMetadataOn(target, {
propName,
options,
});
}
52 changes: 52 additions & 0 deletions src/preset/metadata/preset.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {
getPresetMetadataFrom,
PRESET_METADATA_KEY,
setPresetMetadataOn,
} from './preset';

describe('setPresetMetadataOn() function', () => {
it('should set metadata on type`s prototype under `PRESET_METADATA_KEY`', () => {
const metadata = { __medatadta: true };
const t = getFakeType();

setPresetMetadataOn(t, metadata as any);

expect(t[PRESET_METADATA_KEY]).toEqual([metadata]);
});

it('should set array of metadata on type`s prototype under `PRESET_METADATA_KEY`', () => {
const metadata = { __medatadta: true };
const metadata2 = { __medatadta2: true };
const t = getFakeType();

setPresetMetadataOn(t, [metadata, metadata2] as any);

expect(t[PRESET_METADATA_KEY]).toEqual([metadata, metadata2]);
});

it('should preserve previously set metadata on type`s prototype under `PRESET_METADATA_KEY`', () => {
const metadata = { __medatadta: true };
const metadata2 = { __medatadta2: true };
const t = getFakeType();

setPresetMetadataOn(t, metadata as any);
expect(t[PRESET_METADATA_KEY]).toEqual([metadata]);

setPresetMetadataOn(t, metadata2 as any);
expect(t[PRESET_METADATA_KEY]).toEqual([metadata, metadata2]);
});
});

describe('getPresetMetadataFrom() function', () => {
it('should return metadata from type under `PRESET_METADATA_KEY`', () => {
const metadata = { __metadata: true };
const t = getFakeType();
setPresetMetadataOn(t, metadata as any);

expect(getPresetMetadataFrom({ prototype: t })).toEqual([metadata]);
});
});

function getFakeType(): any {
return class {};
}
26 changes: 26 additions & 0 deletions src/preset/metadata/preset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export const PRESET_METADATA_KEY = '__Preset-Key__';

export interface PresetMetadata {
propName: string | symbol;
options?: PresetMetadataOptions;
}

// tslint:disable-next-line:no-empty-interface
export interface PresetMetadataOptions {}

export function setPresetMetadataOn(
type: any,
metadata: PresetMetadata | PresetMetadata[],
) {
if (!Array.isArray(metadata)) {
metadata = [metadata];
}

const existingMetadata = type[PRESET_METADATA_KEY] || [];
type[PRESET_METADATA_KEY] = [...existingMetadata, ...metadata];
}

export function getPresetMetadataFrom(type: any): PresetMetadata[] {
const proto = type.prototype || (type.prototype = {});
return proto[PRESET_METADATA_KEY] || [];
}
Loading

0 comments on commit 0cebc3d

Please sign in to comment.