Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Add Template Tag format guides #1978

Merged
merged 41 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
1aaceec
docs: add initial template tag format page
Nov 20, 2023
2c2a67a
docs: add IDE setup
Nov 21, 2023
e5e765b
docs: tweak sentence
Nov 21, 2023
528f4f6
docs: port code examples of template-imports readme
Nov 21, 2023
3f4a1b4
docs: full rewrite of the template tag format guide
Dec 21, 2023
e7151b0
fix: typos
Dec 21, 2023
2cc1e39
docs: remove simply as it might not be simple for everyone
Dec 21, 2023
b78bbcb
docs: add nested component info
Dec 21, 2023
3e16462
docs: fix import path
Dec 21, 2023
11474cf
Update guides/release/components/template-tag-format.md
IgnaceMaes Dec 22, 2023
206b746
docs: reword tip sentence
Dec 22, 2023
aa39c52
fix: use your own app
Dec 22, 2023
a494d34
docs: rephrase sentence
Dec 22, 2023
c57a53c
fix: colon typo
Dec 22, 2023
a61262e
Apply suggestions from code review
IgnaceMaes Dec 27, 2023
0fa636e
refactor: use function over arrow syntax
Dec 27, 2023
11ff45a
feat: link to previous section
Dec 27, 2023
af94444
feat: add co-location RFC link
Dec 27, 2023
c03e910
docs: use relative imports
Dec 31, 2023
af128c4
Update guides/release/components/template-tag-format.md
IgnaceMaes Jan 22, 2024
4214351
docs: add link to rfc
Jan 22, 2024
4a24993
fix: include the in link
Jan 22, 2024
aeb3de2
Update guides/release/components/template-tag-format.md
IgnaceMaes Jan 23, 2024
ad2ebd6
docs: clarify invokables
Jan 23, 2024
294d26b
docs: make inline import blocks explicit
Jan 23, 2024
7cd69d0
docs: remove barrel file mention
Jan 23, 2024
b3d6344
docs: clarify rfc stage
Jan 23, 2024
57cdaa3
docs: add camelCase convention
Jan 23, 2024
dd0fc74
docs: callout installation
Jan 23, 2024
b3b707f
docs: make built-ins an actual code block
Jan 23, 2024
d7f6312
docs: fix internal link
Jan 23, 2024
9e40721
docs: reword
Jan 23, 2024
020308e
fix: typo
Jan 23, 2024
c68687a
docs: reword sentence on imports
Jan 23, 2024
5ea8d3d
deps: set override for ember-cli-showdown
Jan 25, 2024
6ad0e9b
Revert "deps: set override for ember-cli-showdown"
Jan 25, 2024
3a96d53
chore: point to vsc repo for textmate grammars
IgnaceMaes Feb 7, 2024
ceaa2e0
docs: add keywords section
IgnaceMaes Feb 7, 2024
7334378
Fix linting
mansona Feb 7, 2024
a406109
fix test
mansona Feb 7, 2024
41bd12b
fix node tests
mansona Feb 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 171 additions & 0 deletions guides/release/components/template-tag-format.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
The template tag component format allows defining a component's template using a `<template>` tag block. This block is a first-class participant in JavaScript and TypeScript with strict mode template semantics. For this new format, the extensions `.gjs` and `.gts` are used respectively.

Template tag components address a number of pain points when defining a template as a separate file, and provide a number of new capabilities:

- Accessing local JavaScript values requires no backing class, enabling much easier use of existing JavaScript ecosystem tools.
- Authoring more than one component in a single file, where colocation makes sense.
- Creating locally-scoped helpers, modifiers, and other JavaScript functionality *just works*.

## Installation

To use template tag components, install the [ember-template-imports](https://github.com/ember-template-imports/ember-template-imports) addon:

```bash
npm add --save-dev ember-template-imports
```

For integration with tools like Prettier and Glint, and information on version compatibility, refer to the addon's readme.

## Editor Integrations

To get syntax highlighting inside embedded templates and support for the GJS file extension, you may need to configure your editor.

### Visual Studio Code

The [Ember.js extension pack](https://marketplace.visualstudio.com/items?itemName=EmberTooling.emberjs) bundles everything you need to get started. More specifically, the [vscode-glimmer-syntax](https://marketplace.visualstudio.com/items?itemName=lifeart.vscode-glimmer-syntax) extension will add support for `glimmer-js` and `glimmer-ts` languages.

### Neovim

Here's an [example Neovim Config](https://github.com/NullVoxPopuli/dotfiles/blob/main/home/.config/nvim/lua/plugins/syntax.lua#L52) with support for good highlighting of embedded templates in JS and TS, using:

- [nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter)
- [tree-sitter-glimmer](https://github.com/alexlafroscia/tree-sitter-glimmer)

### Other editors

For other editors, you may be able to get support using one of these other syntax definitions:

- [TextMate](https://github.com/IgnaceMaes/glimmer-textmate-grammar)
- [TreeSitter](https://github.com/alexlafroscia/tree-sitter-glimmer)

## Using Template Tags and `.gjs`/`.gts` Files

The new `<template>` tag format is available in `.gjs` and `.gts` files. These file extensions represent a new file format "GlimmerJS" and "GlimmerTS", which are supersets of standard JavaScript and TypeScript respectively. In this syntax, templates are defined in JavaScript files directly.

This example defines a template-only component, which is the default export of hello.gjs:

```text {data-filename="app/components/hello.gjs"}
<template>
<span>Hello, {{@name}}!</span>
</template>
```

You would be able to use this component in another component like so:

```text {data-filename="app/components/world.gjs"}
import Hello from 'example-app/components/hello';

<template>
<Hello @name="world" />
</template>
```
You can also export the component explicitly:

```text {data-filename="components/hello.gjs"}
export default <template>
<span>Hello, {{@name}}!</span>
</template>;
```

Omitting the `export default` is just syntactic sugar. In addition, you can
define template-only components and assign them to variables, allowing you to
export components with named exports:

```text {data-filename="components/hello.gjs"}
export const First = <template>First</template>;

export const Second = <template>Second</template>;

export const Third = <template>Third</template>;
```

This also allows you to create components that are only used locally, in the
same file:

```text {data-filename="components/custom-select.gjs"}
const Option = <template>
<option selected={{@selected}} value={{@value}}>
{{or @title @value}}
</option>
</template>;

export const CustomSelect = <template>
<select>
{{#each @options as |opt|}}
<Option
@value={{opt.value}}
@selected={{eq opt @selectedOption}}
/>
{{/each}}
</select>
</template>;
```

Helpers and modifiers can also be defined in the same file as your components,
making them very flexible:

```text {data-filename="components/list.gjs"}
import { modifier } from 'ember-modifier';

const plusOne = (num) => num + 1;

const setScrollPosition = modifier((element, [position]) => {
element.scrollTop = position
});

<template>
<div class="scroll-container" {{setScrollPosition @scrollPos}}>
{{#each @items as |item index|}}
Item #{{plusOne index}}: {{item}}
{{/each}}
</div>
</template>
```

Finally, to associate a template with a class-based component, you can use the
template syntax directly in the class body:

```text {data-filename="components/hello.gjs"}
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { on } from '@ember/modifier';

export default class Hello extends Component {
@tracked count = 0;

increment = () => {
this.count += 1;
};

decrement = () => {
this.count -= 1;
};

<template>
<button {{on "click" this.increment}}>+</button>
Count: {{this.count}}
<button {{on "click" this.decrement}}>&minus;</button>
</template>
}
```

Template tag components can also be used for writing tests. In fact, this aligned syntax between app code and test code is one of the big advantages of the new authoring format.

Just like in app code, the template tag has access to the outer scope. This means you can reference variables directly in your tests:

```text {data-filename="tests/integration/components/hello-test.gjs"}
import Hello from 'example-app/components/hello';
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';

module('Integration | Component | hello', function (hooks) {
setupRenderingTest(hooks);

test('renders name argument', async function (assert) {
const name = 'world';
await render(<template><Hello @name={{name}} /></template>);
assert.dom('[data-test-id="some-selector"]').hasText(name);
});
});
```
2 changes: 2 additions & 0 deletions guides/release/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@
url: template-lifecycle-dom-and-modifiers
- title: "Built-in Components"
url: "built-in-components"
- title: "Template Tag Format"
url: "template-tag-format"
- title: "Routing"
url: "routing"
pages:
Expand Down