From e241586dbf289491233d223162b27c3a589b658d Mon Sep 17 00:00:00 2001 From: justinbmeyer Date: Mon, 30 Sep 2019 21:38:25 -0500 Subject: [PATCH 1/4] prototype docs for can-template --- docs/can-stache-element.md | 166 +++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) diff --git a/docs/can-stache-element.md b/docs/can-stache-element.md index 8708bca..ccd079d 100644 --- a/docs/can-stache-element.md +++ b/docs/can-stache-element.md @@ -206,6 +206,172 @@ document.body.querySelector("button#remove").addEventListener("click", () => { @codepen @highlight 14-23,only +### Passing templates (customizing layout) + +It's a very common need to customize the html of a custom element. For example, +you might want a `` element to write out the "Hello World" message inside an +`

`, `

` or any other DOM structure. + +On a high level, this customization involves two steps: + +- Passing templates with `` +- Calling the templates with `{{this.template()}}` + +#### Passing templates with `` + +When defining a `StacheElement` in a [can-stache], you can declaratively create and +pass templates with the `` element. + +For example, one might want to customize one `` element to write out +"Hello World" message in a `

` or in an italic paragraph as follows: + +```html + + +

{{message}}

+
+
+ + + +

I say "{{message}}"!

+
+
+``` + +Here's what you need to know about ``: + +- Every `` __MUST__ have a name attribute. This is + the name of the property on the custom + element that will be set to the template. In the previous example, both ``'s will be created + with a `messageTemplate` property: + + ```js + document.querySelector("hello-world").messageTemplate //-> templateFunction() + ``` + +- You can have multiple ``s within a custom element. For example, + the following passes two templates to configure ``: + + ```html + + + {{greeting}} + + + {{subject}} + + + ``` + +- ``s have the same scope of the custom element, __plus__ + a `LetScope` that the custom element can optionally provide. This means + that a `{{this.someData}}` immediately outside the `` will + reference the same value as `{{this.someData}}` within a ``: + + ```html + {{this.someData}} + + +

{{message}}

+

Also: {{this.someData}}

+
+
+ ``` + @highlight 1,5 + + The custom element can add additional data, like `message`, to these + templates. We will see how to do that in the next section. + +#### Calling the templates with `{{this.template()}}` + +Once templates are passed to a custom element, you can call those templates +within the `StacheElement`'s [can-stache-element/static.view]. For example, +`` might call a passed `messageTemplate` as follows: + +```js +class HelloWorld extends StacheElement { + static view = ` +
{{ this.messageTemplate() }}
+ `; + static props = { + messageTemplate: {type: Function, required: true}, + message: "Hello World" + } +} +``` + +While the above will render the passed `messageTemplate`, it will not provide it +a `{{message}}` variable that can be read. You can pass values into a template +with a [can-stache/expressions/hash]. The following passes the message: + +```js +class HelloWorld extends StacheElement { + static view = ` +
{{ this.messageTemplate( message=this.message ) }}
+ `; + static props = { + messageTemplate: {type: Function, required: true}, + message: "Hello World" + } +} +``` + +Sometimes, instead of passing each variable, you might want to pass the +entire custom element: + +```js +class HelloWorld extends StacheElement { + static view = ` +
{{ this.messageTemplate( helloWorld=this ) }}
+ `; + static props = { + messageTemplate: {type: Function, required: true}, + message: "Hello World" + } +} +``` + +Finally, you might want to provide a default template if one is not +provided. You can do this either in the view or as a default props +value. + +In the view: + +```js +class HelloWorld extends StacheElement { + static view = ` + {{# if( this.messageTemplate ) }} +
{{ this.messageTemplate( helloWorld=this ) }}
+ {{ else }} +

Default: {{this.message}}

+ {{/ if }} + `; + static props = { + messageTemplate: Function, + message: "Hello World" + } +} +``` + +As a default props value: + +```js +class HelloWorld extends StacheElement { + static view = ` +
{{ this.messageTemplate( helloWorld=this ) }}
+ `; + static props = { + messageTemplate: { + type: Function, + default: stache(`

Default: {{helloWorld.message}}

`) + }, + message: "Hello World" + } +} +``` + + ## Testing Custom elements have [lifecycle methods](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#Using_the_lifecycle_callbacks) that are automatically called by the browser. From 5320e9c68ead7f4cab8aa3f68373ac4e281c508c Mon Sep 17 00:00:00 2001 From: Cherif BOUCHELAGHEM Date: Tue, 1 Oct 2019 22:53:28 +0100 Subject: [PATCH 2/4] CodePen-able examples --- docs/can-stache-element.md | 291 +++++++++++++++++++++++++------------ 1 file changed, 195 insertions(+), 96 deletions(-) diff --git a/docs/can-stache-element.md b/docs/can-stache-element.md index ccd079d..b716950 100644 --- a/docs/can-stache-element.md +++ b/docs/can-stache-element.md @@ -28,16 +28,16 @@ @@ -97,10 +97,10 @@ To create a [can-stache] view for the element, add a [can-stache-element/static. @@ -121,13 +121,13 @@ To add property definitions, add a [can-stache-element/static.props static props @@ -144,16 +144,16 @@ Methods (as well as getters and setters) can be added to the class body as well: @@ -172,34 +172,34 @@ If needed, [can-stache-element/lifecycle-hooks.connected] and [can-stache-elemen import { StacheElement } from "can/everything"; class Timer extends StacheElement { - static view = ` -

{{this.time}}

- `; - static props = { - time: { type: Number, default: 0 }, - timerId: Number - }; - connected() { - this.timerId = setInterval(() => { - this.time++; - }, 1000); - console.log("connected"); - } - disconnected() { - clearInterval(this.timerId); - console.log("disconnected"); - } + static view = ` +

{{this.time}}

+ `; + static props = { + time: { type: Number, default: 0 }, + timerId: Number + }; + connected() { + this.timerId = setInterval(() => { + this.time++; + }, 1000); + console.log("connected"); + } + disconnected() { + clearInterval(this.timerId); + console.log("disconnected"); + } } customElements.define("time-er", Timer); let timer; document.body.querySelector("button#add").addEventListener("click", () => { - timer = document.createElement("time-er"); - document.body.appendChild(timer); + timer = document.createElement("time-er"); + document.body.appendChild(timer); }); document.body.querySelector("button#remove").addEventListener("click", () => { - document.body.removeChild(timer); + document.body.removeChild(timer); }); ``` @@ -289,7 +289,12 @@ Once templates are passed to a custom element, you can call those templates within the `StacheElement`'s [can-stache-element/static.view]. For example, `` might call a passed `messageTemplate` as follows: -```js +```html + + + ``` +@codepen +@highlight 8,17-26,only While the above will render the passed `messageTemplate`, it will not provide it a `{{message}}` variable that can be read. You can pass values into a template with a [can-stache/expressions/hash]. The following passes the message: -```js +```html + + + ``` +@codepen +@highlight 8 Sometimes, instead of passing each variable, you might want to pass the entire custom element: -```js +```html + + + ``` +@codepen +@highlight 8,21,only Finally, you might want to provide a default template if one is not provided. You can do this either in the view or as a default props @@ -338,7 +398,12 @@ value. In the view: -```js +```html + + + ``` +@codepen +@highlight 7-13,15,only As a default props value: -```js +```html + + + +``` +@codepen +@highlight 7-9,13,only ## Testing @@ -401,20 +500,20 @@ To test an element's properties and methods, call the [can-stache-element/lifecy ```js import { StacheElement } from "can/everything"; class Counter extends StacheElement { - static view = ` - Count: {{this.count}} - - `; - static props = { - count: 6 - }; - increment() { - this.count++; - } + static view = ` + Count: {{this.count}} + + `; + static props = { + count: 6 + }; + increment() { + this.count++; + } } customElements.define("count-er", Counter); const counter = new Counter() - .initialize({ count: 20 }); + .initialize({ count: 20 }); counter.count === 20; // -> true @@ -431,20 +530,20 @@ To test an element's view, call the [can-stache-element/lifecycle-methods.render ```js import { StacheElement } from "can/everything"; class Counter extends StacheElement { - static view = ` - Count: {{this.count}} - - `; - static props = { - count: 6 - }; - increment() { - this.count++; - } + static view = ` + Count: {{this.count}} + + `; + static props = { + count: 6 + }; + increment() { + this.count++; + } } customElements.define("count-er", Counter); const counter = new Counter() - .render({ count: 20 }); + .render({ count: 20 }); counter.firstElementChild.innerHTML === "20"; // -> true @@ -462,26 +561,26 @@ To test the functionality of the `connected` or `disconnected` hooks, you can ca import { StacheElement } from "can/everything"; class Timer extends StacheElement { - static view = ` -

{{this.time}}

- `; - static props = { - time: { type: Number, default: 0 }, - timerId: Number - }; - connected() { - this.timerId = setInterval(() => { - this.time++; - }, 1000); - } - disconnected() { - clearInterval(this.timerId); - } + static view = ` +

{{this.time}}

+ `; + static props = { + time: { type: Number, default: 0 }, + timerId: Number + }; + connected() { + this.timerId = setInterval(() => { + this.time++; + }, 1000); + } + disconnected() { + clearInterval(this.timerId); + } } customElements.define("time-er", Timer); const timer = new Timer() - .connect(); + .connect(); timer.firstElementChild; // ->

0

From e863f5df6b911ee9c39423487f127a8a1d7c3c80 Mon Sep 17 00:00:00 2001 From: Cherif BOUCHELAGHEM Date: Wed, 2 Oct 2019 16:54:03 +0100 Subject: [PATCH 3/4] Add value change example --- docs/can-stache-element.md | 43 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/docs/can-stache-element.md b/docs/can-stache-element.md index b716950..0f314d8 100644 --- a/docs/can-stache-element.md +++ b/docs/can-stache-element.md @@ -471,6 +471,49 @@ customElements.define("my-app", App); @codepen @highlight 7-9,13,only +If a property changes, the rendered passed template also update +its HTML like following: + +```html + + + +``` +@codepen +@highlight 17-19,28,only + + ## Testing Custom elements have [lifecycle methods](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#Using_the_lifecycle_callbacks) that are automatically called by the browser. From 466d4ffa7e803d17065d9d50b1ea5a41a3eef04c Mon Sep 17 00:00:00 2001 From: Cherif BOUCHELAGHEM Date: Wed, 2 Oct 2019 20:26:19 +0100 Subject: [PATCH 4/4] Updates after review --- docs/can-stache-element.md | 66 +++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/docs/can-stache-element.md b/docs/can-stache-element.md index 0f314d8..0efbcc6 100644 --- a/docs/can-stache-element.md +++ b/docs/can-stache-element.md @@ -29,7 +29,7 @@ import { StacheElement } from "can/everything"; class Counter extends StacheElement { static view = ` - Count: {{this.count}} + Count: {{ this.count }} `; static props = { @@ -98,7 +98,7 @@ To create a [can-stache] view for the element, add a [can-stache-element/static. import { StacheElement } from "can/everything"; class Counter extends StacheElement { static view = ` - Count: {{this.count}} + Count: {{ this.count }} `; } @@ -122,7 +122,7 @@ To add property definitions, add a [can-stache-element/static.props static props import { StacheElement } from "can/everything"; class Counter extends StacheElement { static view = ` - Count: {{this.count}} + Count: {{ this.count }} `; static props = { @@ -145,7 +145,7 @@ Methods (as well as getters and setters) can be added to the class body as well: import { StacheElement } from "can/everything"; class Counter extends StacheElement { static view = ` - Count: {{this.count}} + Count: {{ this.count }} `; static props = { @@ -173,7 +173,7 @@ import { StacheElement } from "can/everything"; class Timer extends StacheElement { static view = ` -

{{this.time}}

+

{{ this.time }}

`; static props = { time: { type: Number, default: 0 }, @@ -215,11 +215,11 @@ you might want a `` element to write out the "Hello World" message On a high level, this customization involves two steps: - Passing templates with `` -- Calling the templates with `{{this.template()}}` +- Calling the templates with `{{ this.template() }}` #### Passing templates with `` -When defining a `StacheElement` in a [can-stache], you can declaratively create and +When rendering a StacheElement in a [can-stache] template, you can declaratively create and pass templates with the `` element. For example, one might want to customize one `` element to write out @@ -228,13 +228,13 @@ For example, one might want to customize one `` element to write ou ```html -

{{message}}

+

{{ message }}

-

I say "{{message}}"!

+

I say "{{ message }}"!

``` @@ -256,25 +256,25 @@ Here's what you need to know about ``: ```html - {{greeting}} + {{ greeting }} - {{subject}} + {{ subject }} ``` - ``s have the same scope of the custom element, __plus__ a `LetScope` that the custom element can optionally provide. This means - that a `{{this.someData}}` immediately outside the `` will - reference the same value as `{{this.someData}}` within a ``: + that a `{{ this.someData }}` immediately outside the `` will + reference the same value as `{{ this.someData }}` within a ``: ```html - {{this.someData}} + {{ this.someData }} -

{{message}}

-

Also: {{this.someData}}

+

{{ message }}

+

Also: {{ this.someData }}

``` @@ -283,7 +283,7 @@ Here's what you need to know about ``: The custom element can add additional data, like `message`, to these templates. We will see how to do that in the next section. -#### Calling the templates with `{{this.template()}}` +#### Calling the templates with `{{ this.template() }}` Once templates are passed to a custom element, you can call those templates within the `StacheElement`'s [can-stache-element/static.view]. For example, @@ -319,10 +319,10 @@ customElements.define("my-app", App); ``` @codepen -@highlight 8,17-26,only +@highlight 19,23,only While the above will render the passed `messageTemplate`, it will not provide it -a `{{message}}` variable that can be read. You can pass values into a template +a `{{ message }}` variable that can be read. You can pass values into a template with a [can-stache/expressions/hash]. The following passes the message: ```html @@ -333,7 +333,7 @@ import { StacheElement } from "can/everything"; class HelloWorld extends StacheElement { static view = ` -
{{ this.messageTemplate( message = this.message) }}
+
{{ this.messageTemplate(message = this.message) }}
`; static props = { messageTemplate: {type: Function, required: true}, @@ -355,7 +355,7 @@ customElements.define("my-app", App); ``` @codepen -@highlight 8 +@highlight 8,only Sometimes, instead of passing each variable, you might want to pass the entire custom element: @@ -368,7 +368,7 @@ import { StacheElement } from "can/everything"; class HelloWorld extends StacheElement { static view = ` -
{{ this.messageTemplate( helloWorld=this ) }}
+
{{ this.messageTemplate(helloWorld = this) }}
`; static props = { messageTemplate: {type: Function, required: true}, @@ -407,7 +407,7 @@ import { StacheElement } from "can/everything"; class HelloWorld extends StacheElement { static view = ` {{# if( this.messageTemplate ) }} -
{{ this.messageTemplate( helloWorld=this ) }}
+
{{ this.messageTemplate(helloWorld = this) }}
{{ else }}

Default: {{this.message}}

{{/ if }} @@ -422,9 +422,6 @@ customElements.define("hello-world", HelloWorld); class App extends StacheElement { static view = ` - -

{{ helloWorld.message }}

-
`; } @@ -444,12 +441,12 @@ import { StacheElement, stache } from "can/everything"; class HelloWorld extends StacheElement { static view = ` -
{{ this.messageTemplate( helloWorld=this ) }}
+
{{ this.messageTemplate(helloWorld = this) }}
`; static props = { messageTemplate: { type: Function, - default: stache(`

Default: {{helloWorld.message}}

`) + default: stache(`

Default: {{ helloWorld.message }}

`) }, message: "Hello World" } @@ -459,9 +456,6 @@ customElements.define("hello-world", HelloWorld); class App extends StacheElement { static view = ` - -

{{ helloWorld.message }}

-
`; } @@ -482,12 +476,12 @@ import { StacheElement, stache } from "can/everything"; class HelloWorld extends StacheElement { static view = ` -
{{ this.messageTemplate( helloWorld=this ) }}
+
{{ this.messageTemplate(helloWorld = this) }}
`; static props = { messageTemplate: { type: Function, - default: stache(`

Default: {{helloWorld.message}}

`) + default: stache(`

Default: {{ helloWorld.message }}

`) }, message: "Hello World" } @@ -544,7 +538,7 @@ To test an element's properties and methods, call the [can-stache-element/lifecy import { StacheElement } from "can/everything"; class Counter extends StacheElement { static view = ` - Count: {{this.count}} + Count: {{ this.count }} `; static props = { @@ -574,7 +568,7 @@ To test an element's view, call the [can-stache-element/lifecycle-methods.render import { StacheElement } from "can/everything"; class Counter extends StacheElement { static view = ` - Count: {{this.count}} + Count: {{ this.count }} `; static props = { @@ -605,7 +599,7 @@ import { StacheElement } from "can/everything"; class Timer extends StacheElement { static view = ` -

{{this.time}}

+

{{ this.time }}

`; static props = { time: { type: Number, default: 0 },