Skip to content

Commit

Permalink
Split architecture doc into multiple explainers
Browse files Browse the repository at this point in the history
  • Loading branch information
janechu committed Nov 27, 2024
1 parent 47d5caf commit 71c2fda
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 136 deletions.
136 changes: 0 additions & 136 deletions packages/web-components/fast-element/ARCHITECTURE.md

This file was deleted.

63 changes: 63 additions & 0 deletions packages/web-components/fast-element/ARCHITECTURE_FASTELEMENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# FASTElement

The `FASTElement` is our extension of the `HTMLElement`. As such it leverages the lifecycles but also requires additional setup before before the class `constructor` or the `connectedCallback` method is executed which are explained in the [overview](./ARCHITECTURE_OVERVIEW.md). This document explains specifically what `FASTElement` leverages inside the `HTMLElement`s lifecycle hooks.

For an explanation into `HTMLElement` lifecycle hooks refer to the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks).

## A Custom Element is Detected by the Browser

Because the `FASTElement` is an extension of the `HTMLElement`, it makes use of the same lifecycle hooks and extends them with `$fastController`. It also initializes using another class `ElementController`, the methods this are called during the custom elements native `constructor`, `connectedCallback`, `disconnectedCallback`, and `attributeChangedCallback` lifecycle hooks.

### 🔄 **Lifecycle**: Initialization

```mermaid
flowchart TD
A[A <code>FASTElement</code> web component is added to the <code>DOM</code>] --> B
B[<code>FASTElement</code> initializes the <code>ElementController.forCustomElement</code> passing the Custom Element instance] --> C
B --> F[Observables applied to the <code>FASTElement</code> are updated on the FAST global without values]
C{Is an <code>ElementController</code> available?}
C --> |yes|E
C --> |no|D
D[Initialize a new <code>ElementController</code> referred to as setting an element controller strategy]
D --> F
E[<code>ElementController</code> captures the Custom Element instance and the definition and attaches them to the <code>$fastController</code> which is then attached to the instance]
```

### 🔄 **Lifecycle**: Component is connected

```mermaid
flowchart TD
A[browser <code>HTMLElement</code>'s <code>connectedCallback</code> is called] --> B
B[<code>this.$fastController.connect</code> in FASTElement is called] --> C
B --> D
B --> E
B --> F
C[bind observables by capturing the Custom Elements properties and setting the values from the bound observables on the Custom Element]
D[connect behaviors by call the <code>connectedCallback</code> on all behaviors]
E[render template, execute an <code>ElementViewTemplate</code>'s render method]
F[add styles either as an appended <code>StyleElement</code> node or an <code>adoptedStylesheet</code> which may include and attach behaviors]
```

#### Render Template

The rendering of the template on the `ElementController` is by the `renderTemplate` method which is called during the `ElementController.connect` method which is triggered by `HTMLElement`s `connectedCallback` lifecycle.

The `renderTemplate` identifies the Custom Element, and the shadow root associated with the Custom Element. This then places a rendering of the template (an `ElementView`) onto the internal `view` of the controller. When creating the `ElementView`/`HTMLView` using the `ViewTemplate.render`, the `Compile.compile()` method identifies a `DocumentFragment` either by using an existing `<template>` tag, or creating one to wrap the contents of the shadow root. A new `CompilationContext` is created and the `compileAttributes` function is called, this results in the replacement of the placeholder attributes initally set-up during the pre-render step with their values if a value has been assigned. The factories with the associated nodes identified are then passed to the context. The view then binds all behaviors to the source element. The `CompilationContext.createView` is executed with the `DocumentFragment` as the root, and returns an `HTMLView`. This `HTMLView` includes an `appendTo` method to attach the fragment to the host element, which it then does. It should be noted that the compiled HTML is a `string`, which when set on the `DocumentFragment` as `innerHTML`, this allows the browser to dictate the creation of HTML nodes.

### 🔄 **Lifecycle**: Component is disconnected

When a component is disconnected, a cleanup step is created to remove associated behaviors.

