component_registrator.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. /**
  2. * DevExtreme (integration/knockout/component_registrator.js)
  3. * Version: 19.1.16
  4. * Build date: Tue Oct 18 2022
  5. *
  6. * Copyright (c) 2012 - 2022 Developer Express Inc. ALL RIGHTS RESERVED
  7. * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
  8. */
  9. "use strict";
  10. var $ = require("../../core/renderer");
  11. var ko = require("knockout");
  12. var Callbacks = require("../../core/utils/callbacks");
  13. var errors = require("../../core/errors");
  14. var inflector = require("../../core/utils/inflector");
  15. var isPlainObject = require("../../core/utils/type").isPlainObject;
  16. var registerComponentCallbacks = require("../../core/component_registrator_callbacks");
  17. var Widget = require("../../ui/widget/ui.widget");
  18. var KoTemplate = require("./template");
  19. var Editor = require("../../ui/editor/editor");
  20. var Locker = require("../../core/utils/locker");
  21. var getClosestNodeWithContext = require("./utils").getClosestNodeWithContext;
  22. var config = require("../../core/config");
  23. var LOCKS_DATA_KEY = "dxKoLocks";
  24. var CREATED_WITH_KO_DATA_KEY = "dxKoCreation";
  25. var editorsBindingHandlers = [];
  26. var registerComponentKoBinding = function(componentName, componentClass) {
  27. if (componentClass.subclassOf(Editor)) {
  28. editorsBindingHandlers.push(componentName)
  29. }
  30. ko.bindingHandlers[componentName] = {
  31. init: function(domNode, valueAccessor) {
  32. var $element = $(domNode);
  33. var optionChangedCallbacks = Callbacks();
  34. var optionsByReference = {};
  35. var component;
  36. var knockoutConfig = config().knockout;
  37. var isBindingPropertyPredicateName = knockoutConfig && knockoutConfig.isBindingPropertyPredicateName;
  38. var isBindingPropertyPredicate;
  39. var ctorOptions = {
  40. onInitializing: function() {
  41. optionsByReference = this._getOptionsByReference();
  42. ko.computed(function() {
  43. var model = ko.unwrap(valueAccessor());
  44. if (component) {
  45. component.beginUpdate()
  46. }
  47. isBindingPropertyPredicate = isBindingPropertyPredicateName && model && model[isBindingPropertyPredicateName];
  48. unwrapModel(model);
  49. if (component) {
  50. component.endUpdate()
  51. }
  52. }, null, {
  53. disposeWhenNodeIsRemoved: domNode
  54. });
  55. component = this
  56. },
  57. modelByElement: function($element) {
  58. if ($element.length) {
  59. var node = getClosestNodeWithContext($element.get(0));
  60. return ko.dataFor(node)
  61. }
  62. },
  63. nestedComponentOptions: function(component) {
  64. return {
  65. modelByElement: component.option("modelByElement"),
  66. nestedComponentOptions: component.option("nestedComponentOptions")
  67. }
  68. },
  69. _optionChangedCallbacks: optionChangedCallbacks,
  70. integrationOptions: {
  71. watchMethod: function(fn, callback, options) {
  72. options = options || {};
  73. var skipCallback = options.skipImmediate;
  74. var watcher = ko.computed(function() {
  75. var newValue = ko.unwrap(fn());
  76. if (!skipCallback) {
  77. callback(newValue)
  78. }
  79. skipCallback = false
  80. });
  81. return function() {
  82. watcher.dispose()
  83. }
  84. },
  85. templates: {
  86. "dx-polymorph-widget": {
  87. render: function(options) {
  88. var widgetName = ko.utils.unwrapObservable(options.model.widget);
  89. if (!widgetName) {
  90. return
  91. }
  92. if ("button" === widgetName || "tabs" === widgetName || "dropDownMenu" === widgetName) {
  93. var deprecatedName = widgetName;
  94. widgetName = inflector.camelize("dx-" + widgetName);
  95. errors.log("W0001", "dxToolbar - 'widget' item field", deprecatedName, "16.1", "Use: '" + widgetName + "' instead")
  96. }
  97. var markup = $("<div>").attr("data-bind", widgetName + ": options").get(0);
  98. $(options.container).append(markup);
  99. ko.applyBindings(options.model, markup)
  100. }
  101. }
  102. },
  103. createTemplate: function(element) {
  104. return new KoTemplate(element)
  105. }
  106. }
  107. };
  108. var optionNameToModelMap = {};
  109. var applyModelValueToOption = function(optionName, modelValue, unwrap) {
  110. var locks = $element.data(LOCKS_DATA_KEY);
  111. var optionValue = unwrap ? ko.unwrap(modelValue) : modelValue;
  112. if (ko.isWriteableObservable(modelValue)) {
  113. optionNameToModelMap[optionName] = modelValue
  114. }
  115. if (component) {
  116. if (locks.locked(optionName)) {
  117. return
  118. }
  119. locks.obtain(optionName);
  120. try {
  121. if (ko.ignoreDependencies) {
  122. ko.ignoreDependencies(component.option, component, [optionName, optionValue])
  123. } else {
  124. component.option(optionName, optionValue)
  125. }
  126. } finally {
  127. locks.release(optionName)
  128. }
  129. } else {
  130. ctorOptions[optionName] = optionValue
  131. }
  132. };
  133. var handleOptionChanged = function(args) {
  134. var optionName = args.fullName;
  135. var optionValue = args.value;
  136. if (!(optionName in optionNameToModelMap)) {
  137. return
  138. }
  139. var $element = this._$element;
  140. var locks = $element.data(LOCKS_DATA_KEY);
  141. if (locks.locked(optionName)) {
  142. return
  143. }
  144. locks.obtain(optionName);
  145. try {
  146. optionNameToModelMap[optionName](optionValue)
  147. } finally {
  148. locks.release(optionName)
  149. }
  150. };
  151. var createComponent = function() {
  152. optionChangedCallbacks.add(handleOptionChanged);
  153. $element.data(CREATED_WITH_KO_DATA_KEY, true).data(LOCKS_DATA_KEY, new Locker);
  154. new componentClass($element, ctorOptions);
  155. ctorOptions = null
  156. };
  157. var unwrapModelValue = function(currentModel, propertyName, propertyPath) {
  158. if (propertyPath === isBindingPropertyPredicateName) {
  159. return
  160. }
  161. if (!isBindingPropertyPredicate || isBindingPropertyPredicate(propertyPath, propertyName, currentModel)) {
  162. var unwrappedPropertyValue;
  163. ko.computed(function() {
  164. var propertyValue = currentModel[propertyName];
  165. applyModelValueToOption(propertyPath, propertyValue, true);
  166. unwrappedPropertyValue = ko.unwrap(propertyValue)
  167. }, null, {
  168. disposeWhenNodeIsRemoved: domNode
  169. });
  170. if (isPlainObject(unwrappedPropertyValue)) {
  171. if (!optionsByReference[propertyPath]) {
  172. unwrapModel(unwrappedPropertyValue, propertyPath)
  173. }
  174. }
  175. } else {
  176. applyModelValueToOption(propertyPath, currentModel[propertyName], false)
  177. }
  178. };
  179. var unwrapModel = function(model, propertyPath) {
  180. for (var propertyName in model) {
  181. if (Object.prototype.hasOwnProperty.call(model, propertyName)) {
  182. unwrapModelValue(model, propertyName, propertyPath ? [propertyPath, propertyName].join(".") : propertyName)
  183. }
  184. }
  185. };
  186. createComponent();
  187. return {
  188. controlsDescendantBindings: componentClass.subclassOf(Widget)
  189. }
  190. }
  191. };
  192. if ("dxValidator" === componentName) {
  193. ko.bindingHandlers.dxValidator.after = editorsBindingHandlers
  194. }
  195. };
  196. registerComponentCallbacks.add(function(name, componentClass) {
  197. registerComponentKoBinding(name, componentClass)
  198. });