Merge remote-tracking branch 'origin/master'

# Conflicts:
#	src/app/app.component.html
#	src/app/app.component.ts
#	src/app/app.module.ts
#	src/app/modules/dyn-form/dyn-form.module.ts
This commit is contained in:
Sebastian Seedorf
2017-05-30 09:20:34 +02:00
12 changed files with 346 additions and 263 deletions

View File

@@ -1 +1 @@
<dyn-form [questions]="formQuestions" (onSubmit)="submit($event)"></dyn-form>
<dyn-form [questions]="formQuestions" (onSubmit)="submit($event)" [value]="formValue" [type]="'insert'"></dyn-form>

View File

@@ -18,6 +18,6 @@ export class AppComponent {
submit(value: any) {
console.log("send", value);
console.log('send', value);
}
}

View File

@@ -1,8 +1,8 @@
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { DynFormModule } from './modules/dyn-form/dyn-form.module';
import {AppComponent} from './app.component';
import {DynFormModule} from './modules/dyn-form/dyn-form.module';
import {HttpCachedService} from './services/http-cached.service';
import {HttpModule} from '@angular/http';

View File

@@ -6,14 +6,14 @@ import {FormControl, FormGroup} from '@angular/forms';
selector: 'dyn-form',
template: `
<form (ngSubmit)="submit()" [formGroup]="form">
<dyn-question *ngFor="let question of questions" [question]="question" [form]="form"></dyn-question>
<dyn-question *ngFor="let question of questions" [hidden]="question.properties.methods && question.properties.methods.indexOf(type)==-1" [question]="question" [form]="form" [type]="type"></dyn-question>
<div class="form-row">
<button type="submit">Save</button>
</div>
</form>
`
})
export class DynFormComponent {
export class DynFormComponent implements OnInit {
@Input() questions: QuestionInterface[];
@Input() value: any;
@Input() type: "'insert'|'update'|'delete'|'view'";
@@ -22,13 +22,17 @@ export class DynFormComponent {
private form: FormGroup;
constructor() {
this.form = new FormGroup({
'flags': new FormControl({'alpha': true})
});
public ngOnInit () {
let controls = {};
for(let i = 0; i < this.questions.length; i++) {
let key = this.questions[i].properties.key;
controls[key] = new FormControl(this.value[key]);
}
this.form = new FormGroup(controls);
}
submit() {
private submit() {
console.log("isValid", this.form.valid);
this.onSubmit.emit(this.form.value);
}
}

View File

@@ -2,9 +2,11 @@ import { ReactiveFormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { DynFormComponent } from './dyn-form.component';
import {DynQuestionComponent} from './dyn-question.component';
import {CounterInputComponent} from './inputs/counter-input.component';
import {KeysPipe, TagInputComponent} from './inputs/tag-input.component';
import {DynQuestionComponent} from './dyn-question.component';
import {HiddenInputComponent} from './inputs/hidden-input.component';
import {DropdownInputComponent} from './inputs/dropdown-input.component';
import {QuestionService} from './services/question.service';
@@ -21,6 +23,8 @@ import {QuestionService} from './services/question.service';
DynQuestionComponent,
CounterInputComponent,
TagInputComponent,
HiddenInputComponent,
DropdownInputComponent,
KeysPipe
],
providers: [

View File

@@ -6,11 +6,21 @@ import {FormGroup} from '@angular/forms';
selector: 'dyn-question',
template: `
<div [ngSwitch]="question.type" [formGroup]="form">
<label [for]="question.properties.key">{{question.properties.label}}</label>
<tag-input *ngSwitchCase="'flag'"
[formControlName]="question.properties.key" [id]="question.properties.key"
[items]="question.properties.options" [nullable]="question.constraints.optional"
(ngModelChange)="change()"></tag-input>
<label [for]="question.properties.key">{{question.properties.label}}</label>
<tag-input *ngSwitchCase="'flag'"
[formControlName]="question.properties.key" [id]="question.properties.key"
[nullable]="question.constraints.optional" [readonly]="type=='view'"
[items]="question.properties.options"
(ngModelChange)="change()"></tag-input>
<hidden-input *ngSwitchCase="'hidden'"
[formControlName]="question.properties.key" [id]="question.properties.key"
[nullable]="question.constraints.optional" [readonly]="type=='view'"
(ngModelChange)="change()"></hidden-input>
<dropdown-input *ngSwitchCase="'dropdown'"
[formControlName]="question.properties.key" [id]="question.properties.key"
[nullable]="question.constraints.optional" [readonly]="type=='view'"
[items]="question.properties.options"
(ngModelChange)="change()"></dropdown-input>
</div>
`
})

View File

@@ -0,0 +1,98 @@
import {Component, forwardRef, Inject, Input, OnChanges, Pipe, PipeTransform, SimpleChanges} from '@angular/core';
import { NG_VALUE_ACCESSOR, NG_VALIDATORS, FormControl, ValidationErrors } from '@angular/forms';
import {CustomInputComponent} from "./custom-input.component";
import {HelloService} from '../../../services/hello.service';
@Pipe({name: 'keys'})
export class KeysPipe implements PipeTransform {
transform(value: Object, args:string[]) : any {
let keys = [];
for (let key in value) {
keys.push({key: key, value: value[key]});
}
return keys;
}
}
@Component({
selector: 'dropdown-input',
template:
`
<select *ngIf="!readonly" [value]="value" (change)="onChange($event.target.value)">
<option *ngFor="let entry of listedItems" [value]="entry.key">{{entry.value}}</option>
</select>
<div *ngIf="readonly">{{constVal}}</div>
`,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DropdownInputComponent),
multi: true,
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => DropdownInputComponent),
multi: true,
}]
})
export class DropdownInputComponent extends CustomInputComponent implements OnChanges {
@Input() readonly: boolean = false;
@Input() nullable: boolean = false;
@Input() items: Array<{key: string, value: string}> = [];
private listedItems: Array<{key: string, value: string}> = [];
private constVal: string;
constructor(private helloService: HelloService) {
super();
helloService.sayHello();
}
// set initial value
public writeValue(obj: any): void {
console.log("init: ", obj);
this.value = obj;
this.updateConst();
}
public ngOnChanges(changes: SimpleChanges) {
if (changes['items'] || changes['nullable']) {
if (this.nullable)
this.listedItems = [{key: "", value: "N/A"}, {key: null, value: "N/A"}].concat(this.items);
else
this.listedItems = [].concat(this.items);
}
this.updateConst();
}
private updateConst() {
if (this.readonly) {
for (let i=0; i<this.listedItems.length; i++) {
if (this.listedItems[i].key==this.value) {
this.constVal = this.listedItems[i].value;
break;
}
}
}
console.log(this.readonly, this.listedItems, this.value, this.constVal);
}
// validates the form, returns null when valid else the validation object
public validate(c: FormControl): ValidationErrors {
if (this.value===null) {
return this.nullable ? null : {notNullable: {valid: false}};
}
for (let i=0; i<this.items.length; i++) {
if (this.items[i].key==this.value)
return null;
}
return {invalidValue: {valid: false}};
}
// on button click
protected onChange(value: any): boolean {
this.value = value;
super.change();
return false;
}
}

View File

@@ -0,0 +1,33 @@
import {Component, forwardRef, Input} from '@angular/core';
import { NG_VALUE_ACCESSOR, NG_VALIDATORS, FormControl, ValidationErrors } from '@angular/forms';
import {CustomInputComponent} from "./custom-input.component";
@Component({
selector: 'hidden-input',
template: ``,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => HiddenInputComponent),
multi: true,
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => HiddenInputComponent),
multi: true,
}]
})
export class HiddenInputComponent extends CustomInputComponent {
@Input() readonly: boolean = false;
@Input() nullable: boolean = false;
// set initial value
public writeValue(obj: any): void {
this.value = obj;
}
// validates the form, returns null when valid else the validation object
public validate(c: FormControl): ValidationErrors {
return null;
}
}

View File

@@ -34,8 +34,9 @@ export class KeysPipe implements PipeTransform {
}]
})
export class TagInputComponent extends CustomInputComponent {
@Input() items: Array<{key: string, value: string}> = [];
@Input() readonly: boolean = false;
@Input() nullable: boolean = false;
@Input() items: Array<{key: string, value: string}> = [];
private lookup: {[_:string]: string} = {};
@@ -57,9 +58,10 @@ export class TagInputComponent extends CustomInputComponent {
this.value[key] = (this.nullable ? null : false);
}
}
console.log("writeValue", isUpdated, this.value);
if (isUpdated) {
super.change();
setTimeout(() => super.change(), 0);
}
}
@@ -70,6 +72,7 @@ export class TagInputComponent extends CustomInputComponent {
// on button click
protected onChange(key: string, forward: boolean): boolean {
if (this.readonly) return false;
let value = this.value[key];
if (!this.nullable)
value = !value;

View File

@@ -0,0 +1,8 @@
import { Injectable } from '@angular/core';
@Injectable()
export class HelloService {
sayHello(): void {
console.log("Hello world from my Service!");
}
}

View File

@@ -4,6 +4,8 @@
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"mapRoot":"/src/",
"sourceRoot":"/src/",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"lib": [ "es2015", "dom" ],