From 6b5dc637f2226856e7f4bb5f5ea73e3a875f3024 Mon Sep 17 00:00:00 2001 From: Corbin Crutchley Date: Wed, 27 Dec 2023 01:33:37 -0700 Subject: [PATCH 1/5] docs: add first article bits n stuffs --- .../index.md | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 content/blog/angular-templates-dont-work-how-you-think/index.md diff --git a/content/blog/angular-templates-dont-work-how-you-think/index.md b/content/blog/angular-templates-dont-work-how-you-think/index.md new file mode 100644 index 00000000..5b2afd5e --- /dev/null +++ b/content/blog/angular-templates-dont-work-how-you-think/index.md @@ -0,0 +1,78 @@ +--- +{ + title: "Angular's Templates Don't Work the Way You Think They Do", + description: "", + published: '2023-12-27T13:45:00.284Z', + authors: ['crutchcorn'], + tags: ['angular', 'webdev', javascript'], + attached: [], + license: 'cc-by-nc-sa-4' +} +--- + +But what if I told you that these `host` properties were not unique to a directive? See, when I asked you at the start of the article to think of directives like components without templates I wasn't joking: **Angular components are directives with an additional template that is rendered as the `selector`'s children**. + +```typescript +@Component({ + selector: 'do-nothing', + standalone: true, + // Nothing in the template means nothing rendered as the `do-nothing` element + template: '', +}) +class DoNothingComponent {} + +@Component({ + selector: 'app-root', + standalone: true, + imports: [DoNothingComponent], + template: ` +
+ + +
+`, +}) +export class App {} +``` + +This code sample will render: + +````html +
+ +
+```` + +Because we added an empty template. This `do-nothing` element isn't special, either; the browser is built to allow non-registered elements and treat them akin to a `div` when they render. + +Don't believe me? Try to render the above markup in HTML: + +```html +
+ +

Hello, world!

+
+
+``` + +This will render the same markup as typed; no removal of `` will occur and the `

` element will act as if it were inside of two `div`s. + +That's all that's _really_ happening when we add a template to our existing `` element: + +```typescript +@Component({ + selector: 'do-nothing', + standalone: true, + template: '

Hello, world!

', +}) +class DoNothingComponent {} +``` + +While there _is_ a template compiler in Angular, it's only really there for [reactivity](/posts/what-is-reactivity). Otherwise, it injects the results of `template` in the `selector`'s (by default empty) children array. + +This is why when people ask me: + +> How to remove the host element created by an Angular component's `selector`? + +The answer is: **it is not possible to remove the host element**. The host element is not being created by the `selector`, but rather is injecting the component's template as the children of a non-standard HTML element; who's default behavior is to be a blank slate. + From a71e86896c65635c014248ff79a54d696ddf27fb Mon Sep 17 00:00:00 2001 From: Corbin Crutchley Date: Wed, 27 Dec 2023 01:39:17 -0700 Subject: [PATCH 2/5] docs: reframe section to make more sense --- .../index.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/content/blog/angular-templates-dont-work-how-you-think/index.md b/content/blog/angular-templates-dont-work-how-you-think/index.md index 5b2afd5e..bf9d0b04 100644 --- a/content/blog/angular-templates-dont-work-how-you-think/index.md +++ b/content/blog/angular-templates-dont-work-how-you-think/index.md @@ -10,7 +10,17 @@ } --- -But what if I told you that these `host` properties were not unique to a directive? See, when I asked you at the start of the article to think of directives like components without templates I wasn't joking: **Angular components are directives with an additional template that is rendered as the `selector`'s children**. +When I started learning Angular, I was taught about Angular's components like this: + +> Angular's components have a template that is part of a component and a selector that indicates where the template should go. The way Angular adds this template in is by using a compiler to turn the template into a function that is then executed to generate the DOM nodes. + + My thinking when learning about this went something like this: + +> **This my old wrong internal model of how Angular templates worked:** +> +> A component compiles its template and assigns it to a `selector`. Whenever Angular sees that `selector`, it runs the template and injects an element with the `selector` to act as the parent. + +But see that's not right. Let's take the following example of a `do-nothing` component: ```typescript @Component({ From 180e94d575d7e90cd87405d534b823efd102ec07 Mon Sep 17 00:00:00 2001 From: Corbin Crutchley Date: Wed, 27 Dec 2023 01:45:47 -0700 Subject: [PATCH 3/5] docs: finalize initial article draft --- .../index.md | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/content/blog/angular-templates-dont-work-how-you-think/index.md b/content/blog/angular-templates-dont-work-how-you-think/index.md index bf9d0b04..5d13eec8 100644 --- a/content/blog/angular-templates-dont-work-how-you-think/index.md +++ b/content/blog/angular-templates-dont-work-how-you-think/index.md @@ -20,7 +20,11 @@ When I started learning Angular, I was taught about Angular's components like th > > A component compiles its template and assigns it to a `selector`. Whenever Angular sees that `selector`, it runs the template and injects an element with the `selector` to act as the parent. -But see that's not right. Let's take the following example of a `do-nothing` component: +But see that's not right. + +# Explaining Angular templates the _right_ way + +Let's take the following example of a `do-nothing` component: ```typescript @Component({ @@ -86,3 +90,36 @@ This is why when people ask me: The answer is: **it is not possible to remove the host element**. The host element is not being created by the `selector`, but rather is injecting the component's template as the children of a non-standard HTML element; who's default behavior is to be a blank slate. +# Replacing the host element using `selector` + +While you can add reactive attributes and even event listeners to the host element by using the `host` decorator property, sometimes you want to avoid having a non-standard host element and use a built-in HTML element like `li` for your host element. + +To do this, we'll just change our `selector` to be an attribute string: + +```typescript +@Component({ + // Yes, this is supported by components! + selector: 'li[sayHi]', + standalone: true, + template: 'Hello, world!', +}) +class SayHiComponent {} +``` + +Now we can use this component and bind it like we might otherwise: + +````typescript +@Component({ + selector: 'app-root', + standalone: true, + imports: [SayHiComponent], + template: ` +
    +
  • +
+`, +}) +export class App {} +```` + +Now rather than having an arbitrary `` element without any [semantic meaning](/posts/intro-to-web-accessibility#html-semantic-tags), we can use `
  • ` with an attribute to bind our reactivity, lifecycle methods, and everything else components have to offer. From ad5ff72657ecac263cbea2e4659dd61fdcfac22f Mon Sep 17 00:00:00 2001 From: Corbin Crutchley Date: Wed, 27 Dec 2023 01:49:38 -0700 Subject: [PATCH 4/5] docs: add description, etc --- .../blog/angular-templates-dont-work-how-you-think/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/blog/angular-templates-dont-work-how-you-think/index.md b/content/blog/angular-templates-dont-work-how-you-think/index.md index 5d13eec8..ee7c9269 100644 --- a/content/blog/angular-templates-dont-work-how-you-think/index.md +++ b/content/blog/angular-templates-dont-work-how-you-think/index.md @@ -1,10 +1,10 @@ --- { title: "Angular's Templates Don't Work the Way You Think They Do", - description: "", + description: "Angular templates are mission-critial for components. But how do they work? Using a compiler, yes, but how do they bind to the DOM itself? Read on to find out.", published: '2023-12-27T13:45:00.284Z', authors: ['crutchcorn'], - tags: ['angular', 'webdev', javascript'], + tags: ['angular', 'webdev', 'javascript'], attached: [], license: 'cc-by-nc-sa-4' } From cf640f5f96145e1750094fa73ca1bf0a1a66b9ce Mon Sep 17 00:00:00 2001 From: Corbin Crutchley Date: Wed, 27 Dec 2023 01:55:07 -0700 Subject: [PATCH 5/5] docs: add comparison to directives --- content/blog/angular-templates-dont-work-how-you-think/index.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/content/blog/angular-templates-dont-work-how-you-think/index.md b/content/blog/angular-templates-dont-work-how-you-think/index.md index ee7c9269..413e9ffc 100644 --- a/content/blog/angular-templates-dont-work-how-you-think/index.md +++ b/content/blog/angular-templates-dont-work-how-you-think/index.md @@ -90,6 +90,8 @@ This is why when people ask me: The answer is: **it is not possible to remove the host element**. The host element is not being created by the `selector`, but rather is injecting the component's template as the children of a non-standard HTML element; who's default behavior is to be a blank slate. +This is one of the reasons directives and components are _so_ similar to one another; **a component is just a directive with a template to be injected into the children of the selector**. Other than that; their functionality is shared between the two. + # Replacing the host element using `selector` While you can add reactive attributes and even event listeners to the host element by using the `host` decorator property, sometimes you want to avoid having a non-standard host element and use a built-in HTML element like `li` for your host element.