Skip to content

NullVoxPopuli/ember-template-imports

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

56 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ember-template-imports

This addon provides a number of different formats for using template imports within Ember!

import MyComponent from './my-component';

<template>
  <MyComponent/>
</template>

Template imports are an upcoming feature in Ember. Like Glimmer components, the primitive APIs for supporting imports were built before we decided on a final format for their high level usage. There are a number of different ideas for how we can integrate imports with templates, and the idea behind this addon is that it can be a test bed for them all. This way, we can share common tooling between solutions, and work together as a community as we explore the design space.

So far, this addon supports two different potential formats:

  • Template tags, embedded in .gjs files

    import MyComponent from './my-component';
    
    <template>
      <MyComponent/>
    </template>
  • Template literals, similar to the existing hbs helper in tests:

    import { hbs } from 'ember-template-imports';
    import MyComponent from './my-component';
    
    export default hbs`
      <MyComponent/>
    `;

For the previous version of this addon, see this repository. And huge thanks to @patricklx for his contributions here!

Using Template Tags and .gjs Files

Template tags are a new syntax introduced in .gjs, a new file format that is a superset of standard JavaScript. In this syntax, templates are defined in JavaScript files directly.

// components/hello.gjs
<template>
  <span>Hello, {{@name}}!</span>
</template>

This example defines a template-only component, which is the default export of hello.js. You would be able to use this component in another component like so:

// components/hello-world.gjs
import Hello from './hello';

<template>
  <Hello @name="world" />
</template>

You can also export the component explicitly:

// 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:

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:

const Option = <template>
  <option selected={{@selected}} value={{@value}}>
    {{or @title @value}}
  </option>
</template>

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

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

import { helper } from '@ember/component/helper';
import { modifier } from 'ember-modifier';

const plusOne = helper(([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:

// components/hello.gjs
export default class Hello {
  name = 'world';

  <template>
    <span>Hello, {{this.name}}!</span>
  </template>
}

Using Template Literals with hbs

Template literals are an existing JavaScript syntax that has been repurposed to define Ember templates. This syntax can be used in standard JavaScript files to define templates for Ember components.

// components/hello.js
import { hbs } from 'ember-template-imports';

export default hbs`
  <span>Hello, {{@name}}!</span>
`;

This example defines a template-only component, which is the default export of hello.js. You would be able to use this component in another component like so:

// components/hello-world.js
import { hbs } from 'ember-template-imports';
import Hello from './hello';

export default hbs`
  <Hello @name="world" />
`;

In addition, you can define template-only components and assign them to variables, allowing you to export components with named exports:

import { hbs } from 'ember-template-imports';

export const First = hbs`First`;

export const Second = hbs`Second`;

export const Third = hbs`Third`;

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

import { hbs } from 'ember-template-imports';

const Option = hbs`
  <option selected={{@selected}} value={{@value}}>
    {{or @title @value}}
  </option>
`;

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

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

import { hbs } from 'ember-template-imports';
import { helper } from '@ember/component/helper';
import { modifier } from 'ember-modifier';

const plusOne = helper(([num]) => num + 1);

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

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

Finally, to associate a template with a class-based component, you can assign the template to the static template property of the class:

// components/hello.js
import Component from '@glimmer/component';
import { hbs } from 'ember-template-imports';

export default class Hello extends Component {
  name = 'world';

  static template = hbs`
    <span>Hello, {{this.name}}!</span>
  `;
}

This template literal syntax has a few key differences and restrictions from standard JS template literal syntax:

  • Using template interpolations (${}) is disallowed. You cannot embed dynamic values in templates, they must be statically analyzable and compilable.
  • Templates are able to reference all variables that are in scope where they are defined. This is unlike normal template literals, which require you to interpolate a value using ${} to reference and use it.
  • The static template property of class components does not exist directly on the class. It is compiled away, and so it cannot be directly referenced or dynamically modified/assigned.

Compatibility

  • Ember.js v3.27 or above
  • Ember CLI v2.13 or above
  • ember-cli-htmlbars 6.0 or above
  • Node.js v12 or above

Installation

ember install ember-template-imports

Contributing

See the Contributing guide for details.

License

This project is licensed under the MIT License.

About

Template import support in Ember!

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 57.2%
  • TypeScript 38.0%
  • HTML 4.6%
  • Handlebars 0.2%