rasti 4.0.0-alpha.1 → 4.0.0-alpha.2
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/dist/rasti.js +126 -93
- package/dist/rasti.min.js +1 -1
- package/es/Component.js +118 -87
- package/es/utils/getAttributesDiff.js +8 -6
- package/lib/Component.cjs +118 -87
- package/lib/utils/getAttributesDiff.cjs +8 -6
- package/package.json +1 -1
- package/src/Component.js +118 -87
- package/src/utils/getAttributesDiff.js +8 -6
package/es/Component.js
CHANGED
|
@@ -34,7 +34,7 @@ const getExpressionResult = (expression, context) => getResult(expression, conte
|
|
|
34
34
|
* @return {boolean} True if the element is a component root element.
|
|
35
35
|
* @private
|
|
36
36
|
*/
|
|
37
|
-
const isComponent = (el) => el.
|
|
37
|
+
const isComponent = (el) => !!el.dataset[Component.DATASET_ELEMENT] && el.dataset[Component.DATASET_ELEMENT].endsWith('-1');
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
40
|
* Check if an element has the Rasti data attribute.
|
|
@@ -43,7 +43,7 @@ const isComponent = (el) => el.hasAttribute(Component.ATTRIBUTE_ELEMENT) && el.g
|
|
|
43
43
|
* @return {boolean} True if the element has the data attribute.
|
|
44
44
|
* @private
|
|
45
45
|
*/
|
|
46
|
-
const isElement = (el) => el.
|
|
46
|
+
const isElement = (el) => !!el.dataset[Component.DATASET_ELEMENT];
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
49
|
* Generate string with placeholders for interpolated expressions.
|
|
@@ -432,7 +432,7 @@ const parseAttributes = (attributesStr, expressions) => {
|
|
|
432
432
|
/*
|
|
433
433
|
* These option keys will be extended on the component instance.
|
|
434
434
|
*/
|
|
435
|
-
const componentOptions = ['key', 'state', 'onCreate', 'onChange', 'onHydrate', 'onRecycle', 'onUpdate'];
|
|
435
|
+
const componentOptions = ['key', 'state', 'onCreate', 'onChange', 'onHydrate', 'onMount', 'onRecycle', 'onUpdate'];
|
|
436
436
|
|
|
437
437
|
/**
|
|
438
438
|
* @lends module:Component
|
|
@@ -500,34 +500,6 @@ class Component extends View {
|
|
|
500
500
|
return events;
|
|
501
501
|
}
|
|
502
502
|
|
|
503
|
-
/**
|
|
504
|
-
* Subscribes to a `change` event on a model or emitter object and invokes the `onChange` lifecycle method.
|
|
505
|
-
* The subscription is automatically cleaned up when the component is destroyed.
|
|
506
|
-
* By default, the component subscribes to changes on `this.model`, `this.state`, and `this.props`.
|
|
507
|
-
*
|
|
508
|
-
* @param {Object} model - The model or emitter object to listen to.
|
|
509
|
-
* @param {string} [type='change'] - The event type to listen for.
|
|
510
|
-
* @param {Function} [listener=this.onChange] - The callback to invoke when the event is emitted.
|
|
511
|
-
* @returns {Component} The current component instance for chaining.
|
|
512
|
-
*/
|
|
513
|
-
subscribe(model, type = 'change', listener = this.onChange) {
|
|
514
|
-
// Check if model has `on` method.
|
|
515
|
-
if (model.on) this.listenTo(model, type, listener);
|
|
516
|
-
return this;
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
/**
|
|
520
|
-
* Tell if `Component` is a container.
|
|
521
|
-
* In which case, it will not have an element by itself.
|
|
522
|
-
* It will render a single expression which is expected to return a single component as child.
|
|
523
|
-
* `this.el` will be a reference to that child component's element.
|
|
524
|
-
* @return {boolean}
|
|
525
|
-
* @private
|
|
526
|
-
*/
|
|
527
|
-
isContainer() {
|
|
528
|
-
return this.template.elements.length === 0 && this.template.interpolations.length === 1;
|
|
529
|
-
}
|
|
530
|
-
|
|
531
503
|
/**
|
|
532
504
|
* Override super method. We don't want to ensure an element on instantiation.
|
|
533
505
|
* We will provide it later.
|
|
@@ -551,6 +523,34 @@ class Component extends View {
|
|
|
551
523
|
}
|
|
552
524
|
}
|
|
553
525
|
|
|
526
|
+
/**
|
|
527
|
+
* Tell if `Component` is a container.
|
|
528
|
+
* In which case, it will not have an element by itself.
|
|
529
|
+
* It will render a single expression which is expected to return a single component as child.
|
|
530
|
+
* `this.el` will be a reference to that child component's element.
|
|
531
|
+
* @return {boolean}
|
|
532
|
+
* @private
|
|
533
|
+
*/
|
|
534
|
+
isContainer() {
|
|
535
|
+
return this.template.elements.length === 0 && this.template.interpolations.length === 1;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Subscribes to a `change` event on a model or emitter object and invokes the `onChange` lifecycle method.
|
|
540
|
+
* The subscription is automatically cleaned up when the component is destroyed.
|
|
541
|
+
* By default, the component subscribes to changes on `this.model`, `this.state`, and `this.props`.
|
|
542
|
+
*
|
|
543
|
+
* @param {Object} model - The model or emitter object to listen to.
|
|
544
|
+
* @param {string} [type='change'] - The event type to listen for.
|
|
545
|
+
* @param {Function} [listener=this.onChange] - The callback to invoke when the event is emitted.
|
|
546
|
+
* @returns {Component} The current component instance for chaining.
|
|
547
|
+
*/
|
|
548
|
+
subscribe(model, type = 'change', listener = this.onChange) {
|
|
549
|
+
// Check if model has `on` method.
|
|
550
|
+
if (model.on) this.listenTo(model, type, listener);
|
|
551
|
+
return this;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
554
|
/**
|
|
555
555
|
* Used internally on the render process.
|
|
556
556
|
* Attach the `Component` to the dom element providing `this.el`, delegate events,
|
|
@@ -595,6 +595,25 @@ class Component extends View {
|
|
|
595
595
|
return this;
|
|
596
596
|
}
|
|
597
597
|
|
|
598
|
+
/**
|
|
599
|
+
* Used internally on the render process.
|
|
600
|
+
* Reuse a `Component` by replacing the placeholder comment with the real nodes.
|
|
601
|
+
* Call `onRecycle` lifecycle method.
|
|
602
|
+
* @param parent {node} The parent node.
|
|
603
|
+
* @return {Component} The component instance.
|
|
604
|
+
* @private
|
|
605
|
+
*/
|
|
606
|
+
recycle(parent) {
|
|
607
|
+
// Locate the placeholder comment and replace it with the real nodes
|
|
608
|
+
const toBeReplaced = findComment(parent, Component.MARKER_RECYCLED(this.uid), isComponent);
|
|
609
|
+
// Replace it with this.el.
|
|
610
|
+
toBeReplaced.replaceWith(...this.getNodes());
|
|
611
|
+
// Call `onRecycle` lifecycle method.
|
|
612
|
+
this.onRecycle.call(this);
|
|
613
|
+
// Return `this` for chaining.
|
|
614
|
+
return this;
|
|
615
|
+
}
|
|
616
|
+
|
|
598
617
|
/**
|
|
599
618
|
* Get a `comment` marker with same data attribute as this component.
|
|
600
619
|
* Used to replace the component when it is recycled.
|
|
@@ -621,65 +640,19 @@ class Component extends View {
|
|
|
621
640
|
}
|
|
622
641
|
|
|
623
642
|
/**
|
|
624
|
-
*
|
|
625
|
-
*
|
|
626
|
-
* Call `onRecycle` lifecycle method.
|
|
627
|
-
* @param parent {node} The parent node.
|
|
628
|
-
* @return {Component} The component instance.
|
|
643
|
+
* Call onMount lifecycle method on children and self.
|
|
644
|
+
* Used internally during the mount and render process.
|
|
629
645
|
* @private
|
|
630
646
|
*/
|
|
631
|
-
|
|
632
|
-
//
|
|
633
|
-
|
|
634
|
-
//
|
|
635
|
-
|
|
636
|
-
// Call `onRecycle` lifecycle method.
|
|
637
|
-
this.onRecycle.call(this);
|
|
647
|
+
callOnMount() {
|
|
648
|
+
// Call onMount on children first.
|
|
649
|
+
this.children.forEach(child => child.callOnMount());
|
|
650
|
+
// Call onMount on self.
|
|
651
|
+
this.onMount.call(this);
|
|
638
652
|
// Return `this` for chaining.
|
|
639
653
|
return this;
|
|
640
654
|
}
|
|
641
655
|
|
|
642
|
-
/**
|
|
643
|
-
* Lifecycle method. Called when the view is created, at the end of the `constructor`.
|
|
644
|
-
* @param options {object} The view options.
|
|
645
|
-
*/
|
|
646
|
-
onCreate() {}
|
|
647
|
-
|
|
648
|
-
/**
|
|
649
|
-
* Lifecycle method. Called when model emits `change` event.
|
|
650
|
-
* By default calls `render` method.
|
|
651
|
-
* This method can be extended with custom logic.
|
|
652
|
-
* Maybe comparing new attributes with previous ones and calling
|
|
653
|
-
* render when needed.
|
|
654
|
-
* @param model {Rasti.Model} The model that emitted the event.
|
|
655
|
-
* @param changed {object} Object containing keys and values that has changed.
|
|
656
|
-
* @param [...args] {any} Any extra arguments passed to set method.
|
|
657
|
-
*/
|
|
658
|
-
onChange() {
|
|
659
|
-
this.render();
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
/**
|
|
663
|
-
* Lifecycle method. Called when the component is rendered for the first time and hydrated.
|
|
664
|
-
*/
|
|
665
|
-
onHydrate() {}
|
|
666
|
-
|
|
667
|
-
/**
|
|
668
|
-
* Lifecycle method. Called when the component is recycled (reused with the same key) and added to the DOM again.
|
|
669
|
-
*/
|
|
670
|
-
onRecycle() {}
|
|
671
|
-
|
|
672
|
-
/**
|
|
673
|
-
* Lifecycle method. Called when the component is updated or re-rendered.
|
|
674
|
-
*/
|
|
675
|
-
onUpdate() {}
|
|
676
|
-
|
|
677
|
-
/**
|
|
678
|
-
* Lifecycle method. Called when the component is destroyed.
|
|
679
|
-
* @param {object} options Options object or any arguments passed to `destroy` method.
|
|
680
|
-
*/
|
|
681
|
-
onDestroy() {}
|
|
682
|
-
|
|
683
656
|
/**
|
|
684
657
|
* Tagged template helper method.
|
|
685
658
|
* Used to create a partial template.
|
|
@@ -805,6 +778,7 @@ class Component extends View {
|
|
|
805
778
|
* - Components with a `key` are recycled if a previous child with the same key exists.
|
|
806
779
|
* - Unkeyed components are recycled if they have the same type and position in the template or partial.
|
|
807
780
|
* A recycled `Component` will call the `onRecycle` lifecycle method.
|
|
781
|
+
* - All child components will have their `onMount` lifecycle method called after they are added to the DOM. New children will be hydrated first (calling their `onHydrate` lifecycle method).
|
|
808
782
|
* - If the active element is inside the component, it will retain focus after the render.
|
|
809
783
|
* @return {Component} The component instance.
|
|
810
784
|
*/
|
|
@@ -874,12 +848,12 @@ class Component extends View {
|
|
|
874
848
|
discarded.destroy();
|
|
875
849
|
});
|
|
876
850
|
// Add new children. Hydrate them.
|
|
877
|
-
nextChildren.forEach(child =>
|
|
878
|
-
this.addChild(child).hydrate(fragment);
|
|
879
|
-
});
|
|
851
|
+
nextChildren.forEach(child => this.addChild(child).hydrate(fragment));
|
|
880
852
|
|
|
881
853
|
interpolation.update(fragment);
|
|
882
854
|
});
|
|
855
|
+
// Call onMount lifecycle method on all children after interpolations update.
|
|
856
|
+
this.children.forEach(child => child.callOnMount());
|
|
883
857
|
// Destroy unused children.
|
|
884
858
|
previousChildren.forEach(prev => {
|
|
885
859
|
if (this.children.indexOf(prev) < 0) prev.destroy();
|
|
@@ -903,6 +877,55 @@ class Component extends View {
|
|
|
903
877
|
return this;
|
|
904
878
|
}
|
|
905
879
|
|
|
880
|
+
/**
|
|
881
|
+
* Lifecycle method. Called when the view is created, at the end of the `constructor`.
|
|
882
|
+
* @param options {object} The view options.
|
|
883
|
+
*/
|
|
884
|
+
onCreate() {}
|
|
885
|
+
|
|
886
|
+
/**
|
|
887
|
+
* Lifecycle method. Called when model emits `change` event.
|
|
888
|
+
* By default calls `render` method.
|
|
889
|
+
* This method can be extended with custom logic.
|
|
890
|
+
* Maybe comparing new attributes with previous ones and calling
|
|
891
|
+
* render when needed.
|
|
892
|
+
* @param model {Rasti.Model} The model that emitted the event.
|
|
893
|
+
* @param changed {object} Object containing keys and values that has changed.
|
|
894
|
+
* @param [...args] {any} Any extra arguments passed to set method.
|
|
895
|
+
*/
|
|
896
|
+
onChange() {
|
|
897
|
+
this.render();
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* Lifecycle method. Called when the component is rendered for the first time and hydrated.
|
|
902
|
+
*/
|
|
903
|
+
onHydrate() {}
|
|
904
|
+
|
|
905
|
+
/**
|
|
906
|
+
* Lifecycle method. Called when the component is mounted to the DOM.
|
|
907
|
+
* This method is called after `onHydrate` and only when the component's element is actually present in the document.
|
|
908
|
+
* It is called during the initial mount process (when using `Component.mount()`) and when new child components are added during render.
|
|
909
|
+
* Use this method to perform operations that require the element to be in the DOM, such as measuring dimensions or focusing elements.
|
|
910
|
+
*/
|
|
911
|
+
onMount() {}
|
|
912
|
+
|
|
913
|
+
/**
|
|
914
|
+
* Lifecycle method. Called when the component is recycled (reused with the same key) and added to the DOM again.
|
|
915
|
+
*/
|
|
916
|
+
onRecycle() {}
|
|
917
|
+
|
|
918
|
+
/**
|
|
919
|
+
* Lifecycle method. Called when the component is updated or re-rendered.
|
|
920
|
+
*/
|
|
921
|
+
onUpdate() {}
|
|
922
|
+
|
|
923
|
+
/**
|
|
924
|
+
* Lifecycle method. Called when the component is destroyed.
|
|
925
|
+
* @param {object} options Options object or any arguments passed to `destroy` method.
|
|
926
|
+
*/
|
|
927
|
+
onDestroy() {}
|
|
928
|
+
|
|
906
929
|
/**
|
|
907
930
|
* Mark a string as safe HTML to be rendered.
|
|
908
931
|
* Normally you don't need to use this method, as Rasti will automatically mark string literals
|
|
@@ -939,6 +962,7 @@ class Component extends View {
|
|
|
939
962
|
* Mount the component into the dom.
|
|
940
963
|
* It instantiate the Component view using options,
|
|
941
964
|
* appends its element into the DOM (if `el` is provided).
|
|
965
|
+
* The `onMount` lifecycle method will be called after the component is added to the DOM.
|
|
942
966
|
* And returns the view instance.
|
|
943
967
|
* @static
|
|
944
968
|
* @param {object} [options] The view options.
|
|
@@ -960,6 +984,8 @@ class Component extends View {
|
|
|
960
984
|
// Append element to the DOM.
|
|
961
985
|
el.append(...component.render().getNodes());
|
|
962
986
|
}
|
|
987
|
+
// Call onMount lifecycle method after nodes are in the DOM.
|
|
988
|
+
component.callOnMount();
|
|
963
989
|
}
|
|
964
990
|
// Return component instance.
|
|
965
991
|
return component;
|
|
@@ -1140,6 +1166,11 @@ class Component extends View {
|
|
|
1140
1166
|
Component.ATTRIBUTE_ELEMENT = 'data-rasti-el';
|
|
1141
1167
|
Component.ATTRIBUTE_EVENT = (type) => `data-rasti-on-${type}`;
|
|
1142
1168
|
|
|
1169
|
+
/*
|
|
1170
|
+
* Dataset attribute used to identify elements.
|
|
1171
|
+
*/
|
|
1172
|
+
Component.DATASET_ELEMENT = 'rastiEl';
|
|
1173
|
+
|
|
1143
1174
|
/*
|
|
1144
1175
|
* Placeholders used to temporarily replace expressions in the template.
|
|
1145
1176
|
*/
|
|
@@ -12,12 +12,14 @@ function getAttributesDiff(attributes, previous = {}) {
|
|
|
12
12
|
// Find attributes to add/update.
|
|
13
13
|
Object.keys(attributes).forEach(key => {
|
|
14
14
|
let value = attributes[key];
|
|
15
|
-
|
|
16
|
-
if (value
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (value
|
|
20
|
-
|
|
15
|
+
// Only add if the value is different from previous.
|
|
16
|
+
if (value !== previous[key]) {
|
|
17
|
+
if (value === true) {
|
|
18
|
+
add[key] = '';
|
|
19
|
+
} else if (value !== false) {
|
|
20
|
+
if (value === null || typeof value === 'undefined') value = '';
|
|
21
|
+
add[key] = value;
|
|
22
|
+
}
|
|
21
23
|
}
|
|
22
24
|
});
|
|
23
25
|
// Find attributes to remove.
|
package/lib/Component.cjs
CHANGED
|
@@ -36,7 +36,7 @@ const getExpressionResult = (expression, context) => utils_getResult(expression,
|
|
|
36
36
|
* @return {boolean} True if the element is a component root element.
|
|
37
37
|
* @private
|
|
38
38
|
*/
|
|
39
|
-
const isComponent = (el) => el.
|
|
39
|
+
const isComponent = (el) => !!el.dataset[Component.DATASET_ELEMENT] && el.dataset[Component.DATASET_ELEMENT].endsWith('-1');
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
42
|
* Check if an element has the Rasti data attribute.
|
|
@@ -45,7 +45,7 @@ const isComponent = (el) => el.hasAttribute(Component.ATTRIBUTE_ELEMENT) && el.g
|
|
|
45
45
|
* @return {boolean} True if the element has the data attribute.
|
|
46
46
|
* @private
|
|
47
47
|
*/
|
|
48
|
-
const isElement = (el) => el.
|
|
48
|
+
const isElement = (el) => !!el.dataset[Component.DATASET_ELEMENT];
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* Generate string with placeholders for interpolated expressions.
|
|
@@ -434,7 +434,7 @@ const parseAttributes = (attributesStr, expressions) => {
|
|
|
434
434
|
/*
|
|
435
435
|
* These option keys will be extended on the component instance.
|
|
436
436
|
*/
|
|
437
|
-
const componentOptions = ['key', 'state', 'onCreate', 'onChange', 'onHydrate', 'onRecycle', 'onUpdate'];
|
|
437
|
+
const componentOptions = ['key', 'state', 'onCreate', 'onChange', 'onHydrate', 'onMount', 'onRecycle', 'onUpdate'];
|
|
438
438
|
|
|
439
439
|
/**
|
|
440
440
|
* @lends module:Component
|
|
@@ -502,34 +502,6 @@ class Component extends View {
|
|
|
502
502
|
return events;
|
|
503
503
|
}
|
|
504
504
|
|
|
505
|
-
/**
|
|
506
|
-
* Subscribes to a `change` event on a model or emitter object and invokes the `onChange` lifecycle method.
|
|
507
|
-
* The subscription is automatically cleaned up when the component is destroyed.
|
|
508
|
-
* By default, the component subscribes to changes on `this.model`, `this.state`, and `this.props`.
|
|
509
|
-
*
|
|
510
|
-
* @param {Object} model - The model or emitter object to listen to.
|
|
511
|
-
* @param {string} [type='change'] - The event type to listen for.
|
|
512
|
-
* @param {Function} [listener=this.onChange] - The callback to invoke when the event is emitted.
|
|
513
|
-
* @returns {Component} The current component instance for chaining.
|
|
514
|
-
*/
|
|
515
|
-
subscribe(model, type = 'change', listener = this.onChange) {
|
|
516
|
-
// Check if model has `on` method.
|
|
517
|
-
if (model.on) this.listenTo(model, type, listener);
|
|
518
|
-
return this;
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
/**
|
|
522
|
-
* Tell if `Component` is a container.
|
|
523
|
-
* In which case, it will not have an element by itself.
|
|
524
|
-
* It will render a single expression which is expected to return a single component as child.
|
|
525
|
-
* `this.el` will be a reference to that child component's element.
|
|
526
|
-
* @return {boolean}
|
|
527
|
-
* @private
|
|
528
|
-
*/
|
|
529
|
-
isContainer() {
|
|
530
|
-
return this.template.elements.length === 0 && this.template.interpolations.length === 1;
|
|
531
|
-
}
|
|
532
|
-
|
|
533
505
|
/**
|
|
534
506
|
* Override super method. We don't want to ensure an element on instantiation.
|
|
535
507
|
* We will provide it later.
|
|
@@ -553,6 +525,34 @@ class Component extends View {
|
|
|
553
525
|
}
|
|
554
526
|
}
|
|
555
527
|
|
|
528
|
+
/**
|
|
529
|
+
* Tell if `Component` is a container.
|
|
530
|
+
* In which case, it will not have an element by itself.
|
|
531
|
+
* It will render a single expression which is expected to return a single component as child.
|
|
532
|
+
* `this.el` will be a reference to that child component's element.
|
|
533
|
+
* @return {boolean}
|
|
534
|
+
* @private
|
|
535
|
+
*/
|
|
536
|
+
isContainer() {
|
|
537
|
+
return this.template.elements.length === 0 && this.template.interpolations.length === 1;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Subscribes to a `change` event on a model or emitter object and invokes the `onChange` lifecycle method.
|
|
542
|
+
* The subscription is automatically cleaned up when the component is destroyed.
|
|
543
|
+
* By default, the component subscribes to changes on `this.model`, `this.state`, and `this.props`.
|
|
544
|
+
*
|
|
545
|
+
* @param {Object} model - The model or emitter object to listen to.
|
|
546
|
+
* @param {string} [type='change'] - The event type to listen for.
|
|
547
|
+
* @param {Function} [listener=this.onChange] - The callback to invoke when the event is emitted.
|
|
548
|
+
* @returns {Component} The current component instance for chaining.
|
|
549
|
+
*/
|
|
550
|
+
subscribe(model, type = 'change', listener = this.onChange) {
|
|
551
|
+
// Check if model has `on` method.
|
|
552
|
+
if (model.on) this.listenTo(model, type, listener);
|
|
553
|
+
return this;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
556
|
/**
|
|
557
557
|
* Used internally on the render process.
|
|
558
558
|
* Attach the `Component` to the dom element providing `this.el`, delegate events,
|
|
@@ -597,6 +597,25 @@ class Component extends View {
|
|
|
597
597
|
return this;
|
|
598
598
|
}
|
|
599
599
|
|
|
600
|
+
/**
|
|
601
|
+
* Used internally on the render process.
|
|
602
|
+
* Reuse a `Component` by replacing the placeholder comment with the real nodes.
|
|
603
|
+
* Call `onRecycle` lifecycle method.
|
|
604
|
+
* @param parent {node} The parent node.
|
|
605
|
+
* @return {Component} The component instance.
|
|
606
|
+
* @private
|
|
607
|
+
*/
|
|
608
|
+
recycle(parent) {
|
|
609
|
+
// Locate the placeholder comment and replace it with the real nodes
|
|
610
|
+
const toBeReplaced = utils_findComment(parent, Component.MARKER_RECYCLED(this.uid), isComponent);
|
|
611
|
+
// Replace it with this.el.
|
|
612
|
+
toBeReplaced.replaceWith(...this.getNodes());
|
|
613
|
+
// Call `onRecycle` lifecycle method.
|
|
614
|
+
this.onRecycle.call(this);
|
|
615
|
+
// Return `this` for chaining.
|
|
616
|
+
return this;
|
|
617
|
+
}
|
|
618
|
+
|
|
600
619
|
/**
|
|
601
620
|
* Get a `comment` marker with same data attribute as this component.
|
|
602
621
|
* Used to replace the component when it is recycled.
|
|
@@ -623,65 +642,19 @@ class Component extends View {
|
|
|
623
642
|
}
|
|
624
643
|
|
|
625
644
|
/**
|
|
626
|
-
*
|
|
627
|
-
*
|
|
628
|
-
* Call `onRecycle` lifecycle method.
|
|
629
|
-
* @param parent {node} The parent node.
|
|
630
|
-
* @return {Component} The component instance.
|
|
645
|
+
* Call onMount lifecycle method on children and self.
|
|
646
|
+
* Used internally during the mount and render process.
|
|
631
647
|
* @private
|
|
632
648
|
*/
|
|
633
|
-
|
|
634
|
-
//
|
|
635
|
-
|
|
636
|
-
//
|
|
637
|
-
|
|
638
|
-
// Call `onRecycle` lifecycle method.
|
|
639
|
-
this.onRecycle.call(this);
|
|
649
|
+
callOnMount() {
|
|
650
|
+
// Call onMount on children first.
|
|
651
|
+
this.children.forEach(child => child.callOnMount());
|
|
652
|
+
// Call onMount on self.
|
|
653
|
+
this.onMount.call(this);
|
|
640
654
|
// Return `this` for chaining.
|
|
641
655
|
return this;
|
|
642
656
|
}
|
|
643
657
|
|
|
644
|
-
/**
|
|
645
|
-
* Lifecycle method. Called when the view is created, at the end of the `constructor`.
|
|
646
|
-
* @param options {object} The view options.
|
|
647
|
-
*/
|
|
648
|
-
onCreate() {}
|
|
649
|
-
|
|
650
|
-
/**
|
|
651
|
-
* Lifecycle method. Called when model emits `change` event.
|
|
652
|
-
* By default calls `render` method.
|
|
653
|
-
* This method can be extended with custom logic.
|
|
654
|
-
* Maybe comparing new attributes with previous ones and calling
|
|
655
|
-
* render when needed.
|
|
656
|
-
* @param model {Rasti.Model} The model that emitted the event.
|
|
657
|
-
* @param changed {object} Object containing keys and values that has changed.
|
|
658
|
-
* @param [...args] {any} Any extra arguments passed to set method.
|
|
659
|
-
*/
|
|
660
|
-
onChange() {
|
|
661
|
-
this.render();
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
/**
|
|
665
|
-
* Lifecycle method. Called when the component is rendered for the first time and hydrated.
|
|
666
|
-
*/
|
|
667
|
-
onHydrate() {}
|
|
668
|
-
|
|
669
|
-
/**
|
|
670
|
-
* Lifecycle method. Called when the component is recycled (reused with the same key) and added to the DOM again.
|
|
671
|
-
*/
|
|
672
|
-
onRecycle() {}
|
|
673
|
-
|
|
674
|
-
/**
|
|
675
|
-
* Lifecycle method. Called when the component is updated or re-rendered.
|
|
676
|
-
*/
|
|
677
|
-
onUpdate() {}
|
|
678
|
-
|
|
679
|
-
/**
|
|
680
|
-
* Lifecycle method. Called when the component is destroyed.
|
|
681
|
-
* @param {object} options Options object or any arguments passed to `destroy` method.
|
|
682
|
-
*/
|
|
683
|
-
onDestroy() {}
|
|
684
|
-
|
|
685
658
|
/**
|
|
686
659
|
* Tagged template helper method.
|
|
687
660
|
* Used to create a partial template.
|
|
@@ -807,6 +780,7 @@ class Component extends View {
|
|
|
807
780
|
* - Components with a `key` are recycled if a previous child with the same key exists.
|
|
808
781
|
* - Unkeyed components are recycled if they have the same type and position in the template or partial.
|
|
809
782
|
* A recycled `Component` will call the `onRecycle` lifecycle method.
|
|
783
|
+
* - All child components will have their `onMount` lifecycle method called after they are added to the DOM. New children will be hydrated first (calling their `onHydrate` lifecycle method).
|
|
810
784
|
* - If the active element is inside the component, it will retain focus after the render.
|
|
811
785
|
* @return {Component} The component instance.
|
|
812
786
|
*/
|
|
@@ -876,12 +850,12 @@ class Component extends View {
|
|
|
876
850
|
discarded.destroy();
|
|
877
851
|
});
|
|
878
852
|
// Add new children. Hydrate them.
|
|
879
|
-
nextChildren.forEach(child =>
|
|
880
|
-
this.addChild(child).hydrate(fragment);
|
|
881
|
-
});
|
|
853
|
+
nextChildren.forEach(child => this.addChild(child).hydrate(fragment));
|
|
882
854
|
|
|
883
855
|
interpolation.update(fragment);
|
|
884
856
|
});
|
|
857
|
+
// Call onMount lifecycle method on all children after interpolations update.
|
|
858
|
+
this.children.forEach(child => child.callOnMount());
|
|
885
859
|
// Destroy unused children.
|
|
886
860
|
previousChildren.forEach(prev => {
|
|
887
861
|
if (this.children.indexOf(prev) < 0) prev.destroy();
|
|
@@ -905,6 +879,55 @@ class Component extends View {
|
|
|
905
879
|
return this;
|
|
906
880
|
}
|
|
907
881
|
|
|
882
|
+
/**
|
|
883
|
+
* Lifecycle method. Called when the view is created, at the end of the `constructor`.
|
|
884
|
+
* @param options {object} The view options.
|
|
885
|
+
*/
|
|
886
|
+
onCreate() {}
|
|
887
|
+
|
|
888
|
+
/**
|
|
889
|
+
* Lifecycle method. Called when model emits `change` event.
|
|
890
|
+
* By default calls `render` method.
|
|
891
|
+
* This method can be extended with custom logic.
|
|
892
|
+
* Maybe comparing new attributes with previous ones and calling
|
|
893
|
+
* render when needed.
|
|
894
|
+
* @param model {Rasti.Model} The model that emitted the event.
|
|
895
|
+
* @param changed {object} Object containing keys and values that has changed.
|
|
896
|
+
* @param [...args] {any} Any extra arguments passed to set method.
|
|
897
|
+
*/
|
|
898
|
+
onChange() {
|
|
899
|
+
this.render();
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
/**
|
|
903
|
+
* Lifecycle method. Called when the component is rendered for the first time and hydrated.
|
|
904
|
+
*/
|
|
905
|
+
onHydrate() {}
|
|
906
|
+
|
|
907
|
+
/**
|
|
908
|
+
* Lifecycle method. Called when the component is mounted to the DOM.
|
|
909
|
+
* This method is called after `onHydrate` and only when the component's element is actually present in the document.
|
|
910
|
+
* It is called during the initial mount process (when using `Component.mount()`) and when new child components are added during render.
|
|
911
|
+
* Use this method to perform operations that require the element to be in the DOM, such as measuring dimensions or focusing elements.
|
|
912
|
+
*/
|
|
913
|
+
onMount() {}
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* Lifecycle method. Called when the component is recycled (reused with the same key) and added to the DOM again.
|
|
917
|
+
*/
|
|
918
|
+
onRecycle() {}
|
|
919
|
+
|
|
920
|
+
/**
|
|
921
|
+
* Lifecycle method. Called when the component is updated or re-rendered.
|
|
922
|
+
*/
|
|
923
|
+
onUpdate() {}
|
|
924
|
+
|
|
925
|
+
/**
|
|
926
|
+
* Lifecycle method. Called when the component is destroyed.
|
|
927
|
+
* @param {object} options Options object or any arguments passed to `destroy` method.
|
|
928
|
+
*/
|
|
929
|
+
onDestroy() {}
|
|
930
|
+
|
|
908
931
|
/**
|
|
909
932
|
* Mark a string as safe HTML to be rendered.
|
|
910
933
|
* Normally you don't need to use this method, as Rasti will automatically mark string literals
|
|
@@ -941,6 +964,7 @@ class Component extends View {
|
|
|
941
964
|
* Mount the component into the dom.
|
|
942
965
|
* It instantiate the Component view using options,
|
|
943
966
|
* appends its element into the DOM (if `el` is provided).
|
|
967
|
+
* The `onMount` lifecycle method will be called after the component is added to the DOM.
|
|
944
968
|
* And returns the view instance.
|
|
945
969
|
* @static
|
|
946
970
|
* @param {object} [options] The view options.
|
|
@@ -962,6 +986,8 @@ class Component extends View {
|
|
|
962
986
|
// Append element to the DOM.
|
|
963
987
|
el.append(...component.render().getNodes());
|
|
964
988
|
}
|
|
989
|
+
// Call onMount lifecycle method after nodes are in the DOM.
|
|
990
|
+
component.callOnMount();
|
|
965
991
|
}
|
|
966
992
|
// Return component instance.
|
|
967
993
|
return component;
|
|
@@ -1142,6 +1168,11 @@ class Component extends View {
|
|
|
1142
1168
|
Component.ATTRIBUTE_ELEMENT = 'data-rasti-el';
|
|
1143
1169
|
Component.ATTRIBUTE_EVENT = (type) => `data-rasti-on-${type}`;
|
|
1144
1170
|
|
|
1171
|
+
/*
|
|
1172
|
+
* Dataset attribute used to identify elements.
|
|
1173
|
+
*/
|
|
1174
|
+
Component.DATASET_ELEMENT = 'rastiEl';
|
|
1175
|
+
|
|
1145
1176
|
/*
|
|
1146
1177
|
* Placeholders used to temporarily replace expressions in the template.
|
|
1147
1178
|
*/
|
|
@@ -14,12 +14,14 @@ function getAttributesDiff(attributes, previous = {}) {
|
|
|
14
14
|
// Find attributes to add/update.
|
|
15
15
|
Object.keys(attributes).forEach(key => {
|
|
16
16
|
let value = attributes[key];
|
|
17
|
-
|
|
18
|
-
if (value
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (value
|
|
22
|
-
|
|
17
|
+
// Only add if the value is different from previous.
|
|
18
|
+
if (value !== previous[key]) {
|
|
19
|
+
if (value === true) {
|
|
20
|
+
add[key] = '';
|
|
21
|
+
} else if (value !== false) {
|
|
22
|
+
if (value === null || typeof value === 'undefined') value = '';
|
|
23
|
+
add[key] = value;
|
|
24
|
+
}
|
|
23
25
|
}
|
|
24
26
|
});
|
|
25
27
|
// Find attributes to remove.
|