### 🔄 **Lifecycle**: Attribute has been changed

Attributes have an `AttributeDefinition` which allows for converters, attachment of the `<attributName>Changed` aspect of the `@attr` decorator among other capabilities.

```mermaid
flowchart TD
A[The browser <code>HTMLElement</code>'s <code>attributeChangedCallback</code> is called] --> B
B[<code>this.$fastController.onAttributeChangedCallback</code> in <code>FASTElement</code> is called] --> C
C[calls the attribute definitions <code>onAttributeChangedCallback</code> method with the updated value] --> D
D[An <code>Updates.enqueue</code> is called which places the update in the task queue which is then executed, these are performed async unless otherwise specified]
```

These changes are observed and similar to the way `Observables` work, they utilize an `Accessor` pattern which has a `getValue` and `setValue` in which DOM updates are applied.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# `html` tagged template literal

## Pre-render Template

The `html` tagged template function creates a `ViewTemplate` object via the `ViewTemplate.create()` method. This is then used during the `compose` step, before `FASTElement` is instantiated.

During the `Compiler.compile()` method triggered by `ViewTemplate.create()` method, the following happens for each string:
- Factories with unique IDs are created for each tag template literal argument (or `TemplateValue`) which matches with the corresponding string
- A binding is created from the `TemplateValue`

A resulting string using a `createHTML()` function is produced using the `HTMLDirective`s executed for each factory. The behavior is augmented by the previous string from the `html` tag template which determines the aspect if one exists, these aspects are the `@`, `:`, or other binding aspect attached to attributes.

The `createHTML()` function utilizes a `Markup` attribute which is assigned to a factory's unique ID. The strings are concatenated and passed to a new `ViewTemplate` with all the factories (empty until one is assigned) that act as a dictionary with the unique IDs as the key to look up each factory once it has been created. The string this creates is injected into a `<template>` as `innerHTML`, which allows the browser to create the nodes and placeholder factory IDs, with the only `DOM` node that is explicitly created being the wrapping `<template>` element.

## Directives

The `HTMLBindingDirective` applies bindings to items identified as various `TemplateValue`s within the `html` tagged template. The `createHTML` step uses the factory associated with the binding to create strings in the markup using the factory's ID.

```mermaid
flowchart TD
A[A <code>new HTMLBindingDirective</code> is created with a data binding which has a policy, options, <code>createObserver</code>, and an evaluate method assigned to the passed arrow function such as <pre>x => x.foo</pre>]
B[<code>oneTime</code> binding passes the corresponding tag template argument, an arrow function]
C[<code>oneWay</code> binding passes a copy of the corresponding tag template argument, an arrow function]
D[An already specified binding such as a <code>repeat</code> or <code>when</code> directive is passed]
A --> B
A --> C
A --> D
E[When a <code>createObserver</code> is called, an <code>Observable.binding</code> is created passing the arrow function to be evaluated and the subscriber to be notified]
C --> E
F[When a <code>createObserver</code> is called, the instance of the one time binding is returned which includes a bind method returning the arrow function executed against the controller source and context]
B --> F
```
10 changes: 10 additions & 0 deletions packages/web-components/fast-element/ARCHITECTURE_INTRO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Introduction

This document (and the linked documents) explains how the exports and side effects of `@microsoft/fast-element` are used to create custom elements.

## Glossary

- [Overview](./ARCHITECTURE_OVERVIEW.md): How the `@microsoft/fast-element` should be used by a developer and what code is executed during the first render.
- [`FASTElement`](./ARCHITECTURE_FASTELEMENT.md): How the `FASTElement` is architected.
- [`html` tagged template literal](./ARCHITECTURE_HTML_TAGGED_TEMPLATE_LITERAL.md): How the `html` tagged template literal takes and converts the contents into a `VIEWTemplate`.
- [`Updates` queue](./ARCHITECTURE_UPDATES.md): How updates to attributes and observables are processed.
Loading

0 comments on commit 71c2fda

Please sign in to comment.