UNPKG

17.6 kBTypeScriptView Raw
1declare module '@ember/helper' {
2 /**
3 @module @ember/helper
4 */
5 import {
6 setHelperManager as glimmerSetHelperManager,
7 helperCapabilities,
8 } from '@glimmer/manager';
9 import { invokeHelper as glimmerInvokeHelper } from '@glimmer/runtime';
10 import { uniqueId as glimmerUniqueId } from '@ember/-internals/glimmer';
11 import { type Opaque } from '@ember/-internals/utility-types';
12 /**
13 `capabilities` returns a capabilities configuration which can be used to modify
14 the behavior of the manager. Manager capabilities _must_ be provided using the
15 `capabilities` function, as the underlying implementation can change over time.
16
17 The first argument to capabilities is a version string, which is the version of
18 Ember that the capabilities were defined in. Ember can add new versions at any
19 time, and these may have entirely different behaviors, but it will not remove
20 old versions until the next major version.
21
22 ```js
23 capabilities('3.23');
24 ```
25
26 The second argument is an object of capabilities and boolean values indicating
27 whether they are enabled or disabled.
28
29 ```js
30 capabilities('3.23', {
31 hasValue: true,
32 hasDestructor: true,
33 });
34 ```
35
36 If no value is specified, then the default value will be used.
37
38 ### `3.23` capabilities
39
40 #### `hasDestroyable`
41
42 - Default value: false
43
44 Determines if the helper has a destroyable to include in the destructor
45 hierarchy. If enabled, the `getDestroyable` hook will be called, and its result
46 will be associated with the destroyable parent block.
47
48 #### `hasValue`
49
50 - Default value: false
51
52 Determines if the helper has a value which can be used externally. The helper's
53 `getValue` hook will be run whenever the value of the helper is accessed if this
54 capability is enabled.
55
56 @method capabilities
57 @for @ember/helper
58 @static
59 @param {String} managerApiVersion The version of capabilities that are being used
60 @param options The capabilities values
61 @return {Capabilities} The capabilities object instance
62 @public
63 */
64 export const capabilities: typeof helperCapabilities;
65 /**
66 Sets the helper manager for an object or function.
67
68 ```js
69 setHelperManager((owner) => new ClassHelperManager(owner), Helper)
70 ```
71
72 When a value is used as a helper in a template, the helper manager is looked up
73 on the object by walking up its prototype chain and finding the first helper
74 manager. This manager then receives the value and can create and manage an
75 instance of a helper from it. This provides a layer of indirection that allows
76 users to design high-level helper APIs, without Ember needing to worry about the
77 details. High-level APIs can be experimented with and iterated on while the
78 core of Ember helpers remains stable, and new APIs can be introduced gradually
79 over time to existing code bases.
80
81 `setHelperManager` receives two arguments:
82
83 1. A factory function, which receives the `owner` and returns an instance of a
84 helper manager.
85 2. A helper definition, which is the object or function to associate the factory function with.
86
87 The first time the object is looked up, the factory function will be called to
88 create the helper manager. It will be cached, and in subsequent lookups the
89 cached helper manager will be used instead.
90
91 Only one helper manager is guaranteed to exist per `owner` and per usage of
92 `setHelperManager`, so many helpers will end up using the same instance of the
93 helper manager. As such, you should only store state that is related to the
94 manager itself. If you want to store state specific to a particular helper
95 definition, you should assign a unique helper manager to that helper. In
96 general, most managers should either be stateless, or only have the `owner` they
97 were created with as state.
98
99 Helper managers must fulfill the following interface (This example uses
100 [TypeScript interfaces](https://www.typescriptlang.org/docs/handbook/interfaces.html)
101 for precision, you do not need to write helper managers using TypeScript):
102
103 ```ts
104 interface HelperManager<HelperStateBucket> {
105 capabilities: HelperCapabilities;
106
107 createHelper(definition: HelperDefinition, args: TemplateArgs): HelperStateBucket;
108
109 getValue?(bucket: HelperStateBucket): unknown;
110
111 runEffect?(bucket: HelperStateBucket): void;
112
113 getDestroyable?(bucket: HelperStateBucket): object;
114 }
115 ```
116
117 The capabilities property _must_ be provided using the `capabilities()` function
118 imported from the same module as `setHelperManager`:
119
120 ```js
121 import { capabilities } from '@ember/helper';
122
123 class MyHelperManager {
124 capabilities = capabilities('3.21.0', { hasValue: true });
125
126 // ...snip...
127 }
128 ```
129
130 Below is a description of each of the methods on the interface and their
131 functions.
132
133 #### `createHelper`
134
135 `createHelper` is a required hook on the HelperManager interface. The hook is
136 passed the definition of the helper that is currently being created, and is
137 expected to return a _state bucket_. This state bucket is what represents the
138 current state of the helper, and will be passed to the other lifecycle hooks at
139 appropriate times. It is not necessarily related to the definition of the
140 helper itself - for instance, you could return an object _containing_ an
141 instance of the helper:
142
143 ```js
144 class MyManager {
145 createHelper(Definition, args) {
146 return {
147 instance: new Definition(args);
148 };
149 }
150 }
151 ```
152
153 This allows the manager to store metadata that it doesn't want to expose to the
154 user.
155
156 This hook is _not_ autotracked - changes to tracked values used within this hook
157 will _not_ result in a call to any of the other lifecycle hooks. This is because
158 it is unclear what should happen if it invalidates, and rather than make a
159 decision at this point, the initial API is aiming to allow as much expressivity
160 as possible. This could change in the future with changes to capabilities and
161 their behaviors.
162
163 If users do want to autotrack some values used during construction, they can
164 either create the instance of the helper in `runEffect` or `getValue`, or they
165 can use the `cache` API to autotrack the `createHelper` hook themselves. This
166 provides maximum flexibility and expressiveness to manager authors.
167
168 This hook has the following timing semantics:
169
170 **Always**
171 - called as discovered during DOM construction
172 - called in definition order in the template
173
174 #### `getValue`
175
176 `getValue` is an optional hook that should return the value of the helper. This
177 is the value that is returned from the helper and passed into the template.
178
179 This hook is called when the value is requested from the helper (e.g. when the
180 template is rendering and the helper value is needed). The hook is autotracked,
181 and will rerun whenever any tracked values used inside of it are updated.
182 Otherwise it does not rerun.
183
184 > Note: This means that arguments which are not _consumed_ within the hook will
185 > not trigger updates.
186
187 This hook is only called for helpers with the `hasValue` capability enabled.
188 This hook has the following timing semantics:
189
190 **Always**
191 - called the first time the helper value is requested
192 - called after autotracked state has changed
193
194 **Never**
195 - called if the `hasValue` capability is disabled
196
197 #### `runEffect`
198
199 `runEffect` is an optional hook that should run the effect that the helper is
200 applying, setting it up or updating it.
201
202 This hook is scheduled to be called some time after render and prior to paint.
203 There is not a guaranteed, 1-to-1 relationship between a render pass and this
204 hook firing. For instance, multiple render passes could occur, and the hook may
205 only trigger once. It may also never trigger if it was dirtied in one render
206 pass and then destroyed in the next.
207
208 The hook is autotracked, and will rerun whenever any tracked values used inside
209 of it are updated. Otherwise it does not rerun.
210
211 The hook is also run during a time period where state mutations are _disabled_
212 in Ember. Any tracked state mutation will throw an error during this time,
213 including changes to tracked properties, changes made using `Ember.set`, updates
214 to computed properties, etc. This is meant to prevent infinite rerenders and
215 other antipatterns.
216
217 This hook is only called for helpers with the `hasScheduledEffect` capability
218 enabled. This hook is also not called in SSR currently, though this could be
219 added as a capability in the future. It has the following timing semantics:
220
221 **Always**
222 - called after the helper was first created, if the helper has not been
223 destroyed since creation
224 - called after autotracked state has changed, if the helper has not been
225 destroyed during render
226
227 **Never**
228 - called if the `hasScheduledEffect` capability is disabled
229 - called in SSR
230
231 #### `getDestroyable`
232
233 `getDestroyable` is an optional hook that users can use to register a
234 destroyable object for the helper. This destroyable will be registered to the
235 containing block or template parent, and will be destroyed when it is destroyed.
236 See the [Destroyables RFC](https://github.com/emberjs/rfcs/blob/master/text/0580-destroyables.md)
237 for more details.
238
239 `getDestroyable` is only called if the `hasDestroyable` capability is enabled.
240
241 This hook has the following timing semantics:
242
243 **Always**
244 - called immediately after the `createHelper` hook is called
245
246 **Never**
247 - called if the `hasDestroyable` capability is disabled
248
249 @method setHelperManager
250 @for @ember/helper
251 @static
252 @param {Function} factory A factory function which receives an optional owner, and returns a helper manager
253 @param {object} definition The definition to associate the manager factory with
254 @return {object} The definition passed into setHelperManager
255 @public
256 */
257 export const setHelperManager: typeof glimmerSetHelperManager;
258 /**
259 The `invokeHelper` function can be used to create a helper instance in
260 JavaScript.
261
262 To access a helper's value you have to use `getValue` from
263 `@glimmer/tracking/primitives/cache`.
264
265 ```js
266 // app/components/data-loader.js
267 import Component from '@glimmer/component';
268 import { getValue } from '@glimmer/tracking/primitives/cache';
269 import Helper from '@ember/component/helper';
270 import { invokeHelper } from '@ember/helper';
271
272 class PlusOne extends Helper {
273 compute([number]) {
274 return number + 1;
275 }
276 }
277
278 export default class PlusOneComponent extends Component {
279 plusOne = invokeHelper(this, PlusOne, () => {
280 return {
281 positional: [this.args.number],
282 };
283 });
284
285 get value() {
286 return getValue(this.plusOne);
287 }
288 }
289 ```
290 ```js
291 {{this.value}}
292 ```
293
294 It receives three arguments:
295
296 * `context`: The parent context of the helper. When the parent is torn down and
297 removed, the helper will be as well.
298 * `definition`: The definition of the helper.
299 * `computeArgs`: An optional function that produces the arguments to the helper.
300 The function receives the parent context as an argument, and must return an
301 object with a `positional` property that is an array and/or a `named`
302 property that is an object.
303
304 And it returns a Cache instance that contains the most recent value of the
305 helper. You can access the helper using `getValue()` like any other cache. The
306 cache is also destroyable, and using the `destroy()` function on it will cause
307 the helper to be torn down.
308
309 Note that using `getValue()` on helpers that have scheduled effects will not
310 trigger the effect early. Effects will continue to run at their scheduled time.
311
312 @method invokeHelper
313 @for @ember/helper
314 @static
315 @param {object} context The parent context of the helper
316 @param {object} definition The helper definition
317 @param {Function} computeArgs An optional function that produces args
318 @returns
319 @public
320 */
321 export const invokeHelper: typeof glimmerInvokeHelper;
322 /**
323 * Using the `{{hash}}` helper, you can pass objects directly from the template
324 * as an argument to your components.
325 *
326 * ```
327 * import { hash } from '@ember/helper';
328 *
329 * <template>
330 * {{#each-in (hash givenName='Jen' familyName='Weber') as |key value|}}
331 * <p>{{key}}: {{value}}</p>
332 * {{/each-in}}
333 * </template>
334 * ```
335 *
336 * **NOTE:** this example uses the experimental `<template>` feature, which is
337 * the only place you need to import `hash` to use it (it is a built-in when
338 * writing standalone `.hbs` files).
339 */
340 export const hash: HashHelper;
341 export interface HashHelper extends Opaque<'helper:hash'> {}
342 /**
343 * Using the `{{array}}` helper, you can pass arrays directly from the template
344 * as an argument to your components.
345 *
346 * ```js
347 * import { array } from '@ember/helper';
348 *
349 * <template>
350 * <ul>
351 * {{#each (array 'Tom Dale' 'Yehuda Katz' @anotherPerson) as |person|}}
352 * <li>{{person}}</li>
353 * {{/each}}
354 * </ul>
355 * </template>
356 *
357 * **NOTE:** this example uses the experimental `<template>` feature, which is
358 * the only place you need to import `array` to use it (it is a built-in when
359 * writing standalone `.hbs` files).
360 * ```
361 */
362 export const array: ArrayHelper;
363 export interface ArrayHelper extends Opaque<'helper:array'> {}
364 /**
365 * The `{{concat}}` helper makes it easy to dynamically send a number of
366 * parameters to a component or helper as a single parameter in the format of a
367 * concatenated string.
368 *
369 * For example:
370 *
371 * ```js
372 * import { concat } from '@ember/helper';
373 *
374 * <template>
375 * {{get @foo (concat "item" @index)}}
376 * </template>
377 * ```
378 *
379 * This will display the result of `@foo.item1` when `index` is `1`, and
380 * `this.foo.item2` when `index` is `2`, etc.
381 *
382 * **NOTE:** this example uses the experimental `<template>` feature, which is
383 * the only place you need to import `concat` to use it (it is a built-in when
384 * writing standalone `.hbs` files).
385 */
386 export const concat: ConcatHelper;
387 export interface ConcatHelper extends Opaque<'helper:concat'> {}
388 /**
389 * The `{{get}}` helper makes it easy to dynamically look up a property on an
390 * object or an element in an array. The second argument to `{{get}}` can be a
391 * string or a number, depending on the object being accessed.
392 *
393 * To access a property on an object with a string key:
394 *
395 * ```js
396 * import { get } from '@ember/helper';
397 *
398 * <template>
399 * {{get @someObject "objectKey"}}
400 * </template>
401 * ```
402 *
403 * To access the first element in an array:
404 *
405 * ```js
406 * import { get } from '@ember/helper';
407 *
408 * <template>
409 * {{get @someArray 0}}
410 * </template>
411 * ```
412 *
413 * To access a property on an object with a dynamic key:
414 *
415 * ```js
416 * import { get } from '@ember/helper';
417 *
418 * <template>
419 * {{get @address @field}}
420 * </template>
421 * ```
422 *
423 * This will display the result of `@foo.item1` when `index` is `1`, and
424 * `this.foo.item2` when `index` is `2`, etc.
425 *
426 * **NOTE:** this example uses the experimental `<template>` feature, which is
427 * the only place you need to import `concat` to use it (it is a built-in when
428 * writing standalone `.hbs` files).
429 */
430 export const get: GetHelper;
431 export interface GetHelper extends Opaque<'helper:get'> {}
432 /**
433 * `{{fn}}` is a helper that receives a function and some arguments, and returns
434 * a new function that combines. This allows you to pass parameters along to
435 * functions in your templates:
436 *
437 * ```js
438 * import { fn } from '@ember/helper';
439 *
440 * function showAlert(message) {
441 * alert(`The message is: '${message}'`);
442 * }
443 *
444 * <template>
445 * <button type="button" {{on "click" (fn showAlert "Hello!")}}>
446 * Click me!
447 * </button>
448 * </template>
449 * ```
450 */
451 export const fn: FnHelper;
452 export interface FnHelper extends Opaque<'helper:fn'> {}
453 /**
454 * Use the {{uniqueId}} helper to generate a unique ID string suitable for use as
455 * an ID attribute in the DOM.
456 *
457 * Each invocation of {{uniqueId}} will return a new, unique ID string.
458 * You can use the `let` helper to create an ID that can be reused within a template.
459 *
460 * ```js
461 * import { uniqueId } from '@ember/helper';
462 *
463 * <template>
464 * {{#let (uniqueId) as |emailId|}}
465 * <label for={{emailId}}>Email address</label>
466 * <input id={{emailId}} type="email" />
467 * {{/let}}
468 * </template>
469 * ```
470 */
471 export const uniqueId: typeof glimmerUniqueId;
472 export type UniqueIdHelper = typeof uniqueId;
473}