row-builder.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. /**
  2. * Copyright © Magento, Inc. All rights reserved.
  3. * See COPYING.txt for license details.
  4. */
  5. /**
  6. * JQuery UI Widget declaration: 'mage.rowBuilder'
  7. *
  8. * @api
  9. */
  10. define([
  11. 'jquery',
  12. 'mage/template',
  13. 'jquery/ui'
  14. ], function ($, mageTemplate) {
  15. 'use strict';
  16. $.widget('mage.rowBuilder', {
  17. /**
  18. * options with default values for setting up the template
  19. */
  20. options: {
  21. //Default template options
  22. rowTemplate: '#template-registrant',
  23. rowContainer: '#registrant-container',
  24. //Row index used by the template rows.
  25. rowIndex: 0,
  26. //Row count: Should not be set externally
  27. rowCount: 0,
  28. rowParentElem: '<li></li>',
  29. rowContainerClass: 'fields',
  30. addRowBtn: '#add-registrant-button',
  31. btnRemoveIdPrefix: 'btn-remove',
  32. btnRemoveSelector: '.btn-remove',
  33. rowIdPrefix: 'row',
  34. //This class is added to rows added after the first one. Adds the dotted separator
  35. additionalRowClass: 'add-row',
  36. /*
  37. This is provided during widget instantiation. eg :
  38. formDataPost : {"formData":formData,"templateFields":['field1-name','field2-name'] }
  39. -"formData" is the multi-dimensional array of form field values : [['a','b'],['c','b']]
  40. received from the server and encoded
  41. -"templateFields" are the input fields in the template with index suffixed after the field name
  42. eg field1-name{index}
  43. */
  44. formDataPost: null,
  45. //Default selectors for add element of a template
  46. addEventSelector: 'button',
  47. //Default selectors for remove markup elements of a template
  48. remEventSelector: 'a',
  49. //This option allows adding first row delete option and a row separator
  50. hideFirstRowAddSeparator: true,
  51. //Max rows - This option should be set when instantiating the widget
  52. maxRows: 1000,
  53. maxRowsMsg: '#max-registrant-message'
  54. },
  55. /**
  56. * Initialize create
  57. * @private
  58. */
  59. _create: function () {
  60. this.rowTemplate = mageTemplate(this.options.rowTemplate);
  61. this.options.rowCount = this.options.rowIndex = 0;
  62. //On document ready related tasks
  63. $($.proxy(this.ready, this));
  64. //Binding template-wide events handlers for adding and removing rows
  65. this.element.on(
  66. 'click',
  67. this.options.addEventSelector + this.options.addRowBtn,
  68. $.proxy(this.handleAdd, this)
  69. );
  70. this.element.on(
  71. 'click',
  72. this.options.remEventSelector + this.options.btnRemoveSelector,
  73. $.proxy(this.handleRemove, this)
  74. );
  75. },
  76. /**
  77. * Initialize template
  78. * @public
  79. */
  80. ready: function () {
  81. if (this.options.formDataPost &&
  82. this.options.formDataPost.formData &&
  83. this.options.formDataPost.formData.length
  84. ) {
  85. this.processFormDataArr(this.options.formDataPost);
  86. } else if (this.options.rowIndex === 0 && this.options.maxRows !== 0) {
  87. //If no form data , then add default row
  88. this.addRow(0);
  89. }
  90. },
  91. /**
  92. * Process and loop through all row data to create preselected values. This is used for any error on submit.
  93. * For complex implementations the inheriting widget can override this behavior
  94. * @public
  95. * @param {Object} formDataArr
  96. */
  97. processFormDataArr: function (formDataArr) {
  98. var formData = formDataArr.formData,
  99. templateFields = formDataArr.templateFields,
  100. formRow,
  101. i, j;
  102. for (i = this.options.rowIndex = 0; i < formData.length; this.options.rowIndex = i++) {
  103. this.addRow(i);
  104. formRow = formData[i];
  105. for (j = 0; j < formRow.length; j++) {
  106. this.setFieldById(templateFields[j] + i, formRow[j]);
  107. }
  108. }
  109. },
  110. /**
  111. * Initialize and create markup for template row. Add it to the parent container.
  112. * The template processing will substitute row index at all places marked with _index_ in the template
  113. * using the template
  114. * @public
  115. * @param {Number} index - current index/count of the created template. This will be used as the id
  116. * @return {*}
  117. */
  118. addRow: function (index) {
  119. var row = $(this.options.rowParentElem),
  120. tmpl;
  121. row.addClass(this.options.rowContainerClass).attr('id', this.options.rowIdPrefix + index);
  122. tmpl = this.rowTemplate({
  123. data: {
  124. _index_: index
  125. }
  126. });
  127. $(tmpl).appendTo(row);
  128. $(this.options.rowContainer).append(row).trigger('contentUpdated');
  129. row.addClass(this.options.additionalRowClass);
  130. //Remove 'delete' link and additionalRowClass for first row
  131. if (this.options.rowIndex === 0 && this.options.hideFirstRowAddSeparator) {
  132. $('#' + this._esc(this.options.btnRemoveIdPrefix) + '0').remove();
  133. $('#' + this._esc(this.options.rowIdPrefix) + '0').removeClass(this.options.additionalRowClass);
  134. }
  135. this.maxRowCheck(++this.options.rowCount);
  136. return row;
  137. },
  138. /**
  139. * Remove return item information row
  140. * @public
  141. * @param {*} rowIndex - return item information row index
  142. * @return {Boolean}
  143. */
  144. removeRow: function (rowIndex) {
  145. $('#' + this._esc(this.options.rowIdPrefix) + rowIndex).remove();
  146. this.maxRowCheck(--this.options.rowCount);
  147. return false;
  148. },
  149. /**
  150. * Function to check if maximum rows are exceeded and render/hide maxMsg and Add btn
  151. * @public
  152. * @param {Number} rowIndex
  153. */
  154. maxRowCheck: function (rowIndex) {
  155. var addRowBtn = $(this.options.addRowBtn),
  156. maxRowMsg = $(this.options.maxRowsMsg);
  157. //liIndex starts from 0
  158. if (rowIndex >= this.options.maxRows) {
  159. addRowBtn.hide();
  160. maxRowMsg.show();
  161. } else if (addRowBtn.is(':hidden')) {
  162. addRowBtn.show();
  163. maxRowMsg.hide();
  164. }
  165. },
  166. /**
  167. * Set the value on given element
  168. * @public
  169. * @param {String} domId
  170. * @param {String} value
  171. */
  172. setFieldById: function (domId, value) {
  173. var x = $('#' + this._esc(domId));
  174. if (x.length) {
  175. if (x.is(':checkbox')) {
  176. x.attr('checked', true);
  177. } else if (x.is('option')) {
  178. x.attr('selected', 'selected');
  179. } else {
  180. x.val(value);
  181. }
  182. }
  183. },
  184. /**
  185. * Delegated handler for adding a row
  186. * @public
  187. * @return {Boolean}
  188. */
  189. handleAdd: function () {
  190. this.addRow(++this.options.rowIndex);
  191. return false;
  192. },
  193. /**
  194. * Delegated handler for removing a selected row
  195. * @public
  196. * @param {Object} e - Native event object
  197. * @return {Boolean}
  198. */
  199. handleRemove: function (e) {
  200. this.removeRow($(e.currentTarget).closest('[id^="' + this.options.btnRemoveIdPrefix + '"]')
  201. .attr('id').replace(this.options.btnRemoveIdPrefix, ''));
  202. return false;
  203. },
  204. /**
  205. * Utility function to add escape chars for jquery selector strings
  206. * @private
  207. * @param {String} str - String to be processed
  208. * @return {String}
  209. */
  210. _esc: function (str) {
  211. return str ? str.replace(/([ ;&,.+*~\':"!\^$\[\]()=>|\/@])/g, '\\$1') : str;
  212. }
  213. });
  214. return $.mage.rowBuilder;
  215. });