From 5b32d2aef85160f785ba093f7bea531cc3a6974f Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Thu, 26 Sep 2019 17:02:01 +0930 Subject: [PATCH 1/4] doc(lifecycles): WIP document lifecycle Use v2 as a template --- .../3. fundamentals/2. creating-components.md | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/current/en-us/3. fundamentals/2. creating-components.md b/current/en-us/3. fundamentals/2. creating-components.md index 2f18727..6745df3 100644 --- a/current/en-us/3. fundamentals/2. creating-components.md +++ b/current/en-us/3. fundamentals/2. creating-components.md @@ -89,18 +89,31 @@ export class CustomerDetail { ## The Component Lifecycle -All components have a well-defined lifecycle. Below is a list of methods you can implement on your view-model in order to hook into the component lifecycle: +Every component instance has a lifecycle that you can tap into. This makes it easy for you to perform various actions at particular times. For example, you may want to execute some code as soon as your component properties are bound, but before the component is first rendered. Or, you may want to run some code to manipulate the DOM as soon as possible after your element is attached to the document. Below is a summary of the various lifecycle callbacks that you can hook into in your component. + +| Lifecycle Callback | Description | +| :--- | :--- | +| `constructor` | When the framework instantiates a component, it calls your class's constructor, just like any JavaScript class. This is the best place to put basic initialization code that is not dependent on bindable properties. | +| `created` | The "created" hook runs just after the constructor and can be treated very similarly. The only difference is that the component's `Controller` has been instantiated and is accessible through the `$controller` property, for advanced scenarios. | +| `bind` | If your component has a method named "bind", then the framework will invoke it when it has begun binding values to your bindable properties. In terms of the component hierarchy, the bind hooks execute top-down, from parent to child, so your bindables will have their values set by the owning components, but the bindings in your view are not yet set. This is a good place to perform any work or make changes to anything that your view would depend on because data still flows down synchronously. This is the best time to do anything that might affect children as well. | +| `attached` | If your component has a method named "attached", then the framework will invoke it when it has fully attached your HTML element to the document, along with its children. In terms of the component hierarchy, the attached hooks execute top-down. | +| `detached` | If your component has a method named "detached", then the framework will invoke it when it has fully removed your HTML element from the document. In terms of the component hierarchy, the detached hooks execute bottom-up. | +| `unbind` | If your component has a method named "unbind", then the framework will invoke it when it has fully disconnected bindings from your component. In terms of the component hierarchy, the unbind hooks execute bottom-up. | + +To tap into any of these hooks, simply implement the method on your class. + +
+TODO +These have value, providing the method signatures, the new v2 documentation does not. I'm not sure if that is intentional or whether the signatures have changed. +
-1. `constructor()` - The view-model's constructor is called first. 2. `created(owningView: View, myView: View)` - If the view-model implements the `created` callback it is invoked next. At this point in time, the view has also been created and both the view-model and the view are connected to their controller. The created callback will receive the instance of the "owningView". This is the view that the component is declared inside of. If the component itself has a view, this will be passed second. 3. `bind(bindingContext: Object, overrideContext: Object)` - Databinding is then activated on the view and view-model. If the view-model has a `bind` callback, it will be invoked at this time. The "binding context" to which the component is being bound will be passed first. An "override context" will be passed second. The override context contains information used to traverse the parent hierarchy and can also be used to add any contextual properties that the component wants to add. -4. `attached()` - Next, the component is attached to the DOM (in document). If the view-model has an `attached` callback, it will be invoked at this time. -5. `detached()` - If defined on your view-model - is invoked after the component has been removed from the DOM. Due to navigating away or other reasons. -6. `unbind()` - After a component is detached, it's usually unbound. If your view-model has the `unbind` callback, it will be invoked during this process. -Each of these callbacks is optional. Implement whatever makes sense for your component, but don't feel obligated to implement any of them if they aren't needed for your scenario. Usually, if you implement `bind` you will need to implement `unbind`. The same goes for `attached` and `detached`, but again, it isn't mandatory. +Every lifecycle callback is optional. Implement whatever makes sense for your component, but don't feel obligated to implement any of them if they aren't needed for your scenario. Some of the lifecycle callbacks make sense to implement in pairs (`bind/unbind`, `attached/detached`) in order to clean up any resources you have allocated. If you registered a listener or subscriber remember to remove them. -The order in which the lifecycle hooks are listed above matches the order in which they are invoked. For example, `bind` happens before `attached` to ensure elements take their initial state from the view-model before the view is attached to the DOM and transitioned in. Likewise, `detached` happens before `unbind` to ensure the view is transitioned out and detached from the DOM before `unbind` potentially causes the view to change. +[Aurelia Lifecycle Demo](https://codesandbox.io/embed/aurelia-lifecycle-demo-yy1tc?expanddevtools=1&autoresize=1&fontsize=18&hidenavigation=1&module=%2Fsrc%2Fapp.html&view=preview) > Info > It is important to note that if you implement the `bind` callback function, then the property changed callbacks for any `bindable` properties will not be called when the property value is initially set. The changed callback will be called for any subsequent time the bound value changes. + From 07a3d534c196bd8585b1ac95020236c03254fad4 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 27 Sep 2019 11:44:11 +0930 Subject: [PATCH 2/4] doc(lifecycles): add component hierarchy execute order for constructor and created --- current/en-us/3. fundamentals/2. creating-components.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/current/en-us/3. fundamentals/2. creating-components.md b/current/en-us/3. fundamentals/2. creating-components.md index 6745df3..83b0233 100644 --- a/current/en-us/3. fundamentals/2. creating-components.md +++ b/current/en-us/3. fundamentals/2. creating-components.md @@ -93,8 +93,8 @@ Every component instance has a lifecycle that you can tap into. This makes it ea | Lifecycle Callback | Description | | :--- | :--- | -| `constructor` | When the framework instantiates a component, it calls your class's constructor, just like any JavaScript class. This is the best place to put basic initialization code that is not dependent on bindable properties. | -| `created` | The "created" hook runs just after the constructor and can be treated very similarly. The only difference is that the component's `Controller` has been instantiated and is accessible through the `$controller` property, for advanced scenarios. | +| `constructor` | When the framework instantiates a component, it calls your class's constructor, just like any JavaScript class. This is the best place to put basic initialization code that is not dependent on bindable properties. In terms of the component hierarchy, the constructors execute top-down | +| `created` | The "created" hook runs just after the constructor and can be treated very similarly. In terms of the component hierarchy, the created hooks execute bottom-up | | `bind` | If your component has a method named "bind", then the framework will invoke it when it has begun binding values to your bindable properties. In terms of the component hierarchy, the bind hooks execute top-down, from parent to child, so your bindables will have their values set by the owning components, but the bindings in your view are not yet set. This is a good place to perform any work or make changes to anything that your view would depend on because data still flows down synchronously. This is the best time to do anything that might affect children as well. | | `attached` | If your component has a method named "attached", then the framework will invoke it when it has fully attached your HTML element to the document, along with its children. In terms of the component hierarchy, the attached hooks execute top-down. | | `detached` | If your component has a method named "detached", then the framework will invoke it when it has fully removed your HTML element from the document. In terms of the component hierarchy, the detached hooks execute bottom-up. | From 711405cca1f7587580887a82858dde34c85f4f09 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 27 Sep 2019 11:45:18 +0930 Subject: [PATCH 3/4] doc(lifecycles): add known issue #612 router-view calls bind in wrong order --- current/en-us/3. fundamentals/2. creating-components.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/current/en-us/3. fundamentals/2. creating-components.md b/current/en-us/3. fundamentals/2. creating-components.md index 83b0233..7841aeb 100644 --- a/current/en-us/3. fundamentals/2. creating-components.md +++ b/current/en-us/3. fundamentals/2. creating-components.md @@ -117,3 +117,11 @@ Every lifecycle callback is optional. Implement whatever makes sense for your co > Info > It is important to note that if you implement the `bind` callback function, then the property changed callbacks for any `bindable` properties will not be called when the property value is initially set. The changed callback will be called for any subsequent time the bound value changes. +### Known Issues ### + +#### Issue 612: lifecycle callbacks called in wrong sequence for top level route #### + +See [lifecycle callbacks called in wrong sequence for top level route.](https://github.com/aurelia/router/issues/612) + +Any `` components will have their `bind` lifecycle callback be called bottom-up instead of top-down. + From eea3694da28dfd4aee78c11b250478d280fb9531 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 27 Sep 2019 11:45:54 +0930 Subject: [PATCH 4/4] doc(lifecycles): add known issue incorrect design choice for attached --- .../en-us/3. fundamentals/2. creating-components.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/current/en-us/3. fundamentals/2. creating-components.md b/current/en-us/3. fundamentals/2. creating-components.md index 7841aeb..d120438 100644 --- a/current/en-us/3. fundamentals/2. creating-components.md +++ b/current/en-us/3. fundamentals/2. creating-components.md @@ -96,7 +96,7 @@ Every component instance has a lifecycle that you can tap into. This makes it ea | `constructor` | When the framework instantiates a component, it calls your class's constructor, just like any JavaScript class. This is the best place to put basic initialization code that is not dependent on bindable properties. In terms of the component hierarchy, the constructors execute top-down | | `created` | The "created" hook runs just after the constructor and can be treated very similarly. In terms of the component hierarchy, the created hooks execute bottom-up | | `bind` | If your component has a method named "bind", then the framework will invoke it when it has begun binding values to your bindable properties. In terms of the component hierarchy, the bind hooks execute top-down, from parent to child, so your bindables will have their values set by the owning components, but the bindings in your view are not yet set. This is a good place to perform any work or make changes to anything that your view would depend on because data still flows down synchronously. This is the best time to do anything that might affect children as well. | -| `attached` | If your component has a method named "attached", then the framework will invoke it when it has fully attached your HTML element to the document, along with its children. In terms of the component hierarchy, the attached hooks execute top-down. | +| `attached` | If your component has a method named "attached", then the framework will invoke it when it has fully attached your HTML element to the document, along with its children. In terms of the component hierarchy, the attached hooks execute top-down. (See Known Issues: attached hooks executing top-down is an incorrect design choice)| | `detached` | If your component has a method named "detached", then the framework will invoke it when it has fully removed your HTML element from the document. In terms of the component hierarchy, the detached hooks execute bottom-up. | | `unbind` | If your component has a method named "unbind", then the framework will invoke it when it has fully disconnected bindings from your component. In terms of the component hierarchy, the unbind hooks execute bottom-up. | @@ -125,3 +125,12 @@ See [lifecycle callbacks called in wrong sequence for top level route.](https:// Any `` components will have their `bind` lifecycle callback be called bottom-up instead of top-down. +#### attached hooks executing top-down is an incorrect design choice #### + +In Aurelia v2 the `attached` hooks execute bottom-up, and also includes a new `attaching` hook that execute top-down. + +The problem with `attached` hooks executing top-down is that the children have not yet been attached when the parent's `attached` hook is called. If you want to invoke code that requires measuring of elements or integrating a 3rd party JavaScript library that requires being mounted to the DOM you will want the children to be mounted. + +To work around this problem use `TaskQueue.queueTask` to schedule a task to run after children DOM elements are attached. + +The wrong design choice has been kept in v1 to avoid breaking changes.