惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

K
Kaspersky official blog
小众软件
小众软件
Engineering at Meta
Engineering at Meta
博客园 - 三生石上(FineUI控件)
WordPress大学
WordPress大学
G
Google Developers Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
V
V2EX
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Google DeepMind News
Google DeepMind News
Security Archives - TechRepublic
Security Archives - TechRepublic
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
C
Check Point Blog
aimingoo的专栏
aimingoo的专栏
罗磊的独立博客
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
MongoDB | Blog
MongoDB | Blog
L
LINUX DO - 热门话题
酷 壳 – CoolShell
酷 壳 – CoolShell
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
H
Help Net Security
Martin Fowler
Martin Fowler
G
GRAHAM CLULEY
Simon Willison's Weblog
Simon Willison's Weblog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - Franky
V
Vulnerabilities – Threatpost
云风的 BLOG
云风的 BLOG
博客园_首页
C
Cybersecurity and Infrastructure Security Agency CISA
量子位
Stack Overflow Blog
Stack Overflow Blog
Recent Announcements
Recent Announcements
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
I
Intezer
Scott Helme
Scott Helme
A
About on SuperTechFans
博客园 - 司徒正美
Hacker News: Ask HN
Hacker News: Ask HN
The GitHub Blog
The GitHub Blog
Forbes - Security
Forbes - Security
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
博客园 - 聂微东
人人都是产品经理
人人都是产品经理
The Cloudflare Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Spread Privacy
Spread Privacy
T
Tailwind CSS Blog
S
Security Affairs
宝玉的分享
宝玉的分享

Ember.js Blog

Ember 7.0 Released Announcing the Official TypeScript Types Public Preview Accessibility Working Group Update The 2020 Ember Roadmap Countdown to The New Year - Built-in Addons Countdown to The New Year - Ember Exam Countdown to The New Year - Ember Code Snippet Countdown to The New Year - Ember Changeset Countdown to The New Year - Ember In Viewport Countdown to The New Year - Ember CLI Update Countdown to The New Year - Ember Template Invocation Location Countdown to The New Year - Ember CLI TypeScript Countdown to The New Year - Ember Bootstrap and Ember Paper Countdown to The New Year - Ember CSS Modules Countdown to The New Year - Ember Mapbox GL Countdown to The New Year - Ember Shepherd Countdown to The New Year - Ember Template Lint Countdown to The New Year - Ember Composable Helpers Countdown to The New Year - Ember Leaflet Countdown to The New Year - Ember Intl Countdown to The New Year - Ember Test Selectors Countdown to The New Year - Ember Power Select Countdown to The New Year - Ember Simple Auth Countdown to The New Year - Ember SVG Jar Countdown to The New Year - Ember Page Title Countdown to The New Year- Ember A11Y Testing Countdown to The New Year - Ember Angle Brackets Codemod Countdown to The New Year - Ember CLI Sass Countdown to The New Year - Ember Animated Countdown to The New Year - Ember Auto Import Countdown to The New Year - Ember Concurrency Countdown to The New Year - Ember Tether Countdown to The New Year - Ember Modifier Countdown to The New Year - Ember CLI Mirage Countdown to The New Year - Ember Sortable Coming Soon in Ember Octane - Part 5: Glimmer Components Coming Soon in Ember Octane - Part 4: Modifiers Coming Soon in Ember Octane - Part 3: Tracked Properties Coming Soon in Ember Octane - Part 2: Angle Brackets & Named Arguments Preview Weekend: 2019 Ember Community Survey Coming Soon in Ember Octane - Part 1: Native Classes First Annual DecEmber Event! 2018 Ember Community Survey 2017 Ember Community Survey Announcing The Glimmer 2 Alpha Upcoming deprecation of baseURL in Ember CLI 2.7 2016 Ember Community Survey Announcing Ember Core Team Face to Face, January 2016 Ember.js 1.13.0 and 2.0 Beta Released Another Ember 2.x Status Update Ember.js 1.12 and 1.13 Beta (Glimmer!) Released Ember.js 1.11.1 Released Ember.js 1.11.0 and 1.12 Beta Released Ember.js 1.10.0 and 1.11 Beta Released Compiling templates with Ember 1.10 Core Team Meeting Minutes - 2014/08/01 Core Team Meeting Minutes - 2014/08/14 Core Team Meeting Minutes - 2014/09/12 Cleaning Up Github Issues Core Team Meeting Minutes - 2014/07/11 Core Team Meeting Minutes - 2014/07/25 Core Team Meeting Minutes - 2014/06/13 Core Team Meeting Minutes - 2014/06/20 Core Team Meeting Minutes - 2014/06/27 Core Team Meeting Minutes - 2014/06/06 Core Team Meeting Minutes - 2014/04/25 Core Team Meeting Minutes - 2014/04/04 Ember 1.5.0 and 1.6 Beta Released Core Team Meeting Minutes - 2014/03/07 Core Team Meeting Minutes - 2014/03/14 Core Team Meeting Minutes - 2014/03/21 Core Team Meeting Minutes - 2014/02/28 Core Team Meeting Minutes - 2014/02/21 Core Team Meeting Minutes - 2014/02/14 Ember 1.4.0 and 1.5 Beta Released Core Team Meeting Minutes - 2014/01/27 Core Team Meeting Minutes - 2014/01/31 Core Team Meeting Minutes - 2014/02/07 Core Team Meeting Minutes - 2014/01/17 Core Team Meeting Minutes - 2014/01/03 Ember 1.3.0 and 1.4 Beta Released Core Team Meeting Minutes - 2013/12/20 Core Team Meeting Minutes - 2013/12/06 Ember 1.2.0 and 1.3 Beta Released Ember 1.1.2 Released Ember 1.1.1 and 1.2 Beta Released Ember 1.0 Released Ember 1.0 RC8 Released Ember 1.0 RC7 Released Ember 1.0 RC6.1, RC5.1, RC4.1, RC3.1, RC2.1 RC1.1 Released Ember 1.0 RC6 Ember 1.0 RC5 Ember 1.0 RC4 Ember 1.0 RC3 Announcing the Ember.js Security Policy Ember 1.0 RC2 Ember 1.0 RC Ember 1.0 Prerelease 2 Ember 1.0 Prerelease
Plain Old Functions as Helpers
2022-08-04 · via Ember.js Blog

