From db7fba867cccffc1d5ebc98b57fbb85928e9a324 Mon Sep 17 00:00:00 2001 From: Torgeir Helgevold Date: Sat, 5 Mar 2016 17:53:34 -0500 Subject: [PATCH] docs(cb-dynamic-form): new cookbook on dyn form gen with NgFormModel --- .../_examples/cb-dynamic-form/e2e-spec.js | 24 +++ .../_examples/cb-dynamic-form/ts/.gitignore | 1 + .../cb-dynamic-form/ts/app/app.component.ts | 23 +++ .../app/dynamic-form-question.component.html | 17 +++ .../ts/app/dynamic-form-question.component.ts | 14 ++ .../ts/app/dynamic-form.component.html | 17 +++ .../ts/app/dynamic-form.component.ts | 30 ++++ .../_examples/cb-dynamic-form/ts/app/main.ts | 5 + .../cb-dynamic-form/ts/app/question-base.ts | 25 +++ .../ts/app/question-control.service.ts | 18 +++ .../ts/app/question-dropdown.ts | 12 ++ .../ts/app/question-textbox.ts | 12 ++ .../ts/app/question.service.ts | 47 ++++++ .../cb-dynamic-form/ts/example-config.json | 0 .../_examples/cb-dynamic-form/ts/index.html | 39 +++++ .../_examples/cb-dynamic-form/ts/plnkr.json | 9 ++ .../_examples/cb-dynamic-form/ts/sample.css | 7 + public/docs/ts/latest/cookbook/_data.json | 5 + .../docs/ts/latest/cookbook/dynamic-form.jade | 143 ++++++++++++++++++ .../cookbooks/dynamic-form/dynamic-form.png | Bin 0 -> 22838 bytes 20 files changed, 448 insertions(+) create mode 100644 public/docs/_examples/cb-dynamic-form/e2e-spec.js create mode 100644 public/docs/_examples/cb-dynamic-form/ts/.gitignore create mode 100644 public/docs/_examples/cb-dynamic-form/ts/app/app.component.ts create mode 100644 public/docs/_examples/cb-dynamic-form/ts/app/dynamic-form-question.component.html create mode 100644 public/docs/_examples/cb-dynamic-form/ts/app/dynamic-form-question.component.ts create mode 100644 public/docs/_examples/cb-dynamic-form/ts/app/dynamic-form.component.html create mode 100644 public/docs/_examples/cb-dynamic-form/ts/app/dynamic-form.component.ts create mode 100644 public/docs/_examples/cb-dynamic-form/ts/app/main.ts create mode 100644 public/docs/_examples/cb-dynamic-form/ts/app/question-base.ts create mode 100644 public/docs/_examples/cb-dynamic-form/ts/app/question-control.service.ts create mode 100644 public/docs/_examples/cb-dynamic-form/ts/app/question-dropdown.ts create mode 100644 public/docs/_examples/cb-dynamic-form/ts/app/question-textbox.ts create mode 100644 public/docs/_examples/cb-dynamic-form/ts/app/question.service.ts create mode 100644 public/docs/_examples/cb-dynamic-form/ts/example-config.json create mode 100644 public/docs/_examples/cb-dynamic-form/ts/index.html create mode 100644 public/docs/_examples/cb-dynamic-form/ts/plnkr.json create mode 100644 public/docs/_examples/cb-dynamic-form/ts/sample.css create mode 100644 public/docs/ts/latest/cookbook/dynamic-form.jade create mode 100644 public/resources/images/cookbooks/dynamic-form/dynamic-form.png diff --git a/public/docs/_examples/cb-dynamic-form/e2e-spec.js b/public/docs/_examples/cb-dynamic-form/e2e-spec.js new file mode 100644 index 0000000000..9254c86717 --- /dev/null +++ b/public/docs/_examples/cb-dynamic-form/e2e-spec.js @@ -0,0 +1,24 @@ +describe('Dynamic Form', function () { + + beforeAll(function () { + browser.get(''); + }); + + it('should submit form', function () { + var firstNameElement = element.all(by.css('input[id=firstName]')).get(0); + expect(firstNameElement.getAttribute('value')).toEqual('Bombasto'); + + var emailElement = element.all(by.css('input[id=emailAddress]')).get(0); + var email = 'test@test.com'; + emailElement.sendKeys(email); + expect(emailElement.getAttribute('value')).toEqual(email); + + element(by.css('select option[value="solid"]')).click() + + var saveButton = element.all(by.css('button')).get(0); + saveButton.click().then(function(){ + expect(element(by.xpath("//strong[contains(text(),'Saved the following values')]")).isPresent()).toBe(true); + }); + }); + +}); diff --git a/public/docs/_examples/cb-dynamic-form/ts/.gitignore b/public/docs/_examples/cb-dynamic-form/ts/.gitignore new file mode 100644 index 0000000000..cf44e148ba --- /dev/null +++ b/public/docs/_examples/cb-dynamic-form/ts/.gitignore @@ -0,0 +1 @@ +**/*.js \ No newline at end of file diff --git a/public/docs/_examples/cb-dynamic-form/ts/app/app.component.ts b/public/docs/_examples/cb-dynamic-form/ts/app/app.component.ts new file mode 100644 index 0000000000..a30bf8f677 --- /dev/null +++ b/public/docs/_examples/cb-dynamic-form/ts/app/app.component.ts @@ -0,0 +1,23 @@ +// #docregion +import {Component} from 'angular2/core' +import {DynamicForm} from './dynamic-form.component'; +import {QuestionService} from './question.service'; + +@Component({ + selector: 'my-app', + template: ` +
+

Job Application for Heroes

+ +
+ `, + directives: [DynamicForm], + providers: [QuestionService] +}) +export class AppComponent { + questions:any[] + + constructor(service: QuestionService) { + this.questions = service.getQuestions(); + } +} diff --git a/public/docs/_examples/cb-dynamic-form/ts/app/dynamic-form-question.component.html b/public/docs/_examples/cb-dynamic-form/ts/app/dynamic-form-question.component.html new file mode 100644 index 0000000000..410eb26527 --- /dev/null +++ b/public/docs/_examples/cb-dynamic-form/ts/app/dynamic-form-question.component.html @@ -0,0 +1,17 @@ + +
+
{{question.label}}
+ +
+ + + + + +
+ +
{{question.label}} is required
+
diff --git a/public/docs/_examples/cb-dynamic-form/ts/app/dynamic-form-question.component.ts b/public/docs/_examples/cb-dynamic-form/ts/app/dynamic-form-question.component.ts new file mode 100644 index 0000000000..2348e4d8b9 --- /dev/null +++ b/public/docs/_examples/cb-dynamic-form/ts/app/dynamic-form-question.component.ts @@ -0,0 +1,14 @@ +// #docregion +import {Component, Input} from 'angular2/core'; +import {ControlGroup} from 'angular2/common'; +import {QuestionBase} from './question-base'; + +@Component({ + selector:'df-question', + templateUrl:'app/dynamic-form-question.component.html' +}) +export class DynamicFormQuestionComponent { + @Input() question:QuestionBase; + @Input() form:ControlGroup; + get isValid() { return this.form.controls[this.question.key].valid; } +} diff --git a/public/docs/_examples/cb-dynamic-form/ts/app/dynamic-form.component.html b/public/docs/_examples/cb-dynamic-form/ts/app/dynamic-form.component.html new file mode 100644 index 0000000000..7cb4b08ce3 --- /dev/null +++ b/public/docs/_examples/cb-dynamic-form/ts/app/dynamic-form.component.html @@ -0,0 +1,17 @@ + +
+
+ +
+ +
+ +
+ +
+
+ +
+ Saved the following values
{{payLoad}} +
+
diff --git a/public/docs/_examples/cb-dynamic-form/ts/app/dynamic-form.component.ts b/public/docs/_examples/cb-dynamic-form/ts/app/dynamic-form.component.ts new file mode 100644 index 0000000000..aac95acb39 --- /dev/null +++ b/public/docs/_examples/cb-dynamic-form/ts/app/dynamic-form.component.ts @@ -0,0 +1,30 @@ +// #docregion +import {Component, Input, OnInit} from 'angular2/core'; +import {ControlGroup} from 'angular2/common'; + +import {QuestionBase} from './question-base'; +import {QuestionControlService} from './question-control.service'; +import {DynamicFormQuestionComponent} from './dynamic-form-question.component'; + +@Component({ + selector:'dynamic-form', + templateUrl:'app/dynamic-form.component.html', + directives: [DynamicFormQuestionComponent], + providers: [QuestionControlService] +}) +export class DynamicForm { + + @Input() questions: QuestionBase[] = []; + form: ControlGroup; + payLoad = ''; + + constructor(private _qcs: QuestionControlService) { } + + ngOnInit(){ + this.form = this._qcs.toControlGroup(this.questions); + } + + onSubmit() { + this.payLoad = JSON.stringify(this.form.value); + } +} diff --git a/public/docs/_examples/cb-dynamic-form/ts/app/main.ts b/public/docs/_examples/cb-dynamic-form/ts/app/main.ts new file mode 100644 index 0000000000..dd43b98095 --- /dev/null +++ b/public/docs/_examples/cb-dynamic-form/ts/app/main.ts @@ -0,0 +1,5 @@ +import {bootstrap} from 'angular2/platform/browser'; +import {AppComponent} from './app.component'; + +bootstrap(AppComponent, []) + .catch((err:any) => console.error(err)); diff --git a/public/docs/_examples/cb-dynamic-form/ts/app/question-base.ts b/public/docs/_examples/cb-dynamic-form/ts/app/question-base.ts new file mode 100644 index 0000000000..c22de02989 --- /dev/null +++ b/public/docs/_examples/cb-dynamic-form/ts/app/question-base.ts @@ -0,0 +1,25 @@ +// #docregion +export class QuestionBase{ + value:T; + key:string; + label:string; + required:boolean; + order:number; + controlType:string; + + constructor(options:{ + value?:T, + key?:string, + label?:string, + required?:boolean, + order?:number, + controlType?:string + } = {}){ + this.value = options.value; + this.key = options.key || ''; + this.label = options.label || ''; + this.required = !!options.required; + this.order = options.order === undefined ? 1 : options.order; + this.controlType = options.controlType || ''; + } +} diff --git a/public/docs/_examples/cb-dynamic-form/ts/app/question-control.service.ts b/public/docs/_examples/cb-dynamic-form/ts/app/question-control.service.ts new file mode 100644 index 0000000000..cc7ebc751a --- /dev/null +++ b/public/docs/_examples/cb-dynamic-form/ts/app/question-control.service.ts @@ -0,0 +1,18 @@ +// #docregion +import {Injectable} from 'angular2/core'; +import {ControlGroup, FormBuilder, Validators} from 'angular2/common'; +import {QuestionBase} from './question-base'; + +@Injectable() +export class QuestionControlService { + constructor(private _fb:FormBuilder){ } + + toControlGroup(questions:QuestionBase[] ) { + let group = {}; + + questions.forEach(question => { + group[question.key] = question.required ? [question.value || '', Validators.required] : []; + }); + return this._fb.group(group); + } +} diff --git a/public/docs/_examples/cb-dynamic-form/ts/app/question-dropdown.ts b/public/docs/_examples/cb-dynamic-form/ts/app/question-dropdown.ts new file mode 100644 index 0000000000..1c3ca2807a --- /dev/null +++ b/public/docs/_examples/cb-dynamic-form/ts/app/question-dropdown.ts @@ -0,0 +1,12 @@ +// #docregion +import {QuestionBase} from './question-base'; + +export class DropdownQuestion extends QuestionBase{ + controlType = 'dropdown'; + options:{key:string, value:string}[] = []; + + constructor(options:{} = {}){ + super(options); + this.options = options['options'] || []; + } +} diff --git a/public/docs/_examples/cb-dynamic-form/ts/app/question-textbox.ts b/public/docs/_examples/cb-dynamic-form/ts/app/question-textbox.ts new file mode 100644 index 0000000000..573209d944 --- /dev/null +++ b/public/docs/_examples/cb-dynamic-form/ts/app/question-textbox.ts @@ -0,0 +1,12 @@ +// #docregion +import {QuestionBase} from './question-base'; + +export class TextboxQuestion extends QuestionBase{ + controlType = 'textbox'; + type:string; + + constructor(options:{} = {}){ + super(options); + this.type = options['type'] || ''; + } +} diff --git a/public/docs/_examples/cb-dynamic-form/ts/app/question.service.ts b/public/docs/_examples/cb-dynamic-form/ts/app/question.service.ts new file mode 100644 index 0000000000..0b805758f4 --- /dev/null +++ b/public/docs/_examples/cb-dynamic-form/ts/app/question.service.ts @@ -0,0 +1,47 @@ +// #docregion +import {Injectable} from 'angular2/core'; +import {QuestionBase} from './question-base'; +import {DynamicForm} from './dynamic-form.component'; +import {TextboxQuestion} from './question-textbox'; +import {DropdownQuestion} from './question-dropdown'; + +@Injectable() +export class QuestionService { + + // Todo: get from a remote source of question metadata + // Todo: make asynchronous + getQuestions() { + + let questions:QuestionBase[] = [ + + new DropdownQuestion({ + key:'brave', + label: 'Bravery Rating', + options: [ + {key:'solid', value:'Solid'}, + {key:'great', value:'Great'}, + {key:'good', value:'Good'}, + {key:'unproven',value:'Unproven'} + ], + order: 3 + }), + + new TextboxQuestion({ + key:'firstName', + label:'First name', + value:'Bombasto', + required: true, + order: 1 + }), + + new TextboxQuestion({ + key:'emailAddress', + label:'Email', + type: 'email', + order: 2 + }) + ]; + + return questions.sort((a,b) => a.order - b.order); + } +} diff --git a/public/docs/_examples/cb-dynamic-form/ts/example-config.json b/public/docs/_examples/cb-dynamic-form/ts/example-config.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/public/docs/_examples/cb-dynamic-form/ts/index.html b/public/docs/_examples/cb-dynamic-form/ts/index.html new file mode 100644 index 0000000000..a5874bff3f --- /dev/null +++ b/public/docs/_examples/cb-dynamic-form/ts/index.html @@ -0,0 +1,39 @@ + + + + + Dynamic Form + + + + + + + + + + + + + + + + + + + + Loading app... + + + diff --git a/public/docs/_examples/cb-dynamic-form/ts/plnkr.json b/public/docs/_examples/cb-dynamic-form/ts/plnkr.json new file mode 100644 index 0000000000..faa498f46f --- /dev/null +++ b/public/docs/_examples/cb-dynamic-form/ts/plnkr.json @@ -0,0 +1,9 @@ +{ + "description": "Dynamic Form", + "files":[ + "!**/*.d.ts", + "!**/*.js", + "!**/*.[1].*" + ], + "tags":["cookbook"] +} diff --git a/public/docs/_examples/cb-dynamic-form/ts/sample.css b/public/docs/_examples/cb-dynamic-form/ts/sample.css new file mode 100644 index 0000000000..fe2cc28481 --- /dev/null +++ b/public/docs/_examples/cb-dynamic-form/ts/sample.css @@ -0,0 +1,7 @@ +.errorMessage{ + color:red; +} + +.form-row{ + margin-top: 10px; +} \ No newline at end of file diff --git a/public/docs/ts/latest/cookbook/_data.json b/public/docs/ts/latest/cookbook/_data.json index 5dfbf9c992..31e71596e4 100644 --- a/public/docs/ts/latest/cookbook/_data.json +++ b/public/docs/ts/latest/cookbook/_data.json @@ -14,5 +14,10 @@ "component-communication": { "title": "Component Interaction", "description": "Share information between different directives and components" + }, + + "dynamic-form": { + "title": "Dynamic Form", + "description": "Render dynamic forms with NgFormModel" } } diff --git a/public/docs/ts/latest/cookbook/dynamic-form.jade b/public/docs/ts/latest/cookbook/dynamic-form.jade new file mode 100644 index 0000000000..e196074b66 --- /dev/null +++ b/public/docs/ts/latest/cookbook/dynamic-form.jade @@ -0,0 +1,143 @@ +include ../_util-fns + +:marked + We can't always justify the cost and time to build handcrafted forms, + especially if we'll need a great number of them, they're similar to each other, and they change frequently + to meet rapidly changing business and regulatory requirements. + + It may be more economical to create the forms dynamically, based on metadata that describe the business object model. + + In this cookbook we show how to use `ngFormModel` to dynamically to render a simple form with different control types and validation. + It's a primitive start. + It might evolve to support a much richer variety of questions, more graceful rendering, and superior user experience. + All such greatness has humble beginnings. + + Our example we use a dynamic form to build an online application experience for heroes seeking employment. + The agency is constantly tinkering with the application process. + We can create the forms on the fly *without changing our application code*. + + +:marked + ## Table of contents + + [Question Model](#object-model) + + [Form Component](#form-component) + + [Questionnaire Metadata](#questionnaire-metadata) + + [Dynamic Template](#dynamic-template) + +:marked + **See the [live example](/resources/live-examples/cb-dynamic-form/ts/plnkr.html)**. + +.l-main-section + +:marked + ## Question Model + + The first step is to define an object model that can describe all scenarios needed by the form functionality. + The hero application process involves a form with a lot of questions. + The "question" is the most fundamental object in the model. + + We have created `QuestionBase` as the most fundamental question class. + ++makeExample('cb-dynamic-form/ts/app/question-base.ts','','app/question-base.ts') + +:marked + From this base we derived two new classes in `TextboxQuestion` and `DropdownQuestion` that represent Textbox and Dropdown questions. + The idea is that the form will be bound to specific question types and render the appropriate controls dynamically. + + `TextboxQuestion` supports multiple html5 types like text, email, url etc via the `type` property. + ++makeExample('cb-dynamic-form/ts/app/question-textbox.ts',null,'app/question-textbox.ts')(format='.') + +:marked + `DropdownQuestion` presents a list of choices in a select box. + ++makeExample('cb-dynamic-form/ts/app/question-dropdown.ts',null,'app/question-dropdown.ts')(format='.') + +:marked + Next we have defined `QuestionControlService`, a simple service for transforming our questions to an ngForm control group. + In a nutshell, the control group consumes the metadata from the question model and allows us to specify default values and validation rules. + ++makeExample('cb-dynamic-form/ts/app/question-control.service.ts',null,'app/question-control.service.ts')(format='.') + + +:marked + ## Question form components + Now that we have defined the complete model we are ready to create components to represent the dynamic form. + +:marked + `DynamicForm` is the entry point and the main container for the form. ++makeTabs( + `cb-dynamic-form/ts/app/dynamic-form.component.html, + cb-dynamic-form/ts/app/dynamic-form.component.ts`, + null, + `dynamic-form.component.html, + dynamic-form.component.ts` +) +:marked + It presents a list of questions, each question bound to a `` component element. + The `` tag matches the `DynamicFormQuestionComponent`, + the component responsible for rendering the details of each _individual_ question based on values in the data-bound question object. + ++makeTabs( + `cb-dynamic-form/ts/app/dynamic-form-question.component.html, + cb-dynamic-form/ts/app/dynamic-form-question.component.ts`, + null, + `dynamic-form-question.component.html, + dynamic-form-question.component.ts` +) +:marked + Notice this component can present any type of question in our model. + We only have two types of questions at this point but we can imagine many more. + The `ngSwitch` determines which type of question to display. + + In both components we're relying on Angular's **ngFormMode** to connect the template HTML to the + underlying control objects, populated from the question model with display and validation rules. + + +:marked + ## Questionnaire data +:marked + `DynamicForm` expects the list of questions in the form of an array bound to `@Input() questions`. + + The set of questions we have defined for the job application is returned from the `QuestionService`. + In a real app we'd retrieve these questions from storage. + + The key point is that we control the hero job application questions entirely through the objects returned from `QuestionService`. + Questionnaire maintenance is a simple matter of adding, updating, and removing objects from the `questions` array. + ++makeExample('cb-dynamic-form/ts/app/question.service.ts','','app/question.service.ts') + +:marked + Finally, we display an instance of the form in the `AppComponent` shell. + ++makeExample('cb-dynamic-form/ts/app/app.component.ts','','app.component.ts') + + +:marked + ## Dynamic Template + Although in this example we're model a job application for heroes, there are no references to any specific hero question + outside the objects returned by `QuestionService`. + + This is very important since it allows us to repurpose the components for any type of survey + as long as it's compatible with our *question* object model. + The key is the dynamic data binding of metadata used to render the form + without making any hardcoded assumptions about specific questions. + In addition to control metadata, we are also adding validation dynamically. + + The *Save* button is disabled until the form is in a valid state. + When the form is valid, we can click *Save* and the app renders the current form values as JSON. + This proves that any user input is bound back to the data model. + Saving and retrieving the data is an exercise for another time. + +:marked + The final form looks like this: +figure.image-display + img(src="/resources/images/cookbooks/dynamic-form/dynamic-form.png" alt="Dynamic-Form") + + +:marked + [Back to top](#top) \ No newline at end of file diff --git a/public/resources/images/cookbooks/dynamic-form/dynamic-form.png b/public/resources/images/cookbooks/dynamic-form/dynamic-form.png new file mode 100644 index 0000000000000000000000000000000000000000..54b680a672ac171b239eeaf5f064f2d057075234 GIT binary patch literal 22838 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 P3GvX^HNIV`{V@DLkYYXW literal 0 HcmV?d00001