diff --git a/.local.dic b/.local.dic index 0c19f5a993..1e1a743530 100644 --- a/.local.dic +++ b/.local.dic @@ -252,3 +252,4 @@ websocket working-with-html-css-and-javascript yay ZEIT +userQuestion diff --git a/config/environment.js b/config/environment.js index ccd8bed378..54c8f3ede2 100644 --- a/config/environment.js +++ b/config/environment.js @@ -48,6 +48,8 @@ module.exports = function (environment) { guidemaker: { title: 'Ember Guides', sourceRepo: 'https://github.com/ember-learn/guides-source', + gjsVersions: ['v6.7.0'], + gjsLink: '/release/components/template-tag-format/', }, algolia: { diff --git a/guides/release/accessibility/page-template-considerations.md b/guides/release/accessibility/page-template-considerations.md index 8ad0724f31..8d616240b2 100644 --- a/guides/release/accessibility/page-template-considerations.md +++ b/guides/release/accessibility/page-template-considerations.md @@ -17,29 +17,32 @@ Consider this format: Note that the unique page title is first. This is because it is the most important piece of information from a contextual perspective. Since a user with a screen reader can interrupt the screen reader as they wish, it introduces less fatigue when the unique page title is first, but provides the additional guidance if it is desired. -A simple way to add page titles is to use the `page-title` helper which comes from the [ember-page-title](https://github.com/ember-cli/ember-page-title) addon that is installed by default in new apps. We can use this helper to set the page title at any point in any template. +A simple way to add page titles is to use the `pageTitle` helper which comes from the [ember-page-title](https://github.com/ember-cli/ember-page-title) addon that is installed by default in new apps. We can use this helper to set the page title at any point in any template. For example, if we have a “posts” route, we can set the page title for it like so: +```gjs {data-filename=app/routes/posts.gjs} +import { pageTitle } from 'ember-page-title'; -```handlebars {data-filename=app/routes/posts.hbs} -{{page-title "Posts - Site Title"}} - -{{outlet}} + ``` Extending the example, if we have a “post” route that lives within the “posts” route, we could set its page title like so: -```handlebars {data-filename=app/routes/posts/post.hbs} -{{page-title (concat @model.title " - Site Title")}} +```gjs {data-filename=app/routes/posts/post.gjs} +import { pageTitle } from 'ember-page-title'; -

{{@model.title}}

-``` + +``` -- [ember-cli-head](https://github.com/ronco/ember-cli-head) -- [ember-cli-document-title](https://github.com/kimroen/ember-cli-document-title) +Each call to the `{{pageTitle}}` helper will prepend the title string to the existing title all the way up to the root title in `application.gts`. So, if your application is titled "My App", then the full title for the above example would be "My Title | Posts | My App". To evaluate more addons to add/manage content in the `` of a page, view this category on [Ember Observer](https://emberobserver.com/categories/header-content). @@ -48,14 +51,14 @@ You can test that page titles are generated correctly by asserting on the value ```javascript {data-filename=tests/acceptance/posts-test.js} import { module, test } from 'qunit'; import { visit, currentURL } from '@ember/test-helpers'; -import { setupApplicationTest } from 'my-app-name/tests/helpers'; +import { setupApplicationTest } from 'my-app/tests/helpers'; -module('Acceptance | posts', function(hooks) { +module('Acceptance | posts', function (hooks) { setupApplicationTest(hooks); - test('visiting /posts', async function(assert) { + test('visiting /posts', async function (assert) { await visit('/posts'); - assert.equal(document.title, 'Posts - Site Title'); + assert.equal(document.title, 'Posts | My App'); }); }); ``` diff --git a/guides/release/applications/run-loop.md b/guides/release/applications/run-loop.md index e424dae961..b8f0ab7ebb 100644 --- a/guides/release/applications/run-loop.md +++ b/guides/release/applications/run-loop.md @@ -1,6 +1,22 @@ -**Note:** -* _For basic Ember app development scenarios, you don't need to understand the run loop or use it directly. All common paths are paved nicely for you and don't require working with the run loop._ -* _However, the run loop will be helpful to understand the internals of Ember and to assist in customized performance tuning by manually batching costly work._ +
+
+
+
Zoey says...
+
+ +

+ For basic Ember app development scenarios, you don't need to understand the run loop or use it directly. All common paths are paved nicely for you and don't require working with the run loop. +

+ +

+ However, the run loop will be helpful to understand the internals of Ember and to assist in customized performance tuning by manually batching costly work. +

+ +
+
+ +
+
Ember's internals and most of the code you will write in your applications takes place in a run loop. The run loop is used to batch, and order (or reorder) work in a way that is most effective and efficient. @@ -83,9 +99,13 @@ class Image { and a template to display its attributes: -```handlebars -{{this.width}} -{{this.aspectRatio}} +```gjs +let profilePhoto = new Image({ width: 250, height: 500 }); + + ``` If we execute the following code without the run loop: @@ -169,13 +189,15 @@ which will make you a better Ember developer. You should begin a run loop when the callback fires. -The `Ember.run` method can be used to create a run loop. -In this example, `Ember.run` is used to handle an online +The `run()` method, imported from `@ember/runloop`, can be used to create a run loop. +In this example, `run()` is used to handle an online event (browser gains internet access) and run some Ember code. ```javascript +import { run } from '@ember/runloop'; + window.addEventListener('online', () => { - Ember.run(() => { // begin loop + run(() => { // begin loop // Code that results in jobs being scheduled goes here }); // end loop, jobs are flushed and executed }); @@ -185,15 +207,17 @@ window.addEventListener('online', () => { ## What happens if I forget to start a run loop in an async handler? -As mentioned above, you should wrap any non-Ember async callbacks in `Ember.run`. +As mentioned above, you should wrap any non-Ember async callbacks in `run()`. If you don't, Ember will try to approximate a beginning and end for you. Consider the following callback: ```javascript +import { run } from '@ember/runloop'; + window.addEventListener('online', () => { console.log('Doing things...'); - Ember.run.schedule('actions', () => { + run.schedule('actions', () => { // Do more things }); }); @@ -207,26 +231,28 @@ These automatically created run loops we call _autoruns_. Here is some pseudocode to describe what happens using the example above: ```javascript +import { run } from '@ember/runloop'; + window.addEventListener('online', () => { // 1. autoruns do not change the execution of arbitrary code in a callback. // This code is still run when this callback is executed and will not be // scheduled on an autorun. console.log('Doing things...'); - Ember.run.schedule('actions', () => { + run.schedule('actions', () => { // 2. schedule notices that there is no currently available run loop so it // creates one. It schedules it to close and flush queues on the next // turn of the JS event loop. - if (! Ember.run.hasOpenRunLoop()) { - Ember.run.begin(); + if (! run.hasOpenRunLoop()) { + run.begin(); nextTick(() => { - Ember.run.end() + run.end() }, 0); } // 3. There is now a run loop available so schedule adds its item to the // given queue - Ember.run.schedule('actions', () => { + run.schedule('actions', () => { // Do more things }); @@ -234,7 +260,7 @@ window.addEventListener('online', () => { // 4. This schedule sees the autorun created by schedule above as an available // run loop and adds its item to the given queue. - Ember.run.schedule('afterRender', () => { + run.schedule('afterRender', () => { // Do yet more things }); }); @@ -242,5 +268,5 @@ window.addEventListener('online', () => { ## Where can I find more information? -Check out the [Ember.run](https://api.emberjs.com/ember/release/classes/@ember%2Frunloop) API documentation, +Check out the [`@ember/runloop`](https://api.emberjs.com/ember/release/classes/@ember%2Frunloop) API documentation, as well as the [Backburner library](https://github.com/ebryn/backburner.js/) that powers the run loop. diff --git a/guides/release/components/block-content.md b/guides/release/components/block-content.md index a98d7a245c..4ef844eafa 100644 --- a/guides/release/components/block-content.md +++ b/guides/release/components/block-content.md @@ -2,24 +2,32 @@ Component templates can leave one or more placeholders that users can fill with These are called blocks. Here's an example that provides a component with the implicit default block. -```handlebars - - This is the default block content that will - replace `{{yield}}` (or `{{yield to="default"}}`) - in the `ExampleComponent` template. - -``` - -This is equivalent to explicitly naming the default block using the named block syntax. +```gjs +import ExampleComponent from 'my-app/components/example-component'; -```handlebars - - <:default> + +``` + +This is equivalent to explicitly naming the default block using the named block syntax. + +```gjs +import ExampleComponent from 'my-app/components/example-component'; + + ``` Through Block Content, users of the component can add additional styling and @@ -28,57 +36,61 @@ behavior by using HTML, modifiers, and other components within the block. To make that more concrete, let's take a look at two similar components representing different user's messages. -```handlebars {data-filename="app/components/received-message.hbs"} - -
-

- Tomster - their local time is 4:56pm -

- -

- Hey Zoey, have you had a chance to look at the EmberConf - brainstorming doc I sent you? -

-
+```gjs {data-filename="app/components/received-message.gjs"} + ``` -```handlebars {data-filename="app/components/sent-message.hbs"} - -
-

Zoey

- -

Hey!

- -

- I love the ideas! I'm really excited about where this year's - EmberConf is going, I'm sure it's going to be the best one yet. - Some quick notes: -

- - - -

Let me know when you've nailed down the dates!

-
+```gjs {data-filename="app/components/sent-message.gjs"} + ``` Instead of having two different components, one for sent messages and one for @@ -90,21 +102,26 @@ Their structure is pretty straightforward and similar, so we can use arguments and conditionals to handle the differences in content between them (see the previous chapters for details on how to do this). -```handlebars {data-filename="app/components/message.hbs"} - -
- +```gjs {data-filename="app/components/message.gjs"} +import MessageAvatar from 'my-app/components/message/avatar'; +import MessageUsername from 'my-app/components/message/username'; - ... -
+ ``` This works pretty well, but the message content is very different. It's also @@ -114,21 +131,26 @@ supplied by the `` tag. The way to do this in Ember is by using the `{{yield}}` syntax. -```handlebars {data-filename="app/components/message.hbs"} - -
- + +
+ - {{yield}} -
+ {{yield}} +
+ ```
@@ -151,57 +173,65 @@ The way to do this in Ember is by using the `{{yield}}` syntax. You can think of using `{{yield}}` as leaving a placeholder for the content of the `` tag. -```handlebars {data-filename="app/components/received-message.hbs"} - -

- Hey Zoey, have you had a chance to look at the EmberConf - brainstorming doc I sent you? -

-
+```gjs {data-filename="app/components/received-message.gjs"} +import Message from 'my-app/components/message'; + + ``` -```handlebars {data-filename="app/components/sent-message.hbs"} - -

Hey!

- -

- I love the ideas! I'm really excited about where this year's - EmberConf is going, I'm sure it's going to be the best one yet. - Some quick notes: -

- -
    -
  • - Definitely agree that we should double the coffee budget this - year (it really is impressive how much we go through!) -
  • -
  • - A blimp would definitely make the venue very easy to find, but - I think it might be a bit out of our budget. Maybe we could - rent some spotlights instead? -
  • -
  • - We absolutely will need more hamster wheels, last year's line - was way too long. Will get on that now before rental - season hits its peak. -
  • -
- -

Let me know when you've nailed down the dates!

-
+```gjs {data-filename="app/components/sent-message.gjs"} +import Message from 'my-app/components/message'; + + ``` As shown here, we can pass different content into the tag. The content @@ -230,21 +260,27 @@ hasn't provided a block. For instance, consider an error message dialog that has a default message in cases where we don't know what error occurred. We could show the default message using the `(has-block)` syntax in an `ErrorDialog` component. -```handlebars {data-filename=app/components/error-dialog.hbs} - - {{#if (has-block)}} - {{yield}} - {{else}} - An unknown error occurred! - {{/if}} - +```gjs {data-filename="app/components/error-dialog.gjs"} + ``` Now, if we use our `ErrorDialog` component without a block, we'll get the default message. -```handlebars - +```gjs +import ErrorDialog from 'my-app/components/error-dialog'; + + ``` ```html @@ -256,11 +292,16 @@ default message. If we had a more detailed message, though, we could use the block to pass it to the dialog. -```handlebars - - -

You are not connected to the internet!

-
+```gjs +import ErrorDialog from 'my-app/components/error-dialog'; +import Icon from 'my-app/components/icon'; + + ``` ## Block Parameters @@ -268,16 +309,22 @@ the dialog. Blocks can also pass values back into the template, similar to a callback function in JavaScript. Consider for instance a simple `BlogPost` component. -```handlebars {data-filename=app/components/blog-post.hbs} -

{{@post.title}}

-

{{@post.author}}

+```gjs {data-filename="app/components/blog-post.gjs"} + ``` -```handlebars - - +```gjs +import BlogPost from 'my-app/components/blog-post'; + + ``` We may want to give the user the ability to put extra content before or after @@ -285,79 +332,101 @@ the post, such as an image or a profile. Since we don't know what the user wants to do with the body of the post, we can instead pass the body back to them. -```handlebars {data-filename=app/components/blog-post.hbs} -

{{@post.title}}

-

{{@post.author}}

+```gjs {data-filename="app/components/blog-post.gjs"} + ``` -```handlebars - - - +```gjs +import BlogPost from 'my-app/components/blog-post'; +import AuthorBio from 'my-app/components/author-bio'; - {{postBody}} + ``` We can yield back multiple values as well, separated by spaces. -```handlebars {data-filename=app/components/blog-post.hbs} -{{yield @post.title @post.author @post.body }} +```gjs {data-filename="app/components/blog-post.gjs"} + ``` -```handlebars - - - +```gjs +import BlogPost from 'my-app/components/blog-post'; +import AuthorBio from 'my-app/components/author-bio'; + + ``` ## Named Blocks If you want to yield content to different spots in the same component, you can use named blocks. You just need to specify a name for the yielded block, like this: -```handlebars -{{yield to="somePlace"}} +```gjs + ``` You could also want to pass some values. This is the same process as the default `yield`, but you just have to pass `to` as the last argument. An example would be the popover: -```handlebars {data-filename=app/components/popover.hbs} -
-
- {{yield this.isOpen to="trigger"}} -
- {{#if this.isOpen}} -
- {{yield to="content"}} +```gjs {data-filename="app/components/popover.gjs"} + ``` Without named blocks, we would certainly have to pass components as `args` to the popover. But this is much more practical! Here’s how we would call our named blocks as a consumer: -```handlebars - - <:trigger as |open|> - - - <:content> - This is what is shown when I'm opened! - - +```gjs +import Popover from 'my-app/components/popover'; + + ``` We know the state of the popover because we passed it as an argument to the `yield`. To access its value, use the block parameters at the named block scope. It will not be accessible at the `Popover` level, so if you want the value to be available for all the blocks, you will have to pass it for each of them. @@ -378,38 +447,48 @@ Rendering the previous code example would give this as result: Don't worry, you can also still use `yield` by itself, and mix it with named blocks. Let’s take a card example: -```handlebars {data-filename=app/components/card.hbs} -
- {{#if (has-block "title")}} -
- {{yield to="title"}} +```gjs {data-filename="app/components/card.gjs"} + ``` A yielded block without a name is called `default`. So to access it, it’s like any other named blocks. -```handlebars - - <:title> -

It's nice to have me. Sometimes

- - <:default> - The card content will appear here! - -
+```gjs +import Card from 'my-app/components/card'; + + ``` The title being optional when you create a card, you can use the `(has-block)` helper with the named block by adding its name as a first parameter. That means you could also create this card: -```handlebars - - I don't want any title, and I only have a default content! - +```gjs +import Card from 'my-app/components/card'; + + ``` As you are not using named blocks, you can simply yield the content you would like to add, which becomes the default yield block. diff --git a/guides/release/components/built-in-components.md b/guides/release/components/built-in-components.md index 9b0b1ebf91..1bcd2fb4a2 100644 --- a/guides/release/components/built-in-components.md +++ b/guides/release/components/built-in-components.md @@ -10,18 +10,22 @@ These components are similar in HTML markup to the native `` or `Ask a question about Ember: - +Consider the following example in a component. + +```gjs +import { Input } from '@ember/component'; + + ``` -When Ember renders this template, you will see the following HTML code: +When Ember renders this component, you will see the following HTML code: ```html @@ -35,40 +39,71 @@ Every input should be associated with a label. In HTML, there are a few ways to 1. You can nest the input inside the label. - ```handlebars - - ``` +```gjs +import Component from "@glimmer/component"; +import { Input } from '@ember/component'; +import { tracked } from '@glimmer/tracking'; + +export default class Example extends Component { + @tracked userQuestion = ''; + + +} +``` 2. You can create an ID (globally unique within the webpage), then associate the label to the input with `for` attribute and `id` attribute. - ```handlebars - - - - ``` +```gjs +import Component from "@glimmer/component"; +import { Input } from '@ember/component'; +import { tracked } from '@glimmer/tracking'; + +export default class Example extends Component { + @tracked userQuestion = ''; + myUniqueId = "this-is-a-unique-id"; + + +} +``` 3. You can use the `aria-label` attribute to label the input with a string that is visually hidden but still available to assistive technology. - ```handlebars - - ``` +```gjs +import Component from "@glimmer/component"; +import { Input } from '@ember/component'; +import { tracked } from '@glimmer/tracking'; + +export default class Example extends Component { + @tracked userQuestion = ''; + + +} +``` While it is more appropriate to use the `