Ember 4.5 introduced a long-awaited feature: the ability to use normal JavaScript functions as helpers.

Today, this means two things:

  1. You can use (bound) methods on your backing class as helpers directly.
  2. You can define function-based helpers without importing and using helper().

In the future, it will also work extra nicely with the upcoming <template> feature!

Let's see what each of those looks like.

Using methods as helpers

Ember developers have often wanted to be able to call methods on a backing class as helpers. This often comes up in an {{#each}} loop where you want to calculate some value using state in the backing class for each item. You could work around this by introducing some other intermediate object via a getter, or by extracting a helper you could call with the state and the item from the loop… but now you can call a method exactly like you would in JS code!

Here's a simplified example with a component which can choose to hide items from a list passed in.

Backing class:

import Component from '@glimmer/component';
import { TrackedSet } from 'tracked-built-ins';

export default class HideableList extends Component {
  hiddenItems = new TrackedSet();

  shouldShow = (item) => !this.hiddenItems.has(item);

  hide = (item) => {
    this.hiddenItems.add(item);
  };
}

Template:

<ul>
  {{#each @items as |item|}}
    {{#if (this.shouldShow item)}}
      <li>
        {{item.details}}

        <button type='button' {{on "click" (fn this.hide item)}}>
          Hide
        </button>
      </li>
    {{/if}}
  {{/each}}
</ul>

Notice here that you can do what people have always felt like you should be able to do: {{#if (this.shouldShow item)}}!

In this example I've used arrow functions to bind the methods. Methods do have to be bound, or this won't work! You could also use @action here, and that will do the trick—but it feels a bit weird to put @action on shouldShow(). The Ember Framework team has discussed this little corner, and I expect we will update our teaching and approach here as part of the road to Polaris. There are a lot of options here, including:

  • using arrow functions like this example shows
  • introducing a @bound decorator to replace @action
  • introducing other decorators, to distinguish between “actions” and things which “read” tracked state
  • making direct this.someMethod access “just work”
  • combinations of some or all of the above

We’ll have more to say on that in the future!

Standalone functions as helpers

Instead of writing helper(function(pos, named) { ... }) you can define a function normally and make it the default export from a file in your app or addon's helpers directory. For example, here’s how you might have defined a parse-int helper before:

// app/helpers/parse-int.js
import { helper } from '@ember/component/helper';

export default helper(function parseInt([numberToParse], { radix = 10 }) {
  return Number.parseInt(numberToParse, radix);
});

Here's how we would define that now that we have the ability to use normal functions as helpers:

// app/helpers/parse-int.js
export default function parseInt(numberToParse, { radix = 10 }) {
  return Number.parseInt(numberToParse, radix);
}

Using that in the template is exactly the same either way:

{{parse-int "1234" radix=4}}

Rules for helpers

A helper defined as a plain-old function takes all positional arguments as normal arguments, with a final named argument as its last argument:

function exampleHelper(positional1, positional2, named) {
  if (named.someNamedArg) {
    // ...
  }
}

Note for migrating: In traditional Ember helpers, the positional and named arguments are always passed to the function—if the caller did not pass them, they are present but empty. By contrast, plain-old function helpers work exactly like JavaScript functions: if you want a default value, you need to supply it in the JavaScript.

Helpers with only positional args

For a helper with only positional args, define it like you would a normal JavaScript function with a list of parameters:

export default function add(a, b) {
  return a + b;
}

Helpers with only named args

For a helper with only named args, define your function with an options object as its first argument, the same way you would in normal JavaScript:

export default function onlyNamed({ someOption, anotherOption }) {
  // ...
}

Coming soon

When we land the <template> tag feature (check out First-Class Component Templates for details), you'll also be able to define helpers locally to a module:

const add = (a, b) => a + b;

<template>
  <ul>
    {{#each @numbers as |number index|}}
      <li>{{add number index}}</li>
    {{/each}}
  </ul>
</template>

You can try that out today by installing ember-template-imports, and we’re working on getting that into a stable version of Ember itself soon!

Questions you might have

How do I get started with this?

If you’re on Ember 4.5 or later, you can start using this feature as shown above, no other steps required. If you’re on Ember 3.25 or higher, you can install and use the official polyfill by running ember install ember-functions-as-helper-polyfill. Once you’re on 4.5 or later, the polyfill uses the native implementation instead, so you can remove it separately from doing an upgrade.

Thanks to @nullvoxpopuli for doing the work of getting the polyfill implemented, and to both @nullvoxpopuli and Godfrey Chan (@chancancode) for helping get the feature landed in Ember itself!

Is there a codemod?

Not yet! This one would be pretty straightforward to write, though, so if you’re interested, let us know in #topic-codemods on the Ember Discord. This would be a great place to dip your toes into contributing!

Can I use built-in methods automatically?

In the method example above, I showed using this.shouldShow as a helper. In normal JS you could call this.hiddenItems.has(item), the way shouldShow() does. It seems like this should work in templates too:

{{#each @items as |item|}}
  {{#if (this.hiddenItems.has item)}}
    ...
  {{/if}}
{{/each}}

Unfortunately, this does not work yet, even though it feels like it should. The template language does not yet make sure that the binding through this.hiddenItems is preserved the way it would be in a JavaScript context. The Framework Core team has started exploring how to make that work, and we are hoping to have a good solution in place for Polaris!

Are traditional helpers going away?

No, helpers defined using the helper() function will continue to work exactly like they always have. We expect that over time, most of the community will move to using normal JavaScript functions this way. If or when it makes sense, we may deprecate and remove helper()—but only when it makes sense, and following Ember’s standard SemVer policies as always.