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:
@@ -1 +1 @@
|
||||
<dyn-form [questions]="formQuestions" (onSubmit)="submit($event)"></dyn-form>
|
||||
<dyn-form [questions]="formQuestions" (onSubmit)="submit($event)" [value]="formValue" [type]="'insert'"></dyn-form>
|
||||
@@ -18,6 +18,6 @@ export class AppComponent {
|
||||
|
||||
|
||||
submit(value: any) {
|
||||
console.log("send", value);
|
||||
console.log('send', value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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: [
|
||||
|
||||
@@ -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>
|
||||
`
|
||||
})
|
||||
|
||||
98
src/app/modules/dyn-form/inputs/dropdown-input.component.ts
Normal file
98
src/app/modules/dyn-form/inputs/dropdown-input.component.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
33
src/app/modules/dyn-form/inputs/hidden-input.component.ts
Normal file
33
src/app/modules/dyn-form/inputs/hidden-input.component.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
8
src/app/services/hello.service.ts
Normal file
8
src/app/services/hello.service.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable()
|
||||
export class HelloService {
|
||||
sayHello(): void {
|
||||
console.log("Hello world from my Service!");
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"mapRoot":"/src/",
|
||||
"sourceRoot":"/src/",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"lib": [ "es2015", "dom" ],
|
||||
|
||||
Reference in New Issue
Block a user