Skip to content

Commit 8866067

Browse files
committed
Merge remote-tracking branch 'origin/gjs' into gjs-in-depth
2 parents f540897 + 4cf7ec6 commit 8866067

File tree

6 files changed

+211
-207
lines changed

6 files changed

+211
-207
lines changed

guides/release/accessibility/page-template-considerations.md

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,32 @@ Consider this format:
1717

1818
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.
1919

20-
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.
20+
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.
2121

2222
For example, if we have a “posts” route, we can set the page title for it like so:
2323

24+
```gjs {data-filename=app/routes/posts.gjs}
25+
import { pageTitle } from 'ember-page-title';
2426
25-
```handlebars {data-filename=app/routes/posts.hbs}
26-
{{page-title "Posts - Site Title"}}
27-
28-
{{outlet}}
27+
<template>
28+
{{pageTitle "Posts"}}
29+
{{outlet}}
30+
</template>
2931
```
3032

3133
Extending the example, if we have a “post” route that lives within the “posts” route, we could set its page title like so:
3234

33-
```handlebars {data-filename=app/routes/posts/post.hbs}
34-
{{page-title (concat @model.title " - Site Title")}}
35+
```gjs {data-filename=app/routes/posts/post.gjs}
36+
import { pageTitle } from 'ember-page-title';
3537
36-
<h1>{{@model.title}}</h1>
37-
```
38+
<template>
39+
{{pageTitle @model.title}} {{! e.g., "My Title" }}
3840
39-
When your needs become more complex, the following addons facilitate page titles in a more dynamic and maintainable way.
41+
<h1>{{@model.title}}</h1>
42+
</template>
43+
```
4044

41-
- [ember-cli-head](https://github.com/ronco/ember-cli-head)
42-
- [ember-cli-document-title](https://github.com/kimroen/ember-cli-document-title)
45+
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".
4346

4447
To evaluate more addons to add/manage content in the `<head>` of a page, view this category on [Ember Observer](https://emberobserver.com/categories/header-content).
4548

@@ -48,14 +51,14 @@ You can test that page titles are generated correctly by asserting on the value
4851
```javascript {data-filename=tests/acceptance/posts-test.js}
4952
import { module, test } from 'qunit';
5053
import { visit, currentURL } from '@ember/test-helpers';
51-
import { setupApplicationTest } from 'my-app-name/tests/helpers';
54+
import { setupApplicationTest } from 'my-app/tests/helpers';
5255

53-
module('Acceptance | posts', function(hooks) {
56+
module('Acceptance | posts', function (hooks) {
5457
setupApplicationTest(hooks);
5558

56-
test('visiting /posts', async function(assert) {
59+
test('visiting /posts', async function (assert) {
5760
await visit('/posts');
58-
assert.equal(document.title, 'Posts - Site Title');
61+
assert.equal(document.title, 'Posts | My App');
5962
});
6063
});
6164
```

guides/release/models/index.md

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,9 @@ writing the admin section of a blogging app, which has a feature that
9090
lists the drafts for the currently logged in user.
9191

9292
You might be tempted to make the component responsible for fetching that
93-
data and storing it:
93+
data, storing it, and showing the list of drafts, like this:
9494

95-
```javascript {data-filename=app/components/list-of-drafts.js}
95+
```gjs {data-filename=app/components/list-of-drafts.gjs}
9696
import Component from "@glimmer/component";
9797
import { tracked } from "@glimmer/tracking";
9898
import fetch from "fetch";
@@ -107,30 +107,27 @@ export default class ListOfDraftsComponent extends Component {
107107
this.drafts = data;
108108
});
109109
}
110+
<template>
111+
<ul>
112+
{{#each this.drafts key="id" as |draft|}}
113+
<li>{{draft.title}}</li>
114+
{{/each}}
115+
</ul>
116+
</template>
110117
}
111118
```
112119

113-
You could then show the list of drafts in your component's template like
114-
this:
115-
116-
```handlebars {data-filename=app/components/list-of-drafts.hbs}
117-
<ul>
118-
{{#each this.drafts key="id" as |draft|}}
119-
<li>{{draft.title}}</li>
120-
{{/each}}
121-
</ul>
122-
```
123-
124-
This works great for the `list-of-drafts` component. However, your app
120+
This works great for the `ListOfDrafts` component. However, your app
125121
is likely made up of many different components. On another page you
126122
may want a component to display the number of drafts. You may be
127-
tempted to copy and paste your existing `willRender` code into the new
123+
tempted to copy and paste your existing `constructor` code into the new
128124
component.
129125

130-
```javascript {data-filename=app/components/drafts-button.js}
126+
```gjs {data-filename=app/components/drafts-button.gjs}
131127
import Component from "@glimmer/component";
132128
import { tracked } from "@glimmer/tracking";
133129
import fetch from "fetch";
130+
import { LinkTo } from '@ember/routing';
134131
135132
export default class DraftsButtonComponent extends Component {
136133
@tracked drafts;
@@ -142,13 +139,13 @@ export default class DraftsButtonComponent extends Component {
142139
this.drafts = data;
143140
});
144141
}
145-
}
146-
```
147142
148-
```handlebars {data-filename=app/components/drafts-button.hbs}
149-
<LinkTo @route="drafts">
150-
Drafts ({{this.drafts.length}})
151-
</LinkTo>
143+
<template>
144+
<LinkTo @route="drafts">
145+
Drafts ({{this.drafts.length}})
146+
</LinkTo>
147+
</template>
148+
}
152149
```
153150

154151
Unfortunately, the app will now make two separate requests for the

guides/release/services/index.md

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -62,27 +62,35 @@ You can either invoke it with no arguments, or you can pass it the registered na
6262
When no arguments are passed, the service is loaded based on the name of the decorated property.
6363
You can load the shopping cart service with no arguments like below.
6464

65-
```javascript {data-filename=app/components/cart-contents.js}
65+
```gjs {data-filename=app/components/cart-contents.gjs}
6666
import Component from '@glimmer/component';
6767
import { service } from '@ember/service';
6868
6969
export default class CartContentsComponent extends Component {
7070
// Will load the service defined in: app/services/shopping-cart.js
7171
@service shoppingCart;
72+
73+
<template>
74+
<h2>Shopping Cart</h2>
75+
</template>
7276
}
7377
```
7478

7579
This injects the shopping cart service into the component and makes it available as the `shoppingCart` property.
7680

7781
Another way to inject a service is to provide the name of the service as an argument to the decorator.
7882

79-
```javascript {data-filename=app/components/cart-contents.js}
83+
```gjs {data-filename=app/components/cart-contents.gjs}
8084
import Component from '@glimmer/component';
8185
import { service } from '@ember/service';
8286
8387
export default class CartContentsComponent extends Component {
8488
// Will load the service defined in: app/services/shopping-cart.js
8589
@service('shopping-cart') cart;
90+
91+
<template>
92+
<h2>Shopping Cart</h2>
93+
</template>
8694
}
8795
```
8896

@@ -92,7 +100,7 @@ Sometimes a service may or may not exist, like when an initializer conditionally
92100
Since normal injection will throw an error if the service doesn't exist,
93101
you must look up the service using Ember's [`getOwner`](https://api.emberjs.com/ember/release/classes/@ember%2Fapplication/methods/getOwner?anchor=getOwner) instead.
94102

95-
```javascript {data-filename=app/components/cart-contents.js}
103+
```gjs {data-filename=app/components/cart-contents.gjs}
96104
import Component from '@glimmer/component';
97105
import { getOwner } from '@ember/application';
98106
@@ -101,42 +109,46 @@ export default class CartContentsComponent extends Component {
101109
get cart() {
102110
return getOwner(this).lookup('service:shopping-cart');
103111
}
112+
113+
<template>
114+
<h2>Shopping Cart</h2>
115+
</template>
104116
}
105117
```
106118

107119
Injected properties are lazy loaded; meaning the service will not be instantiated until the property is explicitly called.
108120

109121
Once loaded, a service will persist until the application exits.
110122

123+
Once injected into a component, a service can also be used in the template.
124+
111125
Below we add a remove action to the `cart-contents` component.
112126

113-
```javascript {data-filename=app/components/cart-contents.js}
127+
```gjs {data-filename=app/components/cart-contents.gjs}
114128
import Component from '@glimmer/component';
115129
import { service } from '@ember/service';
116-
import { action } from '@ember/object';
130+
import { on } from '@ember/modifier';
131+
import { fn } from '@ember/helper';
117132
118133
export default class CartContentsComponent extends Component {
119134
@service('shopping-cart') cart;
120135
121-
@action
122-
remove(item) {
136+
remove = (item) => {
123137
this.cart.remove(item);
124-
}
138+
};
139+
140+
<template>
141+
<h2>Shopping Cart</h2>
142+
<ul>
143+
{{#each this.cart.items as |item|}}
144+
<li>
145+
{{item.name}}
146+
<button type="button" {{on "click" (fn this.remove item)}}>Remove</button>
147+
</li>
148+
{{/each}}
149+
</ul>
150+
</template>
125151
}
126152
```
127153

128-
Once injected into a component, a service can also be used in the template.
129-
Note `cart` being used below to get data from the cart.
130-
131-
```handlebars {data-filename=app/components/cart-contents.hbs}
132-
<ul>
133-
{{#each this.cart.items as |item|}}
134-
<li>
135-
{{item.name}}
136-
<button type="button" {{on "click" (fn this.remove item)}}>Remove</button>
137-
</li>
138-
{{/each}}
139-
</ul>
140-
```
141-
142154
<!-- eof - needed for pages that end in a code block -->

guides/release/typescript/additional-resources/gotchas.md

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -54,34 +54,11 @@ For examples, see:
5454
- EmberData [`@belongsTo`][model-belongsto]
5555
- EmberData [`@hasMany`][model-hasmany]
5656

57-
## Templates
58-
59-
Templates are currently totally non-type-checked. This means that you lose any safety when moving into a template context, even if using a Glimmer `Component` in Ember Octane. (Looking for type-checking in templates? Try [Glint][]!)
60-
61-
For example, TypeScript won't detect a mismatch between this action and the corresponding call in the template:
62-
63-
```typescript {data-filename="app/components/my-game.ts"}
64-
import Component from '@ember/component';
65-
import { action } from '@ember/object';
66-
67-
export default class MyGame extends Component {
68-
@action turnWheel(degrees: number) {
69-
// ...
70-
}
71-
}
72-
```
73-
74-
```handlebars {data-filename="app/components/my-game.hbs"}
75-
<button {{on 'click' (fn this.turnWheel 'potato')}}>
76-
Click Me
77-
</button>
78-
```
79-
8057
## Hook Types and Autocomplete
8158

8259
Let's imagine a component which just logs the names of its arguments when it is first constructed. First, we must define the [Signature][] and pass it into our component, then we can use the `Args` member in our Signature to set the type of `args` in the constructor:
8360

84-
```typescript {data-filename="app/components/args-display.ts"}
61+
```gts {data-filename="app/components/args-display.gts"}
8562
import type Owner from '@ember/owner';
8663
import Component from '@glimmer/component';
8764
@@ -136,4 +113,3 @@ export default class MyRoute extends Route {
136113
<!-- External links -->
137114

138115
[declare]: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#the-usedefineforclassfields-flag-and-the-declare-property-modifier
139-
[glint]: https://typed-ember.gitbook.io/glint/

0 commit comments

Comments
 (0)