1
|
|
|
import Section from './section.js'; |
2
|
|
|
import Layout from './layout.js'; |
3
|
|
|
import Sortable from 'sortablejs'; |
4
|
|
|
|
5
|
|
|
var Form = function(id) { |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* form variable is created to represent Form instance. |
9
|
|
|
*/ |
10
|
|
|
var form = this; |
11
|
|
|
|
12
|
|
|
/** |
13
|
|
|
* element variable is created to be a master element of the Form, all the variables will be found via element variable |
14
|
|
|
* Eg: element.find('.btn-add') |
15
|
|
|
*/ |
16
|
|
|
var element = $(id); |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* addButton variable is created to represent Add Section Button of the Form. |
20
|
|
|
*/ |
21
|
|
|
var addButton = element.find('#add_section_button'); |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* saveButton variable is created to represent Save Button of the Form. |
25
|
|
|
*/ |
26
|
|
|
var saveButton = element.find('#save_form_button'); |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* sections variable is created to represent all sections exist on the Form. |
30
|
|
|
*/ |
31
|
|
|
var sections = []; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* sectionWrapper variable is created to represent wrapper element of all sections exist on Form. |
35
|
|
|
*/ |
36
|
|
|
var sectionWrapper = element.find('#section_wrapper'); |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* errorWrapper variable is created to represent wrapper element of errors all exist on Form. |
40
|
|
|
*/ |
41
|
|
|
var errorWrapper = element.find('#error_wrapper'); |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* errors variable is created to temporary store all the errors of the Form. |
45
|
|
|
*/ |
46
|
|
|
var errors = []; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* sectionClass variable is created to represent section class, for better readable. |
50
|
|
|
*/ |
51
|
|
|
var sectionClass = '.item-option'; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* layout variable is created to handle all rendering layout stuff for the Form. |
55
|
|
|
*/ |
56
|
|
|
var layout = new Layout(); |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* Initialize actions when create a single instance of Form. |
60
|
|
|
*/ |
61
|
|
|
this.init = function() { |
62
|
|
|
/* Reindex all the sections (if needed). */ |
63
|
|
|
form._reindexSections(); |
64
|
|
|
/* Assign all exist sections to sections variable. */ |
65
|
|
|
form._assignExistSections(); |
66
|
|
|
/* Initialize Sortable JS to reorder form's sections. */ |
67
|
|
|
form._initilizeSortable(); |
68
|
|
|
/* Validate all the data when submit the Form. */ |
69
|
|
|
form._validate(); |
70
|
|
|
/* Bind add new section feature when click on Add Section Button. */ |
71
|
|
|
form._addNewSection(); |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* Add new section actions when user click on Add Section Button. |
76
|
|
|
*/ |
77
|
|
|
this._addNewSection = function() { |
78
|
|
|
|
79
|
|
|
addButton.click(function(e) { |
80
|
|
|
e.preventDefault(); |
81
|
|
|
/* First: count total sections, assign it to a temporary variable totalSection. */ |
82
|
|
|
var totalSection = form._countTotalSections(); |
83
|
|
|
/* Second: disable add button, don't let user click multiple times. */ |
84
|
|
|
form._disableAddButton(); |
85
|
|
|
/* Third: |
86
|
|
|
* Call an AJAX to get a default layout of a section . |
87
|
|
|
* Display the default layout we just received in the bottom of the section wrapper. |
88
|
|
|
* Initialize new section. |
89
|
|
|
* Update Form's section variable. |
90
|
|
|
* Enable add button. |
91
|
|
|
*/ |
92
|
|
|
layout.getDefaultSectionLayout(totalSection + 1).done(function(html){ |
93
|
|
|
|
94
|
|
|
form._addNewLayout(html); |
95
|
|
|
|
96
|
|
|
var section = new Section(totalSection+1, form); |
97
|
|
|
section.init(); |
98
|
|
|
|
99
|
|
|
form._updateSectionVariable(section); |
100
|
|
|
|
101
|
|
|
form._enableAddButton(); |
102
|
|
|
|
103
|
|
|
}); |
104
|
|
|
|
105
|
|
|
|
106
|
|
|
}); |
107
|
|
|
|
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Validate all the data when submit the Form |
112
|
|
|
*/ |
113
|
|
|
this._validate = function() { |
114
|
|
|
|
115
|
|
|
saveButton.click(function(e){ |
116
|
|
|
e.preventDefault(); |
117
|
|
|
|
118
|
|
|
/* Check if form input is valid or not. */ |
119
|
|
|
if(form._validateSections()) { |
120
|
|
|
/* If yes: submit the form. */ |
121
|
|
|
form._submit(); |
122
|
|
|
} else { |
123
|
|
|
/** |
124
|
|
|
* If no: |
125
|
|
|
* Remove all exist errors on FE. |
126
|
|
|
* Add new errors on FE. |
127
|
|
|
* Truncate errors variable after display error on FE. |
128
|
|
|
*/ |
129
|
|
|
form._removeExistErrors(); |
130
|
|
|
form._addNewErrors(); |
131
|
|
|
form._truncateErrorsVariable(); |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
}); |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* Validate data from all the sections |
139
|
|
|
*/ |
140
|
|
|
this._validateSections = function() { |
141
|
|
|
/** |
142
|
|
|
* Create a loop to loop through all exist sections. |
143
|
|
|
* Validate the sections, return false if any section is invalid. |
144
|
|
|
* Store all errors of all sections. |
145
|
|
|
* Truncate the current errors of all sections |
146
|
|
|
*/ |
147
|
|
|
var flag = true; |
148
|
|
|
sections.forEach(function(section){ |
149
|
|
|
if(!section.validate()) |
150
|
|
|
flag = false; |
|
|
|
|
151
|
|
|
form._updateErrors(section.getErrors()); |
152
|
|
|
section.truncateErrors(); |
153
|
|
|
}); |
154
|
|
|
|
155
|
|
|
return flag; |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* Remove all current exits errors on FE. |
160
|
|
|
*/ |
161
|
|
|
this._removeExistErrors = function() { |
162
|
|
|
|
163
|
|
|
errorWrapper.empty(); |
164
|
|
|
|
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
/** |
168
|
|
|
* Truncate errors variables |
169
|
|
|
*/ |
170
|
|
|
this._truncateErrorsVariable = function() { |
171
|
|
|
errors = []; |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
/** |
175
|
|
|
* Add new error on FE |
176
|
|
|
*/ |
177
|
|
|
this._addNewErrors = function() { |
178
|
|
|
/** |
179
|
|
|
* Create a loop to loop through all exist errors. |
180
|
|
|
* For each error, create a list item to display the error. |
181
|
|
|
* Display the new errors in error wrapper on FE. |
182
|
|
|
*/ |
183
|
|
|
/* Get error listing wrapper */ |
184
|
|
|
var errorListingWrapper = layout.getErrorListingWrapperLayout(); |
185
|
|
|
var errorListing = ''; |
186
|
|
|
/* Get error listing */ |
187
|
|
|
errors.forEach(function(error){ |
188
|
|
|
errorListing += layout.getErrorSingleListingLayout(error); |
189
|
|
|
}); |
190
|
|
|
/* Get errors layout by replace markup in error listing wrapper with error listing */ |
191
|
|
|
var errorsLayout = errorListingWrapper.replace('%error-list-markup%', errorListing); |
192
|
|
|
/* Display errors layout */ |
193
|
|
|
errorWrapper.append(errorsLayout); |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
/** |
197
|
|
|
* Update value for errors variable of the Form. |
198
|
|
|
*/ |
199
|
|
|
this._updateErrors = function(error) { |
200
|
|
|
error.forEach(function(err){ |
201
|
|
|
|
202
|
|
|
errors.push(err); |
203
|
|
|
|
204
|
|
|
}); |
205
|
|
|
|
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* Add new layout to the Form |
210
|
|
|
*/ |
211
|
|
|
this._addNewLayout = function(html) { |
212
|
|
|
/* Display the layout passed via parameter to the bottom of section wrapper. */ |
213
|
|
|
$(JSON.parse(html)).appendTo(sectionWrapper); |
214
|
|
|
|
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
/** |
218
|
|
|
* Assign all exist section to section variable |
219
|
|
|
*/ |
220
|
|
|
this._assignExistSections = function() { |
221
|
|
|
|
222
|
|
|
element.find(sectionClass).each(function() { |
223
|
|
|
|
224
|
|
|
var sectionIndex = $(this).attr('data-index'); |
225
|
|
|
var section = new Section(sectionIndex, form); |
226
|
|
|
section.init(); |
227
|
|
|
|
228
|
|
|
form._updateSectionVariable(section); |
229
|
|
|
|
230
|
|
|
}); |
231
|
|
|
|
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
/** |
235
|
|
|
* Initialize Sortable to reorder form's sections. |
236
|
|
|
*/ |
237
|
|
|
this._initilizeSortable = function() { |
238
|
|
|
|
239
|
|
|
Sortable.create(section_wrapper, { |
|
|
|
|
240
|
|
|
|
241
|
|
|
animation: 150, |
242
|
|
|
// Element dragging ended |
243
|
|
|
onEnd: function (evt) { |
244
|
|
|
|
245
|
|
|
var droppedIndex = evt.oldIndex; |
246
|
|
|
var draggedIndex = evt.newIndex; |
247
|
|
|
var indexDifferent = (droppedIndex != draggedIndex); |
248
|
|
|
/* Check if old index is not the same as old index. */ |
249
|
|
|
if(indexDifferent) { |
250
|
|
|
/* Swap two element. */ |
251
|
|
|
form._sortSectionsVariable(); |
252
|
|
|
form._reindexSections(); |
253
|
|
|
|
254
|
|
|
/** |
255
|
|
|
* Check if the draggedIndex or droppedIndex equal to 0. |
256
|
|
|
* It mean that draggedSectionElement or droppedSectionElement is the first section. |
257
|
|
|
* Add delete button on the new first section and remove delete button on the opposite section. |
258
|
|
|
* Bind delete event on the new first section. |
259
|
|
|
*/ |
260
|
|
|
var draggElFirst = (draggedIndex == 0); |
261
|
|
|
var droppElFirst = (droppedIndex == 0); |
262
|
|
|
if(draggElFirst || droppElFirst) { |
263
|
|
|
|
264
|
|
|
if(draggElFirst) { |
265
|
|
|
|
266
|
|
|
sections[draggedIndex].removeDeleteButton(); |
267
|
|
|
sections[droppedIndex].addDeleteButton(); |
268
|
|
|
sections[droppedIndex].onDelete(); |
269
|
|
|
|
270
|
|
|
} else if (droppElFirst) { |
271
|
|
|
|
272
|
|
|
sections[droppedIndex].removeDeleteButton(); |
273
|
|
|
sections[draggedIndex].addDeleteButton(); |
274
|
|
|
sections[draggedIndex].onDelete(); |
275
|
|
|
|
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
}, |
283
|
|
|
|
284
|
|
|
}); |
285
|
|
|
|
286
|
|
|
} |
287
|
|
|
|
288
|
|
|
/** |
289
|
|
|
* Count total number of sections. |
290
|
|
|
*/ |
291
|
|
|
this._countTotalSections = function() { |
292
|
|
|
|
293
|
|
|
return sections.length; |
294
|
|
|
|
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
/** |
298
|
|
|
* Update value for sections variable of the Form. |
299
|
|
|
*/ |
300
|
|
|
this._updateSectionVariable = function(section) { |
301
|
|
|
|
302
|
|
|
sections.push(section); |
303
|
|
|
|
304
|
|
|
} |
305
|
|
|
|
306
|
|
|
/** |
307
|
|
|
* Submit the Form. |
308
|
|
|
*/ |
309
|
|
|
this._submit = function() { |
310
|
|
|
|
311
|
|
|
element.submit(); |
312
|
|
|
|
313
|
|
|
} |
314
|
|
|
|
315
|
|
|
/** |
316
|
|
|
* Remove a section from the Form. |
317
|
|
|
*/ |
318
|
|
|
this.removeSection = function(sectionIndex) { |
319
|
|
|
|
320
|
|
|
/** |
321
|
|
|
* First: we need to remove the section from sections variable. |
322
|
|
|
* Second: we need to reindex all the sections element of the Form (Html). |
323
|
|
|
* Third: we need to reindex all the sections object of the Form (Javascript Object). |
324
|
|
|
*/ |
325
|
|
|
form._rmSectionInSectionsVar(sectionIndex); |
326
|
|
|
form._reindexSections(); |
327
|
|
|
|
328
|
|
|
} |
329
|
|
|
|
330
|
|
|
/** |
331
|
|
|
* Remove a section from sections variable. |
332
|
|
|
*/ |
333
|
|
|
this._rmSectionInSectionsVar = function(sectionIndex) { |
334
|
|
|
|
335
|
|
|
sections.splice(sectionIndex, 1); |
336
|
|
|
|
337
|
|
|
} |
338
|
|
|
|
339
|
|
|
/** |
340
|
|
|
* Check and reindex all sections. |
341
|
|
|
*/ |
342
|
|
|
this._reindexSections = function() { |
343
|
|
|
|
344
|
|
|
/** |
345
|
|
|
* Loop through all exist section element. |
346
|
|
|
* Reindex the section via index of the element. |
347
|
|
|
*/ |
348
|
|
|
sections.forEach(function(section, index, _arr){ |
|
|
|
|
349
|
|
|
|
350
|
|
|
var sectionIndex = index + 1; |
351
|
|
|
var indexDifferent = (sectionIndex != section.getIndex()); |
352
|
|
|
if(indexDifferent) { |
353
|
|
|
section.reindex(sectionIndex); |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
}) |
357
|
|
|
|
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
/** |
361
|
|
|
* Disable add button. |
362
|
|
|
*/ |
363
|
|
|
this._disableAddButton = function() { |
364
|
|
|
|
365
|
|
|
addButton.css('pointer-events', 'none'); |
366
|
|
|
|
367
|
|
|
} |
368
|
|
|
|
369
|
|
|
/** |
370
|
|
|
* Enable add button. |
371
|
|
|
*/ |
372
|
|
|
this._enableAddButton = function() { |
373
|
|
|
|
374
|
|
|
addButton.css('pointer-events', 'visible'); |
375
|
|
|
|
376
|
|
|
} |
377
|
|
|
|
378
|
|
|
/** |
379
|
|
|
* Sort section variable after drag and drop. |
380
|
|
|
*/ |
381
|
|
|
this._sortSectionsVariable = function() { |
382
|
|
|
|
383
|
|
|
var tempSection = []; |
384
|
|
|
element.find(sectionClass).each(function(elementIndex){ |
385
|
|
|
|
386
|
|
|
var sectionElement = $(this); |
387
|
|
|
var oldIndex = sectionElement.attr('data-index') - 1; |
388
|
|
|
var newIndex = elementIndex; |
389
|
|
|
var indexDifferent = (oldIndex != newIndex); |
390
|
|
|
if(indexDifferent) { |
391
|
|
|
tempSection[newIndex] = sections[newIndex]; |
392
|
|
|
if(typeof tempSection[oldIndex] == 'undefined') { |
393
|
|
|
sections[newIndex] = sections[oldIndex]; |
394
|
|
|
} else { |
395
|
|
|
sections[newIndex] = tempSection[oldIndex]; |
396
|
|
|
} |
397
|
|
|
} |
398
|
|
|
|
399
|
|
|
}); |
400
|
|
|
|
401
|
|
|
} |
402
|
|
|
|
403
|
|
|
} |
404
|
|
|
|
405
|
|
|
export default Form; |
Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.
Consider:
If you or someone else later decides to put another statement in, only the first statement will be executed.
In this case the statement
b = 42
will always be executed, while the logging statement will be executed conditionally.ensures that the proper code will be executed conditionally no matter how many statements are added or removed.