rasti 3.0.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/rasti.js +122 -92
- package/dist/rasti.min.js +1 -1
- package/es/Component.js +98 -68
- package/es/View.js +24 -24
- package/lib/Component.cjs +98 -68
- package/lib/View.cjs +24 -24
- package/package.json +1 -1
- package/src/Component.js +98 -68
- package/src/View.js +24 -24
package/src/Component.js
CHANGED
|
@@ -2,8 +2,12 @@ import View from './View.js';
|
|
|
2
2
|
import getResult from './utils/getResult.js';
|
|
3
3
|
import deepFlat from './utils/deepFlat.js';
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
6
|
* Wrapper class for HTML strings marked as safe.
|
|
7
|
+
* @class SafeHTML
|
|
8
|
+
* @param {string} value The HTML string to be marked as safe.
|
|
9
|
+
* @property {string} value The HTML string.
|
|
10
|
+
* @private
|
|
7
11
|
*/
|
|
8
12
|
class SafeHTML {
|
|
9
13
|
constructor(value) {
|
|
@@ -15,20 +19,22 @@ class SafeHTML {
|
|
|
15
19
|
}
|
|
16
20
|
}
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
/**
|
|
19
23
|
* Same as getResult, but pass context as argument to the expression.
|
|
20
24
|
* Used to evaluate expressions in the context of a component.
|
|
21
25
|
* @param {any} expression The expression to be evaluated.
|
|
22
26
|
* @param {any} context The context to call the expression with.
|
|
23
27
|
* @return {any} The result of the evaluated expression.
|
|
28
|
+
* @private
|
|
24
29
|
*/
|
|
25
30
|
const getExpressionResult = (expression, context) => getResult(expression, context, context);
|
|
26
31
|
|
|
27
|
-
|
|
32
|
+
/**
|
|
28
33
|
* Generate string with placeholders for interpolated expressions.
|
|
29
34
|
* @param strings {array} Array of strings.
|
|
30
35
|
* @param expressions {array} Array of expressions.
|
|
31
36
|
* @return {string} String with placeholders.
|
|
37
|
+
* @private
|
|
32
38
|
*/
|
|
33
39
|
const addPlaceholders = (strings, expressions) =>
|
|
34
40
|
strings.reduce((out, string, i) => {
|
|
@@ -41,11 +47,12 @@ const addPlaceholders = (strings, expressions) =>
|
|
|
41
47
|
return out;
|
|
42
48
|
}, []).join('');
|
|
43
49
|
|
|
44
|
-
|
|
50
|
+
/**
|
|
45
51
|
* Generate one dimensional array with strings and expressions.
|
|
46
52
|
* @param main {string} The main template containing placeholders.
|
|
47
53
|
* @param expressions {array} Array of expressions to replace placeholders.
|
|
48
54
|
* @return {array} Array containing strings and expressions.
|
|
55
|
+
* @private
|
|
49
56
|
*/
|
|
50
57
|
const splitPlaceholders = (main, expressions) => {
|
|
51
58
|
const PH = Component.PLACEHOLDER_EXPRESSION('(\\d+)');
|
|
@@ -65,7 +72,7 @@ const splitPlaceholders = (main, expressions) => {
|
|
|
65
72
|
return out;
|
|
66
73
|
};
|
|
67
74
|
|
|
68
|
-
|
|
75
|
+
/**
|
|
69
76
|
* Expand attributes.
|
|
70
77
|
* @param attributes {array} Array of attributes as key, value pairs.
|
|
71
78
|
* @param getExpressionResult {function} Function to render expressions.
|
|
@@ -73,6 +80,7 @@ const splitPlaceholders = (main, expressions) => {
|
|
|
73
80
|
* @property {object} all All attributes.
|
|
74
81
|
* @property {object} events Event listeners.
|
|
75
82
|
* @property {object} attributes Attributes.
|
|
83
|
+
* @private
|
|
76
84
|
*/
|
|
77
85
|
const expandAttributes = (attributes, getExpressionResult) => {
|
|
78
86
|
const out = attributes.reduce((out, pair) => {
|
|
@@ -110,7 +118,7 @@ const expandAttributes = (attributes, getExpressionResult) => {
|
|
|
110
118
|
return out;
|
|
111
119
|
};
|
|
112
120
|
|
|
113
|
-
|
|
121
|
+
/**
|
|
114
122
|
* Replace component tags with expressions.
|
|
115
123
|
* `<${Component} />` or `<${Component}></${Component}>` will be replaced
|
|
116
124
|
* by a function that mounts the component.
|
|
@@ -119,6 +127,7 @@ const expandAttributes = (attributes, getExpressionResult) => {
|
|
|
119
127
|
* @param main {string} The main template.
|
|
120
128
|
* @return {string} The template with components tags replaced by expressions
|
|
121
129
|
* placeholders.
|
|
130
|
+
* @private
|
|
122
131
|
*/
|
|
123
132
|
const expandComponents = (main, expressions) => {
|
|
124
133
|
const PH = Component.PLACEHOLDER_EXPRESSION('(\\d+)');
|
|
@@ -158,7 +167,7 @@ const expandComponents = (main, expressions) => {
|
|
|
158
167
|
);
|
|
159
168
|
};
|
|
160
169
|
|
|
161
|
-
|
|
170
|
+
/**
|
|
162
171
|
* Parse match data to get tag, attributes, inner html and close tag.
|
|
163
172
|
* @param match {array}
|
|
164
173
|
* @return {object}
|
|
@@ -167,6 +176,7 @@ const expandComponents = (main, expressions) => {
|
|
|
167
176
|
* @property {string} close The closing tag.
|
|
168
177
|
* @property {array} attributes Array of attributes as key, value pairs.
|
|
169
178
|
* @property {string} raw The whole match.
|
|
179
|
+
* @private
|
|
170
180
|
*/
|
|
171
181
|
const parseMatch = (match, expressions) => {
|
|
172
182
|
const PH = Component.PLACEHOLDER_EXPRESSION('(\\d+)');
|
|
@@ -219,13 +229,7 @@ const selfClosingTags = {
|
|
|
219
229
|
/*
|
|
220
230
|
* These option keys will be extended on the component instance.
|
|
221
231
|
*/
|
|
222
|
-
const componentOptions =
|
|
223
|
-
key : true,
|
|
224
|
-
state : true,
|
|
225
|
-
onCreate : true,
|
|
226
|
-
onChange : true,
|
|
227
|
-
onRender : true
|
|
228
|
-
};
|
|
232
|
+
const componentOptions = ['key', 'state', 'onCreate', 'onChange', 'onRender'];
|
|
229
233
|
|
|
230
234
|
/**
|
|
231
235
|
* Components are a special kind of `View` that is designed to be easily composable,
|
|
@@ -258,9 +262,9 @@ const componentOptions = {
|
|
|
258
262
|
export default class Component extends View {
|
|
259
263
|
constructor(options = {}) {
|
|
260
264
|
super(...arguments);
|
|
261
|
-
// Extend "this" with options
|
|
262
|
-
|
|
263
|
-
if (
|
|
265
|
+
// Extend "this" with options.
|
|
266
|
+
componentOptions.forEach(key => {
|
|
267
|
+
if (key in options) this[key] = options[key];
|
|
264
268
|
});
|
|
265
269
|
// Store options by default.
|
|
266
270
|
this.options = options;
|
|
@@ -295,20 +299,22 @@ export default class Component extends View {
|
|
|
295
299
|
return this;
|
|
296
300
|
}
|
|
297
301
|
|
|
298
|
-
|
|
299
|
-
* Tell if
|
|
302
|
+
/**
|
|
303
|
+
* Tell if `Component` is a container.
|
|
300
304
|
* In which case, it will not have an element by itself.
|
|
301
305
|
* It will render a single expression which is expected to return a single component as child.
|
|
302
306
|
* `this.el` will be a reference to that child component's element.
|
|
303
307
|
* @return {boolean}
|
|
308
|
+
* @private
|
|
304
309
|
*/
|
|
305
310
|
isContainer() {
|
|
306
311
|
return !!(!this.tag && this.template);
|
|
307
312
|
}
|
|
308
313
|
|
|
309
|
-
|
|
310
|
-
* Override. We don't want to ensure an element on instantiation.
|
|
314
|
+
/**
|
|
315
|
+
* Override super method. We don't want to ensure an element on instantiation.
|
|
311
316
|
* We will provide it later.
|
|
317
|
+
* @private
|
|
312
318
|
*/
|
|
313
319
|
ensureElement() {
|
|
314
320
|
// If el is provided, delegate events.
|
|
@@ -319,18 +325,25 @@ export default class Component extends View {
|
|
|
319
325
|
}
|
|
320
326
|
}
|
|
321
327
|
|
|
322
|
-
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
* @
|
|
328
|
+
/**
|
|
329
|
+
* Locate the root element of the `Component` within a specified parent node.
|
|
330
|
+
* This is achieved by searching for the element using the unique data attribute assigned to the `Component`.
|
|
331
|
+
* @param {Node} parent - The parent node to search within.
|
|
332
|
+
* @return {Node} The root element of the component, or `null` if not found.
|
|
333
|
+
* @private
|
|
326
334
|
*/
|
|
327
335
|
findElement(parent) {
|
|
328
336
|
return (parent || document).querySelector(`[${Component.DATA_ATTRIBUTE_UID}="${this.uid}"]`);
|
|
329
337
|
}
|
|
330
338
|
|
|
331
|
-
|
|
332
|
-
*
|
|
333
|
-
*
|
|
339
|
+
/**
|
|
340
|
+
* Retrieve the attributes to be applied to the element.
|
|
341
|
+
* This includes attributes to be added, removed, and their HTML representation.
|
|
342
|
+
* @return {object} An object containing the following properties:
|
|
343
|
+
* @property {object} add - Attributes to be added to the element, with their values.
|
|
344
|
+
* @property {object} remove - Attributes to be removed from the element.
|
|
345
|
+
* @property {string} html - A string representation of the attributes for use in HTML.
|
|
346
|
+
* @private
|
|
334
347
|
*/
|
|
335
348
|
getAttributes() {
|
|
336
349
|
const add = {};
|
|
@@ -369,11 +382,13 @@ export default class Component extends View {
|
|
|
369
382
|
return { add, remove, html : html.join(' ') };
|
|
370
383
|
}
|
|
371
384
|
|
|
372
|
-
|
|
385
|
+
/**
|
|
373
386
|
* Used internally on the render process.
|
|
374
|
-
* Attach the
|
|
387
|
+
* Attach the `Component` to the dom element providing `this.el`, delegate events,
|
|
388
|
+
* subscribe to model changes and call `onRender` lifecycle method with `Component.RENDER_TYPE_HYDRATE` as argument.
|
|
375
389
|
* @param parent {node} The parent node.
|
|
376
390
|
* @return {Rasti.Component} The component instance.
|
|
391
|
+
* @private
|
|
377
392
|
*/
|
|
378
393
|
hydrate(parent) {
|
|
379
394
|
// Listen to model changes and call onChange.
|
|
@@ -381,13 +396,13 @@ export default class Component extends View {
|
|
|
381
396
|
// Listen to state changes and call onChange.
|
|
382
397
|
if (this.state) this.subscribe(this.state);
|
|
383
398
|
|
|
384
|
-
if (
|
|
399
|
+
if (this.isContainer()) {
|
|
400
|
+
this.children[0].hydrate(parent);
|
|
401
|
+
this.el = this.children[0].el;
|
|
402
|
+
} else {
|
|
385
403
|
this.el = this.findElement(parent);
|
|
386
404
|
this.delegateEvents();
|
|
387
405
|
this.children.forEach(child => child.hydrate(this.el));
|
|
388
|
-
} else {
|
|
389
|
-
this.children[0].hydrate(parent);
|
|
390
|
-
this.el = this.children[0].el;
|
|
391
406
|
}
|
|
392
407
|
// Call `onRender` lifecycle method.
|
|
393
408
|
this.onRender.call(this, Component.RENDER_TYPE_HYDRATE);
|
|
@@ -395,30 +410,38 @@ export default class Component extends View {
|
|
|
395
410
|
return this;
|
|
396
411
|
}
|
|
397
412
|
|
|
398
|
-
|
|
399
|
-
* Used internally
|
|
400
|
-
* Reuse a
|
|
413
|
+
/**
|
|
414
|
+
* Used internally on the render process.
|
|
415
|
+
* Reuse a `Component` that has `key` when its parent is rendered.
|
|
416
|
+
* Call `onRender` lifecycle method with `Component.RENDER_TYPE_RECYCLE` as argument.
|
|
401
417
|
* @param parent {node} The parent node.
|
|
402
418
|
* @return {Rasti.Component} The component instance.
|
|
419
|
+
* @private
|
|
403
420
|
*/
|
|
404
421
|
recycle(parent) {
|
|
405
422
|
// If component is a container, call recycle on its child.
|
|
406
|
-
if (this.isContainer())
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
423
|
+
if (this.isContainer()) {
|
|
424
|
+
this.children[0].recycle(parent);
|
|
425
|
+
} else {
|
|
426
|
+
// Find placeholder element to be replaced. It has same data attribute as this component.
|
|
427
|
+
const toBeReplaced = this.findElement(parent);
|
|
428
|
+
// Replace it with this.el.
|
|
429
|
+
toBeReplaced.replaceWith(this.el);
|
|
430
|
+
}
|
|
411
431
|
// Call `onRender` lifecycle method.
|
|
412
432
|
this.onRender.call(this, Component.RENDER_TYPE_RECYCLE);
|
|
413
433
|
// Return `this` for chaining.
|
|
414
434
|
return this;
|
|
415
435
|
}
|
|
416
436
|
|
|
417
|
-
|
|
418
|
-
*
|
|
437
|
+
/**
|
|
438
|
+
* Destroy the `Component`.
|
|
439
|
+
* Destroy children components if any, undelegate events, stop listening to events, call `onDestroy` lifecycle method.
|
|
419
440
|
* @param {object} options Options object or any arguments passed to `destroy` method will be passed to `onDestroy` method.
|
|
441
|
+
* @return {Rasti.View} Return `this` for chaining.
|
|
420
442
|
*/
|
|
421
443
|
destroy() {
|
|
444
|
+
// Call super destroy method.
|
|
422
445
|
super.destroy.apply(this, arguments);
|
|
423
446
|
// Set destroyed flag to prevent a last render after destroyed.
|
|
424
447
|
this.destroyed = true;
|
|
@@ -447,8 +470,11 @@ export default class Component extends View {
|
|
|
447
470
|
}
|
|
448
471
|
|
|
449
472
|
/**
|
|
450
|
-
* Lifecycle method. Called
|
|
451
|
-
*
|
|
473
|
+
* Lifecycle method. Called after the component is rendered.
|
|
474
|
+
* - When the component is rendered for the first time, this method is called with `Component.RENDER_TYPE_HYDRATE` as the argument.
|
|
475
|
+
* - When the component is updated or re-rendered, this method is called with `Component.RENDER_TYPE_RENDER` as the argument.
|
|
476
|
+
* - When the component is recycled (reused with the same key), this method is called with `Component.RENDER_TYPE_RECYCLE` as the argument.
|
|
477
|
+
* @param {string} type - The render type. Possible values are: `Component.RENDER_TYPE_HYDRATE`, `Component.RENDER_TYPE_RENDER` and `Component.RENDER_TYPE_RECYCLE`.
|
|
452
478
|
*/
|
|
453
479
|
onRender() {}
|
|
454
480
|
|
|
@@ -530,22 +556,26 @@ export default class Component extends View {
|
|
|
530
556
|
`<${tag} ${attributes} />`;
|
|
531
557
|
}
|
|
532
558
|
|
|
533
|
-
|
|
534
|
-
*
|
|
559
|
+
/**
|
|
560
|
+
* Render the `Component`.
|
|
561
|
+
* - If `this.el` is not present, the `Component` will be rendered as a string inside a `DocumentFragment` and hydrated, making `this.el` available. The `onRender` lifecycle method will be called with `Component.RENDER_TYPE_HYDRATE` as an argument.
|
|
562
|
+
* - If `this.el` is present, the method will update the attributes and inner HTML of the element, or recreate its child component in the case of a container. The `onRender` lifecycle method will be called with `Component.RENDER_TYPE_RENDER` as an argument.
|
|
563
|
+
* - When rendering child components, if the new children have the same key as the previous ones, they will be recycled. A recycled `Component` will call the `onRender` lifecycle method with `Component.RENDER_TYPE_RECYCLE` as an argument.
|
|
564
|
+
* - If the active element is inside the component, it will retain focus after the render.
|
|
565
|
+
* @return {Rasti.Component} The component instance.
|
|
535
566
|
*/
|
|
536
567
|
render() {
|
|
537
568
|
// Prevent a last re render if view is already destroyed.
|
|
538
569
|
if (this.destroyed) return this;
|
|
539
|
-
|
|
570
|
+
// If `this.el` is not present, render the view as a string and hydrate it.
|
|
571
|
+
if (!this.el) {
|
|
572
|
+
const fragment = this.createElement('template');
|
|
573
|
+
fragment.innerHTML = this;
|
|
574
|
+
this.hydrate(fragment.content);
|
|
575
|
+
return this;
|
|
576
|
+
}
|
|
577
|
+
// Update attributes.
|
|
540
578
|
if (!this.isContainer()) {
|
|
541
|
-
// If `this.el` is not present, render the view as a string and hydrate it.
|
|
542
|
-
if (!this.el) {
|
|
543
|
-
const fragment = this.createElement('template');
|
|
544
|
-
fragment.innerHTML = this;
|
|
545
|
-
this.hydrate(fragment.content);
|
|
546
|
-
return this;
|
|
547
|
-
}
|
|
548
|
-
// Set `this.el` attributes.
|
|
549
579
|
const attributes = this.getAttributes();
|
|
550
580
|
// Remove attributes.
|
|
551
581
|
Object.keys(attributes.remove).forEach(key => {
|
|
@@ -556,7 +586,7 @@ export default class Component extends View {
|
|
|
556
586
|
this.el.setAttribute(key, attributes.add[key]);
|
|
557
587
|
});
|
|
558
588
|
}
|
|
559
|
-
// Check for `template` to see if view has innerHTML.
|
|
589
|
+
// Check for `template` to see if view has innerHTML or a child component.
|
|
560
590
|
if (this.template) {
|
|
561
591
|
// Store active element.
|
|
562
592
|
const activeElement = document.activeElement;
|
|
@@ -597,8 +627,8 @@ export default class Component extends View {
|
|
|
597
627
|
this.addChild(nextChildren[0]).hydrate(fragment.content);
|
|
598
628
|
// Get next child element.
|
|
599
629
|
const nextEl = fragment.content.children[0];
|
|
600
|
-
//
|
|
601
|
-
|
|
630
|
+
// Replace `this.el` with nextEl.
|
|
631
|
+
this.el.replaceWith(nextEl);
|
|
602
632
|
// Set `this.el` to nextEl.
|
|
603
633
|
this.el = nextEl;
|
|
604
634
|
} else if (recycledChildren[0]) {
|
|
@@ -634,10 +664,10 @@ export default class Component extends View {
|
|
|
634
664
|
}
|
|
635
665
|
|
|
636
666
|
/**
|
|
637
|
-
* Mark a string as safe HTML to be rendered.
|
|
638
|
-
* Normally you don't need to use this method, as Rasti will automatically mark
|
|
639
|
-
* as safe HTML when the component is @link
|
|
640
|
-
* using the @link
|
|
667
|
+
* Mark a string as safe HTML to be rendered.
|
|
668
|
+
* Normally you don't need to use this method, as Rasti will automatically mark string literals
|
|
669
|
+
* as safe HTML when the component is {@link #module_component_create created} and when
|
|
670
|
+
* using the {@link #module_component__partial Component.partial} method.
|
|
641
671
|
* Be sure that the string is safe to be rendered, as it will be inserted into the DOM without any sanitization.
|
|
642
672
|
* @static
|
|
643
673
|
* @param {string} value
|
|
@@ -751,8 +781,8 @@ export default class Component extends View {
|
|
|
751
781
|
* // Create a navigation component. Add buttons as children. Iterate over items.
|
|
752
782
|
* const Navigation = Component.create`
|
|
753
783
|
* <nav>
|
|
754
|
-
* ${
|
|
755
|
-
* item =>
|
|
784
|
+
* ${({ options, partial }) => options.items.map(
|
|
785
|
+
* item => partial`<${Button}>${item.label}</${Button}>`
|
|
756
786
|
* )}
|
|
757
787
|
* </nav>
|
|
758
788
|
* `;
|
|
@@ -771,11 +801,11 @@ export default class Component extends View {
|
|
|
771
801
|
* ${self => self.renderChildren()}
|
|
772
802
|
* </button>
|
|
773
803
|
* `;
|
|
774
|
-
* // Create a container
|
|
804
|
+
* // Create a container that renders a Button component.
|
|
775
805
|
* const ButtonOk = Component.create`
|
|
776
806
|
* <${Button} className="ok">Ok</${Button}>
|
|
777
807
|
* `;
|
|
778
|
-
* // Create a
|
|
808
|
+
* // Create a container that renders a Button component, using a function.
|
|
779
809
|
* const ButtonCancel = Component.create(() => Button.mount({
|
|
780
810
|
* className: 'cancel',
|
|
781
811
|
* renderChildren: () => 'Cancel'
|
package/src/View.js
CHANGED
|
@@ -4,15 +4,7 @@ import getResult from './utils/getResult.js';
|
|
|
4
4
|
/*
|
|
5
5
|
* These option keys will be extended on the view instance.
|
|
6
6
|
*/
|
|
7
|
-
const viewOptions =
|
|
8
|
-
el : true,
|
|
9
|
-
tag : true,
|
|
10
|
-
attributes : true,
|
|
11
|
-
events : true,
|
|
12
|
-
model : true,
|
|
13
|
-
template : true,
|
|
14
|
-
onDestroy : true
|
|
15
|
-
};
|
|
7
|
+
const viewOptions = ['el', 'tag', 'attributes', 'events', 'model', 'template', 'onDestroy'];
|
|
16
8
|
|
|
17
9
|
/**
|
|
18
10
|
* - Listens for changes and renders the UI.
|
|
@@ -35,6 +27,7 @@ const viewOptions = {
|
|
|
35
27
|
* @property {object|function} events Object in the format `{'event selector' : 'listener'}`. It will be used to bind delegated event listeners to the root element. If it is a function, it will be called to get the events object, bound to the view instance. See {@link module_view_delegateevents View.delegateEvents}.
|
|
36
28
|
* @property {object} model A model or any object containing data and business logic.
|
|
37
29
|
* @property {function} template A function that returns a string with the view's inner HTML. See {@link module_view__render View.render}.
|
|
30
|
+
* @property {number} uid Unique identifier for the view instance. This can be used to generate unique IDs for elements within the view. It is automatically generated and should not be set manually.
|
|
38
31
|
* @example
|
|
39
32
|
* import { View } from 'rasti';
|
|
40
33
|
*
|
|
@@ -63,7 +56,7 @@ export default class View extends Emitter {
|
|
|
63
56
|
this.preinitialize.apply(this, arguments);
|
|
64
57
|
// Generate unique id.
|
|
65
58
|
// Useful to generate element ids.
|
|
66
|
-
this.uid = `
|
|
59
|
+
this.uid = `rasti-${++View.uid}`;
|
|
67
60
|
// Store delegated event listeners,
|
|
68
61
|
// so they can be unbound later.
|
|
69
62
|
this.delegatedEventListeners = [];
|
|
@@ -72,9 +65,9 @@ export default class View extends Emitter {
|
|
|
72
65
|
this.children = [];
|
|
73
66
|
// Mutable array to store handlers to be called on destroy.
|
|
74
67
|
this.destroyQueue = [];
|
|
75
|
-
// Extend "this" with options
|
|
76
|
-
|
|
77
|
-
if (
|
|
68
|
+
// Extend "this" with options.
|
|
69
|
+
viewOptions.forEach(key => {
|
|
70
|
+
if (key in options) this[key] = options[key];
|
|
78
71
|
});
|
|
79
72
|
// Ensure that the view has a root element at `this.el`.
|
|
80
73
|
this.ensureElement();
|
|
@@ -303,17 +296,16 @@ export default class View extends Emitter {
|
|
|
303
296
|
}
|
|
304
297
|
|
|
305
298
|
/**
|
|
306
|
-
* Renders the view.
|
|
299
|
+
* Renders the view.
|
|
307
300
|
* This method should be overridden with custom logic.
|
|
308
301
|
* The only convention is to manipulate the DOM within the scope of `this.el`,
|
|
309
|
-
* and to return `this` for chaining.
|
|
310
|
-
* If you add any child views, you should call `this.destroyChildren` before re-rendering.
|
|
311
|
-
* The default implementation
|
|
312
|
-
* of calling `this.template`, passing `this.model` as
|
|
313
|
-
* <br><br> ⚠ **Security Notice:** The default implementation utilizes `innerHTML
|
|
314
|
-
*
|
|
315
|
-
*
|
|
316
|
-
* static method to escape HTML entities in a string.
|
|
302
|
+
* and to return `this` for chaining.
|
|
303
|
+
* If you add any child views, you should call `this.destroyChildren` before re-rendering.
|
|
304
|
+
* The default implementation updates `this.el`'s innerHTML with the result
|
|
305
|
+
* of calling `this.template`, passing `this.model` as the argument.
|
|
306
|
+
* <br><br> ⚠ **Security Notice:** The default implementation utilizes `innerHTML`, which may introduce Cross-Site Scripting (XSS) risks.
|
|
307
|
+
* Ensure that any user-generated content is properly sanitized before inserting it into the DOM.
|
|
308
|
+
* You can use the {@link #module_view_sanitize View.sanitize} static method to escape HTML entities in a string.
|
|
317
309
|
* For best practices on secure data handling, refer to the
|
|
318
310
|
* [OWASP's XSS Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html).<br><br>
|
|
319
311
|
* @return {Rasti.View} Returns `this` for chaining.
|
|
@@ -344,7 +336,15 @@ export default class View extends Emitter {
|
|
|
344
336
|
}
|
|
345
337
|
}
|
|
346
338
|
|
|
347
|
-
|
|
348
|
-
*
|
|
339
|
+
/**
|
|
340
|
+
* Counter for generating unique IDs for view instances.
|
|
341
|
+
* This is primarily used to assign unique identifiers to each view instance (`this.uid`), which can be helpful for tasks like
|
|
342
|
+
* generating element IDs.
|
|
343
|
+
* {@link #module_component Component}s use `this.uid` to generate data attributes for their elements, to be looked up on hydration.
|
|
344
|
+
* For server-side rendering, this counter should be reset to `0` on every request to ensure that the generated
|
|
345
|
+
* unique IDs match those on the client, enabling seamless hydration of components.
|
|
346
|
+
* @static
|
|
347
|
+
* @type {number}
|
|
348
|
+
* @default 0
|
|
349
349
|
*/
|
|
350
350
|
View.uid = 0;
|