First custom Dynamic Form Module generation
This commit is contained in:
@@ -1,22 +1 @@
|
|||||||
<form #formCounter="ngForm">
|
|
||||||
<counter-input [(ngModel)]="resultCounter" name="res"></counter-input>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<p>Value:</p>
|
|
||||||
<pre>{{ resultCounter }}</pre>
|
|
||||||
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<form #formTag="ngForm">
|
|
||||||
<tag-input [(ngModel)]="resultTag" name="res" [id]="'a'" [items]="{'alpha': 'A', 'beta': 'B', 'gamma': 'G'}"></tag-input>
|
|
||||||
<tag-input [(ngModel)]="resultTag2" name="res2" [id]="'b'" [items]="{'alpha': 'A', 'beta': 'B', 'gamma': 'G'}" [nullable]="true"></tag-input>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<p>Value:</p>
|
|
||||||
<pre>{{ resultTag | json }}</pre>
|
|
||||||
<pre>{{ resultTag2 | json }}</pre>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<dyn-form [questions]="formQuestions" (onSubmit)="submit($event)"></dyn-form>
|
<dyn-form [questions]="formQuestions" (onSubmit)="submit($event)"></dyn-form>
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Component } from '@angular/core';
|
import {Component, Inject} from '@angular/core';
|
||||||
import {QuestionInterface} from './modules/dyn-form/types/question.interface';
|
import {QuestionInterface} from './modules/dyn-form/types/question.interface';
|
||||||
|
import {QuestionService} from './modules/dyn-form/services/question.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-app',
|
selector: 'my-app',
|
||||||
@@ -7,26 +8,14 @@ import {QuestionInterface} from './modules/dyn-form/types/question.interface';
|
|||||||
})
|
})
|
||||||
|
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
public resultCounter = 10;
|
public formQuestions: QuestionInterface[] = [];
|
||||||
public resultTag = {"alpha": true, "beta": false};
|
|
||||||
public resultTag2 = {"alpha": true, "beta": false};
|
|
||||||
|
|
||||||
public formQuestions: QuestionInterface[] = [
|
constructor(private questionService: QuestionService) {
|
||||||
{
|
this.questionService.getQuestions((res, err) => {
|
||||||
type: "flag",
|
this.formQuestions = res;
|
||||||
description: "This is a help tooltip",
|
});
|
||||||
properties: {
|
}
|
||||||
key: "flags",
|
|
||||||
label: "Form Type Flags",
|
|
||||||
order: 1,
|
|
||||||
|
|
||||||
// dropdown && flags
|
|
||||||
options: [{key: "alpha", value: "A"}, {key: "beta", value: "B"}]
|
|
||||||
}, constraints: {
|
|
||||||
optional: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
submit(value: any) {
|
submit(value: any) {
|
||||||
console.log("send", value);
|
console.log("send", value);
|
||||||
|
|||||||
@@ -2,19 +2,20 @@ import { NgModule } from '@angular/core';
|
|||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import {CustomInputModule} from "./custom-input.module";
|
|
||||||
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
|
||||||
import { DynFormModule } from './modules/dyn-form/dyn-form.module';
|
import { DynFormModule } from './modules/dyn-form/dyn-form.module';
|
||||||
|
import {HttpCachedService} from './services/http-cached.service';
|
||||||
|
import {HttpModule} from '@angular/http';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
FormsModule,
|
DynFormModule,
|
||||||
ReactiveFormsModule,
|
HttpModule
|
||||||
CustomInputModule,
|
|
||||||
DynFormModule
|
|
||||||
],
|
],
|
||||||
declarations: [ AppComponent ],
|
declarations: [ AppComponent ],
|
||||||
|
providers: [
|
||||||
|
HttpCachedService
|
||||||
|
],
|
||||||
bootstrap: [ AppComponent ]
|
bootstrap: [ AppComponent ]
|
||||||
})
|
})
|
||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
import { Component, forwardRef } from '@angular/core';
|
|
||||||
import { NG_VALUE_ACCESSOR, NG_VALIDATORS, FormControl, ValidationErrors } from '@angular/forms';
|
|
||||||
import {CustomInputComponent} from "./custom-input.component";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'counter-input',
|
|
||||||
template:
|
|
||||||
`
|
|
||||||
<button (click)="onChange(-1)" (blur)="onBlur()">-</button>
|
|
||||||
{{value}}
|
|
||||||
<button (click)="onChange(1)" (blur)="onBlur()">+</button>
|
|
||||||
`,
|
|
||||||
providers: [
|
|
||||||
{
|
|
||||||
provide: NG_VALUE_ACCESSOR,
|
|
||||||
useExisting: forwardRef(() => CounterInputComponent),
|
|
||||||
multi: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: NG_VALIDATORS,
|
|
||||||
useExisting: forwardRef(() => CounterInputComponent),
|
|
||||||
multi: true,
|
|
||||||
}]
|
|
||||||
})
|
|
||||||
export class CounterInputComponent extends CustomInputComponent {
|
|
||||||
// set initial value
|
|
||||||
public writeValue(obj: any): void {
|
|
||||||
if (obj) {
|
|
||||||
this.value = parseInt(obj);
|
|
||||||
this.parseError = isNaN(this.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// validates the form, returns null when valid else the validation object
|
|
||||||
public validate(c: FormControl): ValidationErrors {
|
|
||||||
return (!this.parseError) ? null : {
|
|
||||||
numberParseError: {
|
|
||||||
valid: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// on button click
|
|
||||||
protected onChange(number: number): void {
|
|
||||||
this.value = this.value + number;
|
|
||||||
this.parseError = isNaN(this.value);
|
|
||||||
|
|
||||||
super.change();
|
|
||||||
}
|
|
||||||
|
|
||||||
// on touched
|
|
||||||
protected onBlur(): void {
|
|
||||||
super.blur();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import {ControlValueAccessor, FormControl, ValidationErrors, Validator} from '@angular/forms';
|
|
||||||
|
|
||||||
export abstract class CustomInputComponent implements ControlValueAccessor, Validator {
|
|
||||||
protected value: any;
|
|
||||||
protected parseError: boolean;
|
|
||||||
private propagateChange: (_: any) => void = () => {};
|
|
||||||
private propagateTouch: () => void = () => {};
|
|
||||||
|
|
||||||
// set initial value
|
|
||||||
public abstract writeValue(obj: any): void;
|
|
||||||
|
|
||||||
// register function to notify on change
|
|
||||||
public registerOnChange(fn: (_: any) => void): void {
|
|
||||||
this.propagateChange = fn;
|
|
||||||
}
|
|
||||||
|
|
||||||
// register function to notify on touch
|
|
||||||
public registerOnTouched(fn: () => void): void {
|
|
||||||
this.propagateTouch = fn;
|
|
||||||
}
|
|
||||||
|
|
||||||
// validates the form, returns null when valid else the validation object
|
|
||||||
public abstract validate(c: FormControl): ValidationErrors;
|
|
||||||
|
|
||||||
// on change
|
|
||||||
protected change(): void {
|
|
||||||
this.propagateChange(this.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// on touch
|
|
||||||
protected blur(): void {
|
|
||||||
this.propagateTouch();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import {FormsModule} from '@angular/forms';
|
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { CounterInputComponent } from './counter-input.component';
|
|
||||||
import {KeysPipe, TagInputComponent} from './tag-input.component';
|
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [
|
|
||||||
CommonModule,
|
|
||||||
FormsModule,
|
|
||||||
],
|
|
||||||
exports: [
|
|
||||||
CounterInputComponent,
|
|
||||||
TagInputComponent,
|
|
||||||
],
|
|
||||||
declarations: [
|
|
||||||
CounterInputComponent,
|
|
||||||
TagInputComponent,
|
|
||||||
KeysPipe
|
|
||||||
],
|
|
||||||
providers: [],
|
|
||||||
})
|
|
||||||
export class CustomInputModule { }
|
|
||||||
@@ -5,6 +5,7 @@ import { DynFormComponent } from './dyn-form.component';
|
|||||||
import {CounterInputComponent} from './inputs/counter-input.component';
|
import {CounterInputComponent} from './inputs/counter-input.component';
|
||||||
import {KeysPipe, TagInputComponent} from './inputs/tag-input.component';
|
import {KeysPipe, TagInputComponent} from './inputs/tag-input.component';
|
||||||
import {DynQuestionComponent} from './dyn-question.component';
|
import {DynQuestionComponent} from './dyn-question.component';
|
||||||
|
import {QuestionService} from './services/question.service';
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@@ -22,6 +23,8 @@ import {DynQuestionComponent} from './dyn-question.component';
|
|||||||
TagInputComponent,
|
TagInputComponent,
|
||||||
KeysPipe
|
KeysPipe
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [
|
||||||
|
QuestionService
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class DynFormModule { }
|
export class DynFormModule { }
|
||||||
|
|||||||
29
src/app/modules/dyn-form/services/question.service.ts
Normal file
29
src/app/modules/dyn-form/services/question.service.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import {HttpCachedService} from '../../../services/http-cached.service';
|
||||||
|
import {QuestionInterface} from '../types/question.interface';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class QuestionService {
|
||||||
|
constructor(private httpCachedService: HttpCachedService) {
|
||||||
|
|
||||||
|
}
|
||||||
|
public getQuestions(cb: (res: QuestionInterface[], err: any) => void) {
|
||||||
|
setTimeout(() => cb([
|
||||||
|
{
|
||||||
|
type: "flag",
|
||||||
|
description: "This is a help tooltip",
|
||||||
|
properties: {
|
||||||
|
key: "flags",
|
||||||
|
label: "Form Type Flags",
|
||||||
|
order: 1,
|
||||||
|
|
||||||
|
// dropdown && flags
|
||||||
|
options: [{key: "alpha", value: "A"}, {key: "beta", value: "B"}]
|
||||||
|
},
|
||||||
|
constraints: {
|
||||||
|
optional: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
], null), 4000);
|
||||||
|
}
|
||||||
|
}
|
||||||
152
src/app/services/http-base.service.ts
Normal file
152
src/app/services/http-base.service.ts
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
import { Http, Response, Headers, RequestOptionsArgs } from '@angular/http';
|
||||||
|
import { ReplaySubject } from 'rxjs/ReplaySubject';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { ResponseInterface } from './response.interface';
|
||||||
|
import 'rxjs/add/operator/map';
|
||||||
|
import 'rxjs/add/operator/catch';
|
||||||
|
import 'rxjs/add/observable/throw';
|
||||||
|
|
||||||
|
|
||||||
|
export abstract class HttpBaseService {
|
||||||
|
abstract get httpBase(): string;
|
||||||
|
abstract get httpSuffix(): string;
|
||||||
|
|
||||||
|
errorEmitter: ReplaySubject<any> = new ReplaySubject(1);
|
||||||
|
|
||||||
|
constructor(private _http: Http) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
getJSON(uri: string, query: {[propName: string]: any}, dataFunc: (data: ResponseInterface, e: Response | any) => void) {
|
||||||
|
var d: ResponseInterface = null;
|
||||||
|
var e: any = null;
|
||||||
|
var that = this;
|
||||||
|
this._doRequest(
|
||||||
|
this._http.get(this._getURI(uri, query), this._getOptions),
|
||||||
|
dataFunc
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
postJSON(uri: string, query: {[propName: string]: any}, body: {[propName: string]: any}, dataFunc: (data: ResponseInterface, e: Response | any) => void) {
|
||||||
|
var d: ResponseInterface;
|
||||||
|
var e: any;
|
||||||
|
var that = this;
|
||||||
|
this._doRequest(
|
||||||
|
this._http.post(this._getURI(uri, query), this._JSONtoQueryString(body), this._postOptions),
|
||||||
|
dataFunc
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
putJSON(uri: string, query: {[propName: string]: any}, body: {[propName: string]: any}, dataFunc: (data: ResponseInterface, e: Response | any) => void) {
|
||||||
|
var d: ResponseInterface;
|
||||||
|
var e: any;
|
||||||
|
var that = this;
|
||||||
|
this._doRequest(
|
||||||
|
this._http.put(this._getURI(uri, query), this._JSONtoQueryString(body), this._putOptions),
|
||||||
|
dataFunc
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteJSON(uri: string, query: {[propName: string]: any}, dataFunc: (data: ResponseInterface, e: Response | any) => void) {
|
||||||
|
var d: ResponseInterface;
|
||||||
|
var e: any;
|
||||||
|
var that = this;
|
||||||
|
this._doRequest(
|
||||||
|
this._http.delete(this._getURI(uri, query), this._deleteOptions),
|
||||||
|
dataFunc
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _doRequest(observe: Observable<Response>, dataFunc: (data: ResponseInterface, e: Response | any) => void) {
|
||||||
|
var d: ResponseInterface;
|
||||||
|
var e: any;
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
|
||||||
|
function _handleError(error: Response | any) {
|
||||||
|
function isJsonParsable(str: any) {
|
||||||
|
try {
|
||||||
|
JSON.parse(str);
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// In a real world app, we might use a remote logging infrastructure
|
||||||
|
let errMsg: string;
|
||||||
|
let errObj: {status: number, statusText: string, msg: string};
|
||||||
|
if (error instanceof Response) {
|
||||||
|
var err: string;
|
||||||
|
if (isJsonParsable(error.text())) {
|
||||||
|
const body = error.json();
|
||||||
|
err = JSON.stringify(body.meta.text || body.meta || body || '');
|
||||||
|
} else {
|
||||||
|
err = error.text();
|
||||||
|
}
|
||||||
|
errMsg = `${error.status} - ${error.statusText || ''} - ${err}`;
|
||||||
|
errObj = {status: error.status, statusText: error.statusText || '', msg: err};
|
||||||
|
} else {
|
||||||
|
errMsg = error.message ? error.message : error.toString();
|
||||||
|
errObj = {status: -1, statusText: "Server Error", msg: errMsg};
|
||||||
|
}
|
||||||
|
console.error(errMsg);
|
||||||
|
console.error(errObj);
|
||||||
|
that.errorEmitter.next(errObj);
|
||||||
|
return Observable.throw(errMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
observe
|
||||||
|
.map(this._extractData)
|
||||||
|
.catch(_handleError)
|
||||||
|
.subscribe(
|
||||||
|
data => d = data,
|
||||||
|
error => {e = error; /*_handleError(error);*/ dataFunc(null, e);},
|
||||||
|
() => dataFunc(d, e)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getURI(uri: string, query: {[propName: string]: any}): string {
|
||||||
|
var qString: string = this._JSONtoQueryString(query);
|
||||||
|
if (qString)
|
||||||
|
return this.httpBase + uri + this.httpSuffix + '?' + this._JSONtoQueryString(query);
|
||||||
|
else
|
||||||
|
return this.httpBase + uri + this.httpSuffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
private get _getOptions(): RequestOptionsArgs {
|
||||||
|
var headers = new Headers();
|
||||||
|
return {
|
||||||
|
headers: headers,
|
||||||
|
withCredentials: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private get _postOptions(): RequestOptionsArgs {
|
||||||
|
var headers = new Headers();
|
||||||
|
headers.append('Content-Type', 'application/x-www-form-urlencoded');
|
||||||
|
return {
|
||||||
|
headers: headers,
|
||||||
|
withCredentials: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private get _putOptions(): RequestOptionsArgs {
|
||||||
|
return this._postOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private get _deleteOptions(): RequestOptionsArgs {
|
||||||
|
return this._getOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _JSONtoQueryString(json: {[propName: string]: any}) {
|
||||||
|
return Object.keys(json).map(function(key) {
|
||||||
|
return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]);
|
||||||
|
}).join('&');
|
||||||
|
}
|
||||||
|
|
||||||
|
private _extractData(res: Response): ResponseInterface {
|
||||||
|
var body = res.json();
|
||||||
|
var resBody: ResponseInterface = body as ResponseInterface || null;
|
||||||
|
return resBody;
|
||||||
|
}
|
||||||
|
}
|
||||||
65
src/app/services/http-cached.service.ts
Normal file
65
src/app/services/http-cached.service.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import { Injectable, Inject } from '@angular/core';
|
||||||
|
import { Http, Response } from '@angular/http';
|
||||||
|
import { HttpService } from './http.service';
|
||||||
|
import { ResponseInterface } from './response.interface';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class HttpCachedService extends HttpService {
|
||||||
|
private _stored: any = {}; // {data: ResponseInterface, e: Response}
|
||||||
|
|
||||||
|
get refreshTime() {
|
||||||
|
return 3*1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(@Inject(Http) _http: Http) {
|
||||||
|
super(_http);
|
||||||
|
}
|
||||||
|
|
||||||
|
getJSON(uri: string, query: {[propName: string]: any}, dataFunc: (data: ResponseInterface, e: Response | any) => void) {
|
||||||
|
var hash: string = this._hashRequest("GET", uri, query);
|
||||||
|
if (this._stored.hasOwnProperty(hash)) {
|
||||||
|
if (this._stored[hash] instanceof Array) {
|
||||||
|
// pending request, add to event emitter
|
||||||
|
this._stored[hash].push(dataFunc);
|
||||||
|
} else {
|
||||||
|
// request alredy stored, send result
|
||||||
|
dataFunc(this._stored[hash].data, this._stored[hash].e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// fulfil request, no matching request stored
|
||||||
|
this._stored[hash] = [];
|
||||||
|
super.getJSON(uri, query, (data, e) => {
|
||||||
|
// request recieved, emit to caller and event subscriber
|
||||||
|
dataFunc(data, e);
|
||||||
|
for (var i = this._stored[hash].length - 1; i >= 0; i--) {
|
||||||
|
this._stored[hash][i](data, e);
|
||||||
|
}
|
||||||
|
this._stored[hash] = {
|
||||||
|
data: data,
|
||||||
|
e: e
|
||||||
|
};
|
||||||
|
var that = this;
|
||||||
|
setTimeout(function() {
|
||||||
|
delete that._stored[hash];
|
||||||
|
}, this.refreshTime);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _hashRequest(method: string, uri: string, query?: Object): string {
|
||||||
|
function objectToString(obj: Object = {}) {
|
||||||
|
var keys: string[] = [];
|
||||||
|
var result: string = '{';
|
||||||
|
for (var key in obj)
|
||||||
|
if (obj.hasOwnProperty(key))
|
||||||
|
keys.push(key);
|
||||||
|
keys.sort();
|
||||||
|
for (var i = keys.length - 1; i >= 0; i--) {
|
||||||
|
result += keys[i]+':"'+obj[keys[i]]+'",'
|
||||||
|
}
|
||||||
|
result += '}';
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return method+" "+uri+" "+objectToString(query);
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/app/services/http.service.ts
Normal file
19
src/app/services/http.service.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { Injectable, Inject } from '@angular/core';
|
||||||
|
import { Http } from '@angular/http';
|
||||||
|
import { HttpBaseService } from './http-base.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class HttpService extends HttpBaseService {
|
||||||
|
get httpBase() {
|
||||||
|
return "http://ccrruby1-1:3085";
|
||||||
|
//return '/public/mocks';
|
||||||
|
}
|
||||||
|
get httpSuffix() {
|
||||||
|
return "";
|
||||||
|
//return '.json';
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(@Inject(Http) _http: Http) {
|
||||||
|
super(_http);
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/app/services/response.interface.ts
Normal file
9
src/app/services/response.interface.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export interface ResponseInterface {
|
||||||
|
meta: {
|
||||||
|
code: number,
|
||||||
|
text: string,
|
||||||
|
uri?: string
|
||||||
|
},
|
||||||
|
data?: any,
|
||||||
|
uri?: string
|
||||||
|
}
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
import {Component, forwardRef, Input, Pipe, PipeTransform} from '@angular/core';
|
|
||||||
import { NG_VALUE_ACCESSOR, NG_VALIDATORS, FormControl, ValidationErrors } from '@angular/forms';
|
|
||||||
import {CustomInputComponent} from "./custom-input.component";
|
|
||||||
|
|
||||||
@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: 'tag-input',
|
|
||||||
template:
|
|
||||||
`
|
|
||||||
<div *ngFor="let entry of value | keys" (click)="onChange(entry.key, true)" (contextmenu)="onChange(entry.key, false)">
|
|
||||||
{{items[entry.key]}}: {{entry.value}}
|
|
||||||
</div>
|
|
||||||
`,
|
|
||||||
providers: [
|
|
||||||
{
|
|
||||||
provide: NG_VALUE_ACCESSOR,
|
|
||||||
useExisting: forwardRef(() => TagInputComponent),
|
|
||||||
multi: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: NG_VALIDATORS,
|
|
||||||
useExisting: forwardRef(() => TagInputComponent),
|
|
||||||
multi: true,
|
|
||||||
}]
|
|
||||||
})
|
|
||||||
export class TagInputComponent extends CustomInputComponent {
|
|
||||||
@Input() items: Array<{key: string, value: string}> = [];
|
|
||||||
@Input() nullable: boolean = false;
|
|
||||||
@Input() id: string = "";
|
|
||||||
|
|
||||||
// set initial value
|
|
||||||
public writeValue(obj: any): void {
|
|
||||||
let isUpdated = false;
|
|
||||||
this.value = {};
|
|
||||||
if (!obj) {
|
|
||||||
obj = {};
|
|
||||||
}
|
|
||||||
for (let key in this.items) {
|
|
||||||
if (this.items.hasOwnProperty(key)) {
|
|
||||||
if (obj.hasOwnProperty(key) && (typeof(obj[key]) === "boolean" || (this.nullable && null===obj[key])))
|
|
||||||
this.value[key] = obj[key];
|
|
||||||
else {
|
|
||||||
isUpdated = true;
|
|
||||||
this.value[key] = (this.nullable ? null : false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(this.id, isUpdated, this.value);
|
|
||||||
if (isUpdated) {
|
|
||||||
super.change();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// validates the form, returns null when valid else the validation object
|
|
||||||
public validate(c: FormControl): ValidationErrors {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// on button click
|
|
||||||
protected onChange(key: string, forward: boolean): boolean {
|
|
||||||
let value = this.value[key];
|
|
||||||
if (!this.nullable)
|
|
||||||
value = !value;
|
|
||||||
else if (value===null)
|
|
||||||
value = forward;
|
|
||||||
else if ((value ? !forward : forward))
|
|
||||||
value = null;
|
|
||||||
else
|
|
||||||
value = !value;
|
|
||||||
|
|
||||||
this.value = Object.assign({}, this.value);
|
|
||||||
this.value[key] = value;
|
|
||||||
|
|
||||||
super.change();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user