Skip to content

Commit

Permalink
feat(widget): widget module. A widget is a specialez version of a dyn…
Browse files Browse the repository at this point in the history
…amic component
  • Loading branch information
cbourget authored and mbarbeau committed Feb 20, 2019
1 parent 3b4f1d9 commit 3da3e34
Show file tree
Hide file tree
Showing 18 changed files with 320 additions and 2 deletions.
1 change: 1 addition & 0 deletions demo/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ <h1>{{title}}</h1>
<hr>

<a mat-list-item routerLink="dynamic-component">Dynamic Component</a>
<a mat-list-item routerLink="widget">Widget</a>
<a mat-list-item routerLink="entity-table">Entity Table</a>
<a mat-list-item routerLink="form">Form</a>

Expand Down
2 changes: 2 additions & 0 deletions demo/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { AppMessageModule } from './core/message/message.module';
import { AppRequestModule } from './core/request/request.module';

import { AppDynamicComponentModule } from './common/dynamic-component/dynamic-component.module';
import { AppWidgetModule } from './common/widget/widget.module';
import { AppEntityTableModule } from './common/entity-table/entity-table.module';
import { AppFormModule } from './common/form/form.module';

Expand Down Expand Up @@ -61,6 +62,7 @@ import { AppComponent } from './app.component';
AppRequestModule,

AppDynamicComponentModule,
AppWidgetModule,
AppEntityTableModule,
AppFormModule,

Expand Down
15 changes: 15 additions & 0 deletions demo/src/app/common/widget/widget-routing.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Routes, RouterModule } from '@angular/router';
import { ModuleWithProviders } from '@angular/core';

import { AppWidgetComponent } from './widget.component';

const routes: Routes = [
{
path: 'widget',
component: AppWidgetComponent
}
];

export const AppWidgetRoutingModule: ModuleWithProviders = RouterModule.forChild(
routes
);
17 changes: 17 additions & 0 deletions demo/src/app/common/widget/widget.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<mat-card>
<mat-card-subtitle>Common</mat-card-subtitle>
<mat-card-title>Widget</mat-card-title>
<mat-card-content>
See the <a href="https://github.com/infra-geo-ouverte/igo2-lib/tree/master/demo/src/app/common/widget">code of this example</a>
</mat-card-content>

<igo-widget-outlet
[widget]="widget"
[inputs]="inputs"
(complete)="onWidgetComplete($event)"
(cancel)="onWidgetCancel($event)">
</igo-widget-outlet>

</mat-card>


12 changes: 12 additions & 0 deletions demo/src/app/common/widget/widget.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
pre,
code {
font-family: monospace, monospace;
}
pre {
overflow: auto;
}
pre > code {
display: block;
padding: 1rem;
word-wrap: normal;
}
59 changes: 59 additions & 0 deletions demo/src/app/common/widget/widget.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {
Component,
Input,
Output,
EventEmitter,
ChangeDetectionStrategy,
ChangeDetectorRef
} from '@angular/core';

import { DynamicComponent, OnUpdateInputs, WidgetComponent, WidgetService } from '@igo2/common';


@Component({
selector: 'app-salutation-widget',
template: `
<p>Hello, my name is {{name}}.</p>
<button mat-flat-button (click)="complete.emit(name)">Nice to meet you</button>
<button mat-flat-button (click)="cancel.emit(name)">Dismiss</button>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppSalutationWidgetComponent implements OnUpdateInputs, WidgetComponent {

@Input() name: string;

@Output() complete = new EventEmitter<string>();

@Output() cancel = new EventEmitter<void>();

constructor(private cdRef: ChangeDetectorRef) {}

onUpdateInputs() {
this.cdRef.detectChanges();
}

}

@Component({
selector: 'app-widget',
templateUrl: './widget.component.html',
styleUrls: ['./widget.component.scss']
})
export class AppWidgetComponent {

widget: DynamicComponent<WidgetComponent> = this.widgetService.create(AppSalutationWidgetComponent);

inputs = {name: 'Bob'};

constructor(private widgetService: WidgetService) {}

onWidgetComplete(name: string) {
alert(`${name} emitted event 'complete' then got automatically destroyed.`);
}

onWidgetCancel() {
alert(`Widget emitted event 'cancel' then got automatically destroyed.`);
}

}
28 changes: 28 additions & 0 deletions demo/src/app/common/widget/widget.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { NgModule } from '@angular/core';
import { MatButtonModule, MatCardModule } from '@angular/material';

import { IgoWidgetModule } from '@igo2/common';

import {
AppSalutationWidgetComponent,
AppWidgetComponent
} from './widget.component';
import { AppWidgetRoutingModule } from './widget-routing.module';

@NgModule({
declarations: [
AppSalutationWidgetComponent,
AppWidgetComponent
],
imports: [
AppWidgetRoutingModule,
MatButtonModule,
MatCardModule,
IgoWidgetModule
],
exports: [AppWidgetComponent],
entryComponents: [
AppSalutationWidgetComponent
]
})
export class AppWidgetModule {}
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import {
ComponentFactory,
ComponentRef,
ViewContainerRef,
SimpleChange
ViewContainerRef
} from '@angular/core';

import { Subscription } from 'rxjs';
Expand Down
1 change: 1 addition & 0 deletions projects/common/src/lib/widget/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './shared';
2 changes: 2 additions & 0 deletions projects/common/src/lib/widget/shared/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './widget.interfaces';
export * from './widget.service';
13 changes: 13 additions & 0 deletions projects/common/src/lib/widget/shared/widget.interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { EventEmitter } from '@angular/core';

/**
* This is the interface a widget component needs to implement. A widget
* component is component that can be created dynamically. It needs
* to emit the 'complete' and 'cancel' event at some time in it's lifecycle.
* Since a widget's inputs are set manually, you may want to implement the 'onUpdateInputs'
* method. This method could, for example, trigger the change detection.
*/
export interface WidgetComponent {
complete: EventEmitter<any>;
cancel: EventEmitter<any>;
}
19 changes: 19 additions & 0 deletions projects/common/src/lib/widget/shared/widget.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {
Injectable
} from '@angular/core';

import { DynamicComponent, DynamicComponentService } from '../../dynamic-component';

import { WidgetComponent } from './widget.interfaces';

@Injectable({
providedIn: 'root'
})
export class WidgetService {

constructor(private dynamicComponentService: DynamicComponentService) {}

create(widgetCls: any): DynamicComponent<WidgetComponent> {
return this.dynamicComponentService.create(widgetCls as WidgetComponent);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<igo-dynamic-outlet
*ngIf="widget"
[component]="widget"
[inputs]="inputs"
[subscribers]="subscribers">
</igo-dynamic-outlet>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
igo-dynamic-outlet {
height: 100%;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import {
Component,
Input,
Output,
EventEmitter,
ChangeDetectionStrategy,
OnDestroy
} from '@angular/core';

import { DynamicComponent } from '../../dynamic-component';

import { WidgetComponent } from '../shared/widget.interfaces';

/**
* This component dynamically renders a widget. It also subscribes
* to the widget's 'cancel' and 'complete' events and destroys it
* when any of those event is emitted.
*/
@Component({
selector: 'igo-widget-outlet',
templateUrl: './widget-outlet.component.html',
styleUrls: ['./widget-outlet.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class WidgetOutletComponent implements OnDestroy {

/**
* Widget subscribers to 'cancel' and 'complete'
* @internal
*/
readonly subscribers = {
cancel: (event: any) => this.onCancel(event),
complete: (event: any) => this.onComplete(event)
};

/**
* Widget
*/
@Input() widget: DynamicComponent<WidgetComponent>;

/**
* Widget inputs
*/
@Input() inputs: {[key: string]: any};

/**
* Event emitted when the widget emits 'complete'
*/
@Output() complete = new EventEmitter<any>();

/**
* Event emitted when the widget emits 'cancel'
*/
@Output() cancel = new EventEmitter<any>();

constructor() {}

/**
* Destroy the current widget and all it's inner subscriptions
* @internal
*/
ngOnDestroy() {
this.destroyWidget();
}

/**
* When the widget emits 'cancel', propagate that event and destroy
* the widget
*/
private onCancel(event: any) {
this.cancel.emit(event);
this.destroyWidget();
}

/**
* When the widget emits 'complete', propagate that event and destroy
* the widget
*/
private onComplete(event: any) {
this.complete.emit(event);
this.destroyWidget();
}

/**
* Destroy the current widget
*/
private destroyWidget() {
if (this.widget !== undefined) {
this.widget.destroy();
}
this.widget = undefined;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import {
IgoDynamicComponentModule
} from '../../dynamic-component/dynamic-component.module';

import { WidgetOutletComponent } from './widget-outlet.component';

/**
* @ignore
*/
@NgModule({
imports: [
CommonModule,
IgoDynamicComponentModule
],
exports: [
WidgetOutletComponent
],
declarations: [
WidgetOutletComponent
]
})
export class IgoWidgetOutletModule {}
20 changes: 20 additions & 0 deletions projects/common/src/lib/widget/widget.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { IgoWidgetOutletModule } from './widget-outlet/widget-outlet.module';
import { WidgetService } from './shared/widget.service';

@NgModule({
imports: [
CommonModule,
IgoWidgetOutletModule
],
exports: [
IgoWidgetOutletModule
],
declarations: [],
providers: [
WidgetService
]
})
export class IgoWidgetModule {}
3 changes: 3 additions & 0 deletions projects/common/src/public_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export * from './lib/sidenav/sidenav.module';
export * from './lib/spinner/spinner.module';
export * from './lib/stop-propagation/stop-propagation.module';
export * from './lib/table/table.module';
export * from './lib/widget/widget.module';
export * from './lib/widget/widget-outlet/widget-outlet.module';

export * from './lib/backdrop';
export * from './lib/clickout';
Expand All @@ -48,3 +50,4 @@ export * from './lib/sidenav';
export * from './lib/spinner';
export * from './lib/stop-propagation';
export * from './lib/table';
export * from './lib/widget';

0 comments on commit 3da3e34

Please sign in to comment.