Issues (105)

product-collection-apply-query-component.js (1 issue)

1
define(function(require) {
2
    'use strict';
3
4
    var ProductCollectionApplyQueryComponent;
5
    var BaseComponent = require('oroui/js/app/components/base/component');
6
    var StandardConfirmation = require('oroui/js/standart-confirmation');
7
    var __ = require('orotranslation/js/translator');
8
    var _ = require('underscore');
9
    var $ = require('jquery');
10
    var mediator = require('oroui/js/mediator');
11
    var InclusionExclusionSubComponent =
12
        require('oroproduct/js/app/components/product-collection-inclusion-exclusion-subcomponent');
13
    var SelectedProductGridSubComponent =
14
        require('oroproduct/js/app/components/product-collection-selected-product-grid-subcomponent');
15
16
    /**
17
     * Perform synchronization between segment definition filters block and grid. By click on "apply the query" button
18
     * will apply the definition filters to the related grid.
19
     */
20
    ProductCollectionApplyQueryComponent = BaseComponent.extend({
21
        /**
22
         * @property {Object}
23
         */
24
        options: {
25
            segmentDefinitionSelectorTemplate: 'input[name="%s"]',
26
            controlsBlockAlias: null,
27
            gridName: null,
28
            scope: null,
29
            excludedControlsBlockAlias: null,
30
            includedControlsBlockAlias: null,
31
            excludedProductsGridName: null,
32
            includedProductsGridName: null,
33
            selectors: {
34
                reset: null,
35
                apply: null,
36
                included: null,
37
                excluded: null
38
            }
39
        },
40
41
        /**
42
         * @property {Object}
43
         */
44
        requiredOptions: [
45
            'segmentDefinitionFieldName',
46
            'controlsBlockAlias',
47
            'gridName',
48
            'scope',
49
            'excludedControlsBlockAlias',
50
            'includedControlsBlockAlias',
51
            'excludedProductsGridName',
52
            'includedProductsGridName'
53
        ],
54
55
        /**
56
         * @property string|null
57
         */
58
        initialDefinitionState: null,
59
60
        /**
61
         * @property string|null
62
         */
63
        initialIncluded: null,
64
65
        /**
66
         * @property string|null
67
         */
68
        initialExcluded: null,
69
70
        /**
71
         * @property string|null
72
         */
73
        currentDefinitionState: null,
74
75
        /**
76
         * @property {Boolean}
77
         */
78
        confirmed: false,
79
80
        /**
81
         * @property {Boolean}
82
         */
83
        confirmModalInitialized: false,
84
85
        /**
86
         * @property {jQuery.Element}
87
         */
88
        $form: null,
89
90
        /**
91
         * @property {jQuery.Element}
92
         */
93
        $included: null,
94
95
        /**
96
         * @property {jQuery.Element}
97
         */
98
        $excluded: null,
99
100
        /**
101
         * @property {String}
102
         */
103
        namespace: null,
104
105
        /**
106
         * @property {Object}
107
         */
108
        inclusionExclusionSubComponent: null,
109
110
        /**
111
         * @property {Object}
112
         */
113
        selectedProductGridSubComponent: null,
114
115
        /**
116
         * @property {String}
117
         */
118
        applyQueryEventName: null,
119
120
        /**
121
         * @inheritDoc
122
         */
123
        constructor: function ProductCollectionApplyQueryComponent() {
124
            ProductCollectionApplyQueryComponent.__super__.constructor.apply(this, arguments);
125
        },
126
127
        /**
128
         * @inheritDoc
129
         */
130
        initialize: function(options) {
131
            this.options = $.extend(true, {}, this.options, options || {});
132
133
            this._checkOptions();
134
135
            this.$included = this.options._sourceElement.find(this.options.selectors.included);
136
            this.$excluded = this.options._sourceElement.find(this.options.selectors.excluded);
137
            this.$form = this.options._sourceElement.closest('form');
138
139
            this.options._sourceElement
140
                .on('click', this.options.selectors.apply, _.bind(this.onApplyQuery, this))
141
                .on('click', this.options.selectors.reset, _.bind(this.onReset, this))
142
                .on('query-designer:validate:not-blank-filters', _.bind(this.onFiltersValidate, this));
143
144
            this.initialDefinitionState = this._getSegmentDefinition();
145
            this.initialIncluded = this.$included.val();
146
            this.initialExcluded = this.$excluded.val();
147
148
            if (this.initialDefinitionState !== null && this.initialDefinitionState !== '') {
149
                this.currentDefinitionState = this.initialDefinitionState;
150
                mediator.on('grid-sidebar:load:' + this.options.controlsBlockAlias, this._applyQuery, this);
151
            }
152
            this.$form.on('submit' + this.eventNamespace(), _.bind(this.onSubmit, this));
153
154
            this.applyQueryEventName = 'productCollection:applyQuery:' + this.eventNamespace();
155
            mediator.on(this.applyQueryEventName, _.bind(this.applyQuery, this));
156
            this._initializeInclusionExclusionSubComponent();
157
            this._initializeSelectedProductGridsSubComponent();
158
159
            this._enableHiddenFieldValidation();
160
        },
161
162
        /**
163
         * @return {String}
164
         */
165
        eventNamespace: function() {
166
            if (this.namespace === null) {
167
                this.namespace = _.uniqueId('.applyQuery');
168
            }
169
170
            return this.namespace;
171
        },
172
173
        /**
174
         * @param {jQuery.Event} event
175
         * @return {Boolean}
176
         */
177
        onSubmit: function(event) {
178
            if (!$(event.target).valid()) {
179
                return true;
180
            }
181
182
            if (!this._isCurrentDefinitionStateChange()) {
183
                return true;
184
            }
185
186
            if (this._isConfirmed()) {
187
                return true;
188
            }
189
190
            event.stopImmediatePropagation();
191
            this._showConfirmModal();
192
193
            return false;
194
        },
195
196
        /**
197
         * @param {jQuery.Event} e
198
         */
199
        onApplyQuery: function(e) {
200
            e.preventDefault();
201
            if (this._isConditionBuilderValid()) {
202
                mediator.trigger(this.applyQueryEventName);
203
            }
204
        },
205
206
        applyQuery: function() {
207
            this.currentDefinitionState = this._getSegmentDefinition();
208
            this._applyQuery(true);
209
        },
210
211
        onConfirmModalOk: function() {
212
            this._setConfirmed();
213
            this.$form.trigger('submit');
214
        },
215
216
        onReset: function(e) {
217
            var filters = this.initialDefinitionState ? JSON.parse(this.initialDefinitionState).filters : [];
218
            this.updateSegmentDefinitionValue('filters', filters);
219
            this.currentDefinitionState = this.initialDefinitionState;
220
            this.$included.val(this.initialIncluded).trigger('change');
221
            this.$excluded.val(this.initialExcluded).trigger('change');
222
            this.onApplyQuery(e);
223
        },
224
225
        /**
226
         * @param {jQuery.Event} e
227
         * @param {Object} data
228
         */
229
        onFiltersValidate: function(e, data) {
230
            var filters = this.fetchSegmentDefinitionValue('filters');
231
            if (!_.isEmpty(filters) || this.$included.val()) {
232
                data.result = true;
233
            }
234
        },
235
236
        _checkOptions: function() {
237
            var requiredMissed = this.requiredOptions.filter(_.bind(function(option) {
238
                return _.isUndefined(this.options[option]);
239
            }, this));
240
            if (requiredMissed.length) {
241
                throw new TypeError('Missing required option(s): ' + requiredMissed.join(', '));
242
            }
243
244
            var requiredSelectors = [];
245
            _.each(this.options.selectors, function(selector, selectorName) {
246
                if (!selector) {
247
                    requiredSelectors.push(selectorName);
248
                }
249
            });
250
            if (requiredSelectors.length) {
251
                throw new TypeError('Missing required selectors(s): ' + requiredSelectors.join(', '));
252
            }
253
        },
254
255
        /**
256
         * @private
257
         */
258
        _applyQuery: function(reload) {
259
            var parameters = {
260
                updateUrl: false,
261
                reload: reload,
262
                params: {}
263
            };
264
265
            parameters.params['sd_' + this.options.gridName] = this.currentDefinitionState;
266
            parameters.params['sd_' + this.options.gridName + ':incl'] = this.$included.val();
267
            parameters.params['sd_' + this.options.gridName + ':excl'] = this.$excluded.val();
268
269
            mediator.trigger('grid-sidebar:change:' + this.options.controlsBlockAlias, parameters);
270
        },
271
272
        /**
273
         * @return {String}
274
         * @private
275
         */
276
        _getSegmentDefinition: function() {
277
            return this._getSegmentDefinitionInput().val();
278
        },
279
280
        _getSegmentDefinitionInput: function() {
281
            var name = this.options.segmentDefinitionFieldName;
282
            return $(this.options.segmentDefinitionSelectorTemplate.replace('%s', name));
283
        },
284
285
        /**
286
         * Loads data from the segment definition input
287
         *
288
         * @param {string=} key name of data branch
289
         */
290
        fetchSegmentDefinitionValue: function(key) {
291
            var data = {};
292
            var json = this._getSegmentDefinitionInput().val();
293
            if (json) {
294
                try {
295
                    data = JSON.parse(json);
296
                } catch (e) {
297
                    return undefined;
298
                }
299
            }
300
            return key ? data[key] : data;
301
        },
302
303
        /**
304
         * Saves data to the segment definition input
305
         *
306
         * @param {Object|string} value data if single argument is passed or key name of data branch
307
         * @param {Object=} value data for data branch
0 ignored issues
show
The parameter value has already been documented on line 306. The second definition is ignored.
Loading history...
308
         */
309
        updateSegmentDefinitionValue: function(value) {
310
            var key;
311
            var data = this.fetchSegmentDefinitionValue();
312
            if (arguments.length === 2) {
313
                key = value;
314
                value = arguments[1];
315
                data[key] = value;
316
            } else {
317
                data = value;
318
            }
319
            this._getSegmentDefinitionInput().val(JSON.stringify(data)).trigger('change');
320
        },
321
322
        /**
323
         * @return {Boolean}
324
         * @private
325
         */
326
        _isCurrentDefinitionStateChange: function() {
327
            return this.currentDefinitionState !== this._getSegmentDefinition();
328
        },
329
330
        /**
331
         * @private
332
         */
333
        _setConfirmed: function() {
334
            this.$form.data('apply-query-confirmed', true);
335
        },
336
337
        /**
338
         * @return {Boolean}
339
         * @private
340
         */
341
        _isConfirmed: function() {
342
            return this.$form.data('apply-query-confirmed');
343
        },
344
345
        /**
346
         * @private
347
         */
348
        _showConfirmModal: function() {
349
            if (!this.confirmModalInitialized) {
350
                if (!this.$form.data('productCollectionApplyQueryModal')) {
351
                    this.$form.data(
352
                        'productCollectionApplyQueryModal',
353
                        new StandardConfirmation({
354
                            content: __('oro.product.product_collection.filter_query.confirmation_modal_content'),
355
                            okText: __('oro.product.product_collection.filter_query.continue')
356
                        })
357
                    );
358
                }
359
                this.$form.data('productCollectionApplyQueryModal').on('ok', _.bind(this.onConfirmModalOk, this));
360
                this.confirmModalInitialized = true;
361
            }
362
363
            this.$form.data('productCollectionApplyQueryModal').open();
364
        },
365
366
        /**
367
         * @private
368
         */
369
        _initializeInclusionExclusionSubComponent: function() {
370
            var options = {
371
                _sourceElement: this.options._sourceElement,
372
                scope: this.options.scope,
373
                selectors: {
374
                    included: this.options.selectors.included,
375
                    excluded: this.options.selectors.excluded
376
                }
377
            };
378
            this.inclusionExclusionSubComponent = new InclusionExclusionSubComponent(options);
379
        },
380
381
        /**
382
         * @private
383
         */
384
        _initializeSelectedProductGridsSubComponent: function() {
385
            var options = {
386
                _sourceElement: this.options._sourceElement,
387
                applyQueryEventName: this.applyQueryEventName,
388
                excludedControlsBlockAlias: this.options.excludedControlsBlockAlias,
389
                includedControlsBlockAlias: this.options.includedControlsBlockAlias,
390
                excludedProductsGridName: this.options.excludedProductsGridName,
391
                includedProductsGridName: this.options.includedProductsGridName,
392
                selectors: {
393
                    included: this.options.selectors.included,
394
                    excluded: this.options.selectors.excluded
395
                }
396
            };
397
            this.selectedProductGridSubComponent = new SelectedProductGridSubComponent(options);
398
        },
399
400
        /**
401
         * @return {Boolean}
402
         * @private
403
         */
404
        _isConditionBuilderValid: function() {
405
            var $form = this.$form;
406
            if (!$form.data('validator')) {
407
                return true;
408
            }
409
410
            $form.valid();
411
412
            var invalidElements = $form.validate().invalidElements();
413
            if (!invalidElements.length) {
414
                return true;
415
            }
416
417
            var $conditionBuilder = this.options._sourceElement.find('.condition-builder');
418
            var conditionBuilderInvalidElements = _.filter(invalidElements, _.bind(function(value) {
419
                return $.contains($conditionBuilder[0], value);
420
            }, this));
421
422
            return !conditionBuilderInvalidElements.length;
423
        },
424
425
        /**
426
         * If conditionBuilderView located in oro-tabs, change form's setting in order to validate hidden fields too.
427
         * Because of it can be hidden.
428
         *
429
         * @private
430
         */
431
        _enableHiddenFieldValidation: function() {
432
            var $form = this.$form;
433
            if ($form.data('validator')) {
434
                $form.validate()
435
                    .settings
436
                    .ignore = ':hidden:not([type=hidden]):not(:parent.' + this.options.controlsBlockAlias + ')';
437
            }
438
        },
439
440
        dispose: function() {
441
            if (this.disposed) {
442
                return;
443
            }
444
445
            if (this.$form.data('productCollectionApplyQueryModal')) {
446
                this.$form.data('productCollectionApplyQueryModal').off('ok', _.bind(this.onConfirmModalOk, this));
447
            }
448
            this.$form.off(this.eventNamespace());
449
            mediator.off('grid-sidebar:load:' + this.options.controlsBlockAlias);
450
            mediator.off(this.applyQueryEventName);
451
452
            ProductCollectionApplyQueryComponent.__super__.dispose.call(this);
453
        }
454
    });
455
456
    return ProductCollectionApplyQueryComponent;
457
});
458