diff --git a/guides/release/in-depth-topics/autotracking-in-depth.md b/guides/release/in-depth-topics/autotracking-in-depth.md index 0ff6f514ce..322fe78141 100644 --- a/guides/release/in-depth-topics/autotracking-in-depth.md +++ b/guides/release/in-depth-topics/autotracking-in-depth.md @@ -9,11 +9,7 @@ When Ember first renders a component, it renders the initial _state_ of that component - the state of the instance, and state of the arguments that are passed to it: -```handlebars {data-filename=app/components/hello.hbs} -{{this.greeting}}, {{@name}}! -``` - -```js {data-filename=app/components/hello.js} +```gjs {data-filename=app/components/hello.gjs} import Component from '@glimmer/component'; export default class HelloComponent extends Component { @@ -29,11 +25,19 @@ export default class HelloComponent extends Component { return 'Hola'; } } + + } ``` -```handlebars {data-filename=app/templates/application.hbs} - +```gjs {data-filename=app/templates/application.gjs} +import 'my-app/components/hello'; + + ``` When Ember renders this template, we get: @@ -59,11 +63,12 @@ Trackable values are values that: We can do this by marking the field with the `@tracked` decorator: -```js {data-filename=app/components/hello.js} +```gjs {data-filename=app/components/hello.gjs data-diff="+2,-5,+6"} import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; export default class HelloComponent extends Component { + language = 'en'; @tracked language = 'en'; get greeting() { @@ -76,6 +81,10 @@ export default class HelloComponent extends Component { return 'Hola'; } } + + } ``` @@ -95,20 +104,10 @@ Tracked properties can be updated like any other property, using standard JavaScript syntax. For instance, we could update a tracked property via an action, as in this example component. -```handlebars {data-filename=app/components/hello.hbs} -{{this.greeting}}, {{@name}}! - - -``` - -```js {data-filename=app/components/hello.js} +``` gjs { data-filename=app/components/hello.gjs data-diff="+3,+18,+19,+20,+21,+25,+26,+27,+28,+29,+30" } import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; -import { action } from '@ember/object'; +import { on } from '@ember/modifier'; export default class HelloComponent extends Component { @tracked language = 'en'; @@ -124,10 +123,19 @@ export default class HelloComponent extends Component { } } - @action - updateLanguage(event) { + updateLanguage = (event) => { this.language = event.target.value; - } + }; + + } ``` @@ -140,10 +148,10 @@ Another way that a tracked property could be updated is asynchronously, if you're sending a request to the server. For instance, maybe we would want to load the user's preferred language: -```js +``` gjs { data-filename=app/components/hello.gjs data-diff="+6,+7,+8,+9,+10,+11,+12,+13,+14,+15" } import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; -import { action } from '@ember/object'; +import { on } from '@ember/modifier'; export default class HelloComponent extends Component { constructor() { @@ -168,6 +176,20 @@ export default class HelloComponent extends Component { return 'Hola'; } } + + updateLanguage = (event) => { + this.language = event.target.value; + }; + + } ``` @@ -180,16 +202,26 @@ app. So far we've only shown tracked properties working through getters, but tracking works through _methods_ or _functions_ as well: -```js +``` gjs { data-diff="+17,+18,+19,+20,+21,+24,+25,+26,+27" } import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; -import { action } from '@ember/object'; +import { on } from '@ember/modifier'; export default class HelloComponent extends Component { + constructor() { + super(...arguments); + + fetch('/api/preferences') + .then(r => r.json()) // convert the response to a JS object + .then(response => { + this.language = response.preferredLanguage; + }); + } + @tracked language = 'en'; @tracked supportedLanguages = ['en', 'de', 'es']; - isSupported(language) { + isSupported = (language) => { return this.supportedLanguages.includes(language); } @@ -207,6 +239,20 @@ export default class HelloComponent extends Component { return 'Hola'; } } + + updateLanguage = (event) => { + this.language = event.target.value; + }; + + } ``` @@ -237,7 +283,7 @@ export default class Person { ```js {data-filename=app/routes/application.js} import Route from '@ember/routing/route'; -import Person from '../../../../utils/person'; +import Person from 'my-app/utils/person'; export default class ApplicationRoute extends Route { model() { @@ -246,27 +292,27 @@ export default class ApplicationRoute extends Route { } ``` -```js {data-filename=app/controllers/application.js} -import Controller from '@ember/controller'; -import { action } from '@ember/object'; - -export default class ApplicationController extends Controller { - @action - updateName(title, name) { - this.model.title = title; - this.model.name = name; - } +```gjs {data-filename=app/templates/application.gjs} +import Component from '@glimmer/component'; +import { on } from '@ember/modifier'; +import { fn } from '@ember/helper'; + +export default class ApplicationRouteComponent extends Component { + updateName = (title, name) => { + this.args.model.title = title; + this.args.model.name = name; + }; + + } ``` -```handlebars {data-filename=app/templates/application.hbs} -{{@model.fullName}} - - -``` - As long as the properties are tracked, and accessed when rendering the template directly or indirectly, everything should update as expected @@ -362,7 +408,8 @@ you cache (or "memoize") a getter by simply marking it as `@cached`. With this in mind, let's introduce caching to `aspectRatio`: -```js +``` js { data-diff="-1,+2,+10,-22,+23,-28,+29" } +import { tracked } from '@glimmer/tracking'; import { cached, tracked } from '@glimmer/tracking'; let count = 0; @@ -383,11 +430,13 @@ let photo = new Photo(); console.log(photo.aspectRatio); // 1.5 console.log(count); // 1 console.log(photo.aspectRatio); // 1.5 +console.log(count); // 2 console.log(count); // 1 photo.width = 800; console.log(photo.aspectRatio); // 2 +console.log(count); // 3 console.log(count); // 2 ``` diff --git a/guides/release/in-depth-topics/patterns-for-actions.md b/guides/release/in-depth-topics/patterns-for-actions.md index d753a80d61..81976a38d8 100644 --- a/guides/release/in-depth-topics/patterns-for-actions.md +++ b/guides/release/in-depth-topics/patterns-for-actions.md @@ -12,27 +12,27 @@ to confirm in order to trigger some action. We'll call this the `ButtonWithConfirmation` component. We can start off with a normal component definition, like we've seen before: -```handlebars {data-filename=app/components/button-with-confirmation.hbs} - - -{{#if this.isConfirming}} -
- - -
-{{/if}} -``` - -```js {data-filename=app/components/button-with-confirmation.js} +```gjs {data-filename=app/components/button-with-confirmation.gjs} import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; export default class ButtonWithConfirmationComponent extends Component { @tracked isConfirming = false; + + } ``` @@ -45,92 +45,113 @@ more about tracked properties in the [Autotracking In-Depth](../autotracking-in- guide. Next, we need to hook up the button to toggle that property. We'll -do this with an _action_: +do this with an _action_. +An action is a function that is bound to the component instance. There are two common ways to bind a function to a component instance. The first way is to assign an arrow function to a property, like this: + +```js + // in component + myAction = () => { + // do something here + }; +``` + +The second way is to import and use the `@action` decorator, like this: + +```js +import { action } from '@ember/object'; +// ... -```js {data-filename=app/components/button-with-confirmation.js} + // in component + @action + myAction() { + // do something here + } +``` + +These two are functionally equivalent and are used interchangeably throughout these guides. + +So, to hook up the button to toggle the `isConfirming` property, we'll define a `launchConfirmDialog` action: + +```gjs {data-filename=app/components/button-with-confirmation.gjs} import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; -import { action } from '@ember/object'; +import { on } from '@ember/modifier'; export default class ButtonWithConfirmationComponent extends Component { @tracked isConfirming = false; - @action - launchConfirmDialog() { + launchConfirmDialog = () => { this.isConfirming = true; - } -} -``` - -```handlebars - + }; -{{#if this.isConfirming}} -
- - - -{{#if this.isConfirming}} -
- +```gjs {data-filename=app/components/button-with-confirmation.gjs} +import Component from '@glimmer/component'; -{{#if this.isConfirming}} -
- {{yield this.confirmValue}} +export default class ButtonWithConfirmationComponent extends Component { + submitConfirm = async() => { + if (this.args.onConfirm) { + // call `onConfirm` with a second argument + await this.args.onConfirm(this.confirmValue); + } - -