1 | declare 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 | }
|