#967 initial support for dynamic components (wip)

- form rendering service to control field-component type mappings
- special <form-field> component with dynamic content creation based on
mapped types
- migrated <text> component to dynamic creation
This commit is contained in:
Denys Vuika 2016-11-14 14:47:13 +00:00 committed by Mario Romano
parent c9e3723613
commit 4c95ed1f71
7 changed files with 140 additions and 4 deletions

View File

@ -19,12 +19,14 @@ import { NgModule, ModuleWithProviders } from '@angular/core';
import { CoreModule } from 'ng2-alfresco-core'; import { CoreModule } from 'ng2-alfresco-core';
import { ActivitiForm } from './src/components/activiti-form.component'; import { ActivitiForm } from './src/components/activiti-form.component';
import { FormFieldComponent } from './src/components/form-field/form-field.component';
import { ActivitiStartForm } from './src/components/activiti-start-form.component'; import { ActivitiStartForm } from './src/components/activiti-start-form.component';
import { FormService } from './src/services/form.service'; import { FormService } from './src/services/form.service';
import { EcmModelService } from './src/services/ecm-model.service'; import { EcmModelService } from './src/services/ecm-model.service';
import { NodeService } from './src/services/node.service'; import { NodeService } from './src/services/node.service';
import { WidgetVisibilityService } from './src/services/widget-visibility.service'; import { WidgetVisibilityService } from './src/services/widget-visibility.service';
import { ActivitiAlfrescoContentService } from './src/services/activiti-alfresco.service'; import { ActivitiAlfrescoContentService } from './src/services/activiti-alfresco.service';
import { FormRenderingService } from './src/services/form-rendering.service';
import { HttpModule } from '@angular/http'; import { HttpModule } from '@angular/http';
import { WIDGET_DIRECTIVES } from './src/components/widgets/index'; import { WIDGET_DIRECTIVES } from './src/components/widgets/index';
@ -38,6 +40,7 @@ export * from './src/services/node.service';
export const ACTIVITI_FORM_DIRECTIVES: any[] = [ export const ACTIVITI_FORM_DIRECTIVES: any[] = [
ActivitiForm, ActivitiForm,
ActivitiStartForm, ActivitiStartForm,
FormFieldComponent,
...WIDGET_DIRECTIVES ...WIDGET_DIRECTIVES
]; ];
@ -46,7 +49,8 @@ export const ACTIVITI_FORM_PROVIDERS: any[] = [
EcmModelService, EcmModelService,
NodeService, NodeService,
WidgetVisibilityService, WidgetVisibilityService,
ActivitiAlfrescoContentService ActivitiAlfrescoContentService,
FormRenderingService
]; ];
@NgModule({ @NgModule({
@ -57,6 +61,9 @@ export const ACTIVITI_FORM_PROVIDERS: any[] = [
declarations: [ declarations: [
...ACTIVITI_FORM_DIRECTIVES ...ACTIVITI_FORM_DIRECTIVES
], ],
entryComponents: [
...WIDGET_DIRECTIVES
],
providers: [ providers: [
...ACTIVITI_FORM_PROVIDERS ...ACTIVITI_FORM_PROVIDERS
], ],

View File

@ -20,6 +20,7 @@ import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { Observable } from 'rxjs/Rx'; import { Observable } from 'rxjs/Rx';
import { ActivitiStartForm } from './activiti-start-form.component'; import { ActivitiStartForm } from './activiti-start-form.component';
import { FormFieldComponent } from './form-field/form-field.component';
import { WIDGET_DIRECTIVES } from './widgets/index'; import { WIDGET_DIRECTIVES } from './widgets/index';
import { FormService } from './../services/form.service'; import { FormService } from './../services/form.service';
import { EcmModelService } from './../services/ecm-model.service'; import { EcmModelService } from './../services/ecm-model.service';
@ -43,6 +44,7 @@ describe('ActivitiStartForm', () => {
imports: [ CoreModule ], imports: [ CoreModule ],
declarations: [ declarations: [
ActivitiStartForm, ActivitiStartForm,
FormFieldComponent,
...WIDGET_DIRECTIVES ...WIDGET_DIRECTIVES
], ],
providers: [ providers: [

View File

@ -0,0 +1,68 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, OnInit, ViewChild, ViewContainerRef, Input, ComponentRef, ComponentFactoryResolver, Output, EventEmitter/*, Injector*/ } from '@angular/core';
import { WidgetVisibilityService } from './../../services/widget-visibility.service';
import { FormRenderingService } from './../../services/form-rendering.service';
import { WidgetComponent } from './../widgets/widget.component';
import { FormFieldModel/*, FormWidgetModel*/ } from './../widgets/core/index';
@Component({
selector: 'form-field',
template: `<div #container></div>`
})
export class FormFieldComponent implements OnInit {
@ViewChild('container', { read: ViewContainerRef })
container: ViewContainerRef;
@Input()
field: FormFieldModel = null;
/** @deprecated component handles visibilty itself */
@Output()
fieldChanged: EventEmitter<FormFieldModel> = new EventEmitter<FormFieldModel>();
private componentRef: ComponentRef<{}>;
constructor(
private formRenderingService: FormRenderingService,
private componentFactoryResolver: ComponentFactoryResolver,
private visibilityService: WidgetVisibilityService
/*,private injector: Injector*/) {
}
ngOnInit() {
if (this.field) {
let componentType = this.formRenderingService.getComponentType(this.field.type);
if (componentType) {
let factory = this.componentFactoryResolver.resolveComponentFactory(componentType);
this.componentRef = this.container.createComponent(factory/*, 0, this.injector*/);
let instance = <WidgetComponent>this.componentRef.instance;
instance.field = this.field;
instance.fieldChanged.subscribe(args => {
if (this.field && this.field.form) {
this.visibilityService.refreshVisibility(this.field.form);
}
/** @deprecated */
this.fieldChanged.emit(args);
});
}
}
}
}

View File

@ -20,7 +20,7 @@
<number-widget [field]="field" (fieldChanged)="fieldChanged($event);"></number-widget> <number-widget [field]="field" (fieldChanged)="fieldChanged($event);"></number-widget>
</div> </div>
<div *ngSwitchCase="'text'"> <div *ngSwitchCase="'text'">
<text-widget [field]="field" (fieldChanged)="fieldChanged($event);"></text-widget> <form-field [field]="field"></form-field>
</div> </div>
<div *ngSwitchCase="'multi-line-text'"> <div *ngSwitchCase="'multi-line-text'">
<multiline-text-widget [field]="field" (fieldChanged)="fieldChanged($event);"></multiline-text-widget> <multiline-text-widget [field]="field" (fieldChanged)="fieldChanged($event);"></multiline-text-widget>

View File

@ -23,6 +23,7 @@ import { FormFieldModel } from './../core/form-field.model';
import { ComponentFixture, TestBed, async } from '@angular/core/testing'; import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { CoreModule } from 'ng2-alfresco-core'; import { CoreModule } from 'ng2-alfresco-core';
import { WIDGET_DIRECTIVES } from '../index'; import { WIDGET_DIRECTIVES } from '../index';
import { FormFieldComponent } from './../../form-field/form-field.component';
import { fakeFormJson } from '../../../services/assets/widget-visibility.service.mock'; import { fakeFormJson } from '../../../services/assets/widget-visibility.service.mock';
describe('ContainerWidget', () => { describe('ContainerWidget', () => {
@ -122,7 +123,7 @@ describe('ContainerWidget', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CoreModule], imports: [CoreModule],
declarations: [WIDGET_DIRECTIVES] declarations: [FormFieldComponent, WIDGET_DIRECTIVES]
}).compileComponents().then(() => { }).compileComponents().then(() => {
fixture = TestBed.createComponent(ContainerWidget); fixture = TestBed.createComponent(ContainerWidget);
containerWidgetComponent = fixture.componentInstance; containerWidgetComponent = fixture.componentInstance;

View File

@ -22,6 +22,7 @@ import { fakeFormJson } from '../../../services/assets/widget-visibility.service
import { TabsWidget } from './tabs.widget'; import { TabsWidget } from './tabs.widget';
import { TabModel } from '../core/tab.model'; import { TabModel } from '../core/tab.model';
import { WIDGET_DIRECTIVES } from '../index'; import { WIDGET_DIRECTIVES } from '../index';
import { FormFieldComponent } from './../../form-field/form-field.component';
import { CoreModule } from 'ng2-alfresco-core'; import { CoreModule } from 'ng2-alfresco-core';
describe('TabsWidget', () => { describe('TabsWidget', () => {
@ -102,7 +103,7 @@ describe('TabsWidget', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CoreModule], imports: [CoreModule],
declarations: [WIDGET_DIRECTIVES] declarations: [FormFieldComponent, WIDGET_DIRECTIVES]
}).compileComponents().then(() => { }).compileComponents().then(() => {
fixture = TestBed.createComponent(TabsWidget); fixture = TestBed.createComponent(TabsWidget);
tabWidgetComponent = fixture.componentInstance; tabWidgetComponent = fixture.componentInstance;

View File

@ -0,0 +1,57 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable, Type } from '@angular/core';
import { TextWidget } from './../components/widgets/text/text.widget';
@Injectable()
export class FormRenderingService {
private types: { [key: string]: Type<{}> } = {
'text': TextWidget
};
getComponentType(fieldType: string): Type<{}> {
if (fieldType) {
return this.types[fieldType] || null;
}
return null;
}
setComponentType(fieldType: string, componentType: Type<{}>, override: boolean = false) {
if (!fieldType) {
throw new Error(`fieldType is null or not defined`);
}
if (!componentType) {
throw new Error(`componentType is null or not defined`);
}
let existing = this.types[fieldType];
if (existing && !override) {
throw new Error(`componentType is already mapped, use override option if you intend replacing existing mapping.`);
}
this.types[fieldType] = componentType;
}
constructor() {
this.setComponentType('xx', TextWidget);
}
}