1+ import { bootstrap , Component , Decorator , Template , If , For , EventEmitter } from 'angular2/angular2' ;
2+ import { FormBuilder , Validators , FormDirectives , ControlGroup } from 'angular2/forms' ;
3+
4+ // HeaderFields renders the bound header control group. It can used as follows:
5+ //
6+ // <survey-header [header]="header"></survey-header>
7+ //
8+ // This component is self-contained and can be tested in isolation.
9+ @Component ( {
10+ selector : 'survey-header' ,
11+ bind : {
12+ "header" : "header"
13+ }
14+ } )
15+ @Template ( {
16+ inline : `
17+ <div [control-group]="header">
18+ <div>
19+ <label>Title:</label> <br/>
20+ <input type="text" control="title"/>
21+ <div *if="! header.controls.title.valid && header.controls.title.dirty">
22+ Title is required
23+ </div>
24+ </div>
25+
26+ <div>
27+ <label>Description:</label> <br/>
28+ <textarea control="description"></textarea>
29+ <div *if="! header.controls.description.valid && header.controls.description.dirty">
30+ Description is required
31+ </div>
32+ </div>
33+
34+ <div>
35+ <label>Publish Date:</label> <br/>
36+ <input type="date" control="date"/>
37+ </div>
38+ </div>
39+ ` ,
40+ directives : [ FormDirectives , If ]
41+ } )
42+ class HeaderFields {
43+ header :ControlGroup ;
44+ }
45+
46+
47+
48+ // SurveyQuestion renders an individual question. It can used as follows:
49+ //
50+ // <survey-question [question]="question" [index]="i" (delete)="onDelete()"></survey-question>
51+ //
52+ // SurveyQuestion uses EventEmitter to fire the delete action.
53+ // This component is self-contained and can be tested in isolation.
54+ @Component ( {
55+ selector : 'survey-question' ,
56+ bind : {
57+ "question" : "question" ,
58+ "index" : "index"
59+ }
60+ } )
61+ @Template ( {
62+ inline : `
63+ <h2>Question #{{index}}</h2>
64+
65+ <button (click)="deleteQuestion()">Delete</button>
66+
67+ <div [control-group]="question">
68+ <div>
69+ <label>Type:</label> <br/>
70+ <select control="type">
71+ <option value=""></option>
72+ <option value="text">Text</option>
73+ <option value="checkbox">Checkbox</option>
74+ <option value="textarea">Textarea</option>
75+ </select>
76+ <div *if="! question.controls.type.valid && question.controls.type.dirty">
77+ Type is required
78+ </div>
79+ </div>
80+
81+ <div>
82+ <label>Question:</label> <br/>
83+ <input type="text" control="questionText">
84+ <div *if="! question.controls.questionText.valid && question.controls.questionText.dirty">
85+ Question is required
86+ </div>
87+ </div>
88+
89+ <div *if="question.contains('responseLength')">
90+ <label>Response Length:</label> <br/>
91+ <input type="number" control="responseLength">
92+ <div *if="! question.controls.responseLength.valid && question.controls.responseLength.dirty">
93+ Length is required
94+ </div>
95+ </div>
96+ </div>
97+ ` ,
98+ directives : [ FormDirectives , If ]
99+ } )
100+ class SurveyQuestion {
101+ question :ControlGroup ;
102+ index :number ;
103+ onDelete :Function ;
104+
105+ constructor ( @EventEmitter ( "delete" ) onDelete :Function ) {
106+ this . onDelete = onDelete ;
107+ }
108+
109+ deleteQuestion ( ) {
110+ // Invoking an injected event emitter will fire an event,
111+ // which in this case will result in calling `deleteQuestion(i)`
112+ this . onDelete ( ) ;
113+ }
114+ }
115+
116+
117+
118+ // SurveyBuilder is a form that allows you to create a survey.
119+ @Component ( {
120+ selector : 'survey-builder-app' ,
121+ services : [ FormBuilder ]
122+ } )
123+ @Template ( {
124+ inline : `
125+ <h1>Create New Survey</h1>
126+
127+ <div [control-group]="form">
128+ <survey-header [header]="form.controls.header"></survey-header>
129+
130+ <button (click)="addQuestion()">Add Question</button>
131+ <survey-question
132+ *for="var q of form.controls.questions.controls; var i=index"
133+ [question]="q"
134+ [index]="i + 1"
135+ (delete)="deleteQuestion(i)">
136+ </survey-question>
137+
138+ <button (click)="submitForm()">Submit</button>
139+ </div>
140+ ` ,
141+ directives : [ FormDirectives , For , HeaderFields , SurveyQuestion ]
142+ } )
143+ class SurveyBuilder {
144+ form :ControlGroup ;
145+ builder :FormBuilder ;
146+
147+ constructor ( b :FormBuilder ) {
148+ this . builder = b ;
149+ this . form = b . group ( {
150+ "header" : b . group ( {
151+ "title" : [ "" , Validators . required ] ,
152+ "description" : [ "" , Validators . required ] ,
153+ "date" : ""
154+ } ) ,
155+ "questions" : b . array ( [ ] )
156+ } ) ;
157+ }
158+
159+ addQuestion ( ) {
160+ var newQuestion = this . builder . group ( {
161+ "type" : [ "" , Validators . required ] ,
162+ "questionText" : [ "" , Validators . required ] ,
163+ "responseLength" : [ 100 , Validators . required ]
164+ } , {
165+ // Optional controls can be dynamically added or removed from the form.
166+ // Here, the responseLength field is optional and not included by default.
167+ "optionals" : {
168+ "responseLength" : false
169+ }
170+ } ) ;
171+
172+ // Every Control has an observable of value changes. You can subscribe to this observable
173+ // to update the form, update the application model, etc.
174+ // These observables can also be transformed and combined. This enables implementing
175+ // complex form interactions in a declarative fashion.
176+ //
177+ // We are disabling the responseLength control when the question type is checkbox.
178+ newQuestion . controls . type . valueChanges . subscribe ( ( v ) =>
179+ v == 'text' || v == 'textarea' ?
180+ newQuestion . include ( 'responseLength' ) : newQuestion . exclude ( 'responseLength' ) ) ;
181+
182+ this . form . controls . questions . push ( newQuestion ) ;
183+ }
184+
185+ deleteQuestion ( index :number ) {
186+ this . form . controls . questions . removeAt ( index ) ;
187+ }
188+
189+ submitForm ( ) {
190+ console . log ( "Submitting a form" )
191+ console . log ( "value" , this . form . value , "valid" , this . form . valid , "errors" , this . form . errors ) ;
192+ }
193+ }
194+
195+ export function main ( ) {
196+ bootstrap ( SurveyBuilder ) ;
197+ }
0 commit comments