]>
git.proxmox.com Git - proxmox-widget-toolkit.git/blob - src/mixin/CBind.js
2 * The Proxmox CBind mixin is intended to supplement the 'bind' mechanism
3 * of ExtJS. In contrast to the 'bind', 'cbind' only acts during the creation
4 * of the component, not during its lifetime. It's only applied once before
5 * the 'initComponent' method is executed, and thus you have only access
6 * to the basic initial configuration of it.
8 * You can use it to get a 'declarative' approach to component declaration,
9 * even when you need to set some properties of sub-components dynamically
10 * (e.g., the 'nodename'). It overwrites the given properties of the 'cbind'
11 * object in the component with their computed values of the computed
12 * cbind configuration object of the 'cbindData' function (or object).
14 * The cbind syntax is inspired by ExtJS' bind syntax ('{property}'), where
15 * it is possible to negate values ('{!negated}'), access sub-properties of
16 * objects ('{object.property}') and even use a getter function,
17 * akin to viewModel formulas ('(get) => get("prop")') to execute more
18 * complicated dependencies (e.g., urls).
20 * The 'cbind' will be recursively applied to all properties (objects/arrays)
21 * that contain an 'xtype' or 'cbind' property, but stops for a subtree if the
22 * object in question does not have either (if you have one or more levels that
23 * have no cbind/xtype property, you can insert empty cbind objects there to
24 * reach deeper nested objects).
26 * This reduces the code in the 'initComponent' and instead we can statically
27 * declare items, buttons, tbars, etc. while the dynamic parts are contained
30 * It is used like in the following example:
32 * Ext.define('Some.Component', {
33 * extend: 'Some.other.Component',
35 * // first it has to be enabled
36 * mixins: ['Proxmox.Mixin.CBind'],
38 * // then a base config has to be defined. this can be a function,
39 * // which has access to the initial config and can store persistent
40 * // properties, as well as return temporary ones (which only exist during
41 * // the cbind process)
42 * // this function will be called before 'initComponent'
43 * cbindData: function(initialconfig) {
44 * // 'this' here is the same as in 'initComponent'
46 * me.persistentProperty = false;
48 * temporaryProperty: true,
52 * // if there is no need for persistent properties, it can also simply be an object
54 * temporaryProperty: true,
55 * // properties itself can also be functions that will be evaluated before
56 * // replacing the values
57 * dynamicProperty: (cfg) => !cfg.temporaryProperty,
65 * // you can 'cbind' the component itself, here the 'target' property
66 * // will be replaced with the content of 'temporaryProperty' (true)
67 * // before the components initComponent
69 * target: '{temporaryProperty}',
76 * value: '{!persistentProperty}',
77 * object: '{objectProp.foo}'
78 * dynamic: (get) => get('numericProp') + 1,
82 * // empty cbind so that subitems are reached
88 * value: '{objectProp.bar}',
97 Ext
.define('Proxmox.Mixin.CBind', {
102 initComponent
: 'cloneTemplates',
106 cloneTemplates: function() {
109 if (typeof me
.cbindData
=== "function") {
110 me
.cbindData
= me
.cbindData(me
.initialConfig
);
112 me
.cbindData
= me
.cbindData
|| {};
114 let getConfigValue = function(cname
) {
115 if (cname
in me
.initialConfig
) {
116 return me
.initialConfig
[cname
];
118 if (cname
in me
.cbindData
) {
119 let res
= me
.cbindData
[cname
];
120 if (typeof res
=== "function") {
121 return res(me
.initialConfig
);
129 throw "unable to get cbind data for '" + cname
+ "'";
132 let applyCBind = function(obj
) {
133 let cbind
= obj
.cbind
, cdata
;
136 for (const prop
in cbind
) { // eslint-disable-line guard-for-in
141 if (typeof cdata
=== 'function') {
142 obj
[prop
] = cdata(getConfigValue
, prop
);
144 } else if ((match
= /^\{(!)?([a-z_][a-z0-9_]*)\}$/i.exec(cdata
))) {
145 let cvalue
= getConfigValue(match
[2]);
146 if (match
[1]) cvalue
= !cvalue
;
149 } else if ((match
= /^\{(!)?([a-z_][a-z0-9_]*(\.[a-z_][a-z0-9_]*)+)\}$/i.exec(cdata
))) {
150 let keys
= match
[2].split('.');
151 let cvalue
= getConfigValue(keys
.shift());
152 keys
.forEach(function(k
) {
156 throw "unable to get cbind data for '" + match
[2] + "'";
159 if (match
[1]) cvalue
= !cvalue
;
163 obj
[prop
] = cdata
.replace(/{([a-z_][a-z0-9_]*)\}/ig, (_match
, cname
) => {
164 let cvalue
= getConfigValue(cname
);
170 throw "unable to parse cbind template '" + cdata
+ "'";
179 let cloneTemplateObject
;
180 let cloneTemplateArray = function(org
) {
181 let copy
, i
, found
, el
, elcopy
, arrayLength
;
183 arrayLength
= org
.length
;
185 for (i
= 0; i
< arrayLength
; i
++) {
187 if (el
.constructor === Object
&& (el
.xtype
|| el
.cbind
)) {
193 if (!found
) return org
; // no need to copy
196 for (i
= 0; i
< arrayLength
; i
++) {
198 if (el
.constructor === Object
&& (el
.xtype
|| el
.cbind
)) {
199 elcopy
= cloneTemplateObject(el
);
204 } else if (el
.constructor === Array
) {
205 elcopy
= cloneTemplateArray(el
);
214 cloneTemplateObject = function(org
) {
215 let res
= {}, prop
, el
, copy
;
216 for (prop
in org
) { // eslint-disable-line guard-for-in
218 if (el
=== undefined || el
=== null) {
222 if (el
.constructor === Object
&& (el
.xtype
|| el
.cbind
)) {
223 copy
= cloneTemplateObject(el
);
228 } else if (el
.constructor === Array
) {
229 copy
= cloneTemplateArray(el
);
238 let condCloneProperties = function() {
241 for (prop
in me
) { // eslint-disable-line guard-for-in
243 if (el
=== undefined || el
=== null) continue;
244 if (typeof el
=== 'object' && el
.constructor === Object
) {
245 if ((el
.xtype
|| el
.cbind
) && prop
!== 'config') {
246 me
[prop
] = cloneTemplateObject(el
);
248 } else if (el
.constructor === Array
) {
249 tmp
= cloneTemplateArray(el
);
255 condCloneProperties();