Completed
Pull Request — master (#51)
by Bui Quang
02:21
created

form.js ➔ ... ➔ this._reindexSections   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 17
rs 9.4285
cc 1
nc 1
nop 0
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;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

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 (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
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, { 
0 ignored issues
show
Bug introduced by
The variable section_wrapper seems to be never declared. If this is a global, consider adding a /** global: section_wrapper */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
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){
0 ignored issues
show
Unused Code introduced by
The parameter _arr is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
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;