UNPKG

ember-source

Version:

A JavaScript framework for creating ambitious web applications

132 lines (110 loc) 3.72 kB
import { meta } from '../@ember/-internals/meta/lib/meta.js'; import { isEmberArray } from '../@ember/array/-internals.js'; import '../@ember/-internals/environment/index.js'; import { trackedData, dirtyTagFor, consumeTag, tagFor } from '../@glimmer/validator/index.js'; import { S as SELF_TAG, C as CHAIN_PASS_THROUGH } from './observers-R1ZklwWy.js'; import { i as isElementDescriptor, s as setClassicDecorator, C as COMPUTED_SETTERS } from './decorator-BdDDBUd2.js'; /** @decorator @private Marks a property as tracked. By default, a component's properties are expected to be static, meaning you are not able to update them and have the template update accordingly. Marking a property as tracked means that when that property changes, a rerender of the component is scheduled so the template is kept up to date. There are two usages for the `@tracked` decorator, shown below. @example No dependencies If you don't pass an argument to `@tracked`, only changes to that property will be tracked: ```typescript import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; export default class MyComponent extends Component { @tracked remainingApples = 10 } ``` When something changes the component's `remainingApples` property, the rerender will be scheduled. @example Dependents In the case that you have a computed property that depends other properties, you want to track both so that when one of the dependents change, a rerender is scheduled. In the following example we have two properties, `eatenApples`, and `remainingApples`. ```typescript import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; const totalApples = 100; export default class MyComponent extends Component { @tracked eatenApples = 0 get remainingApples() { return totalApples - this.eatenApples; } increment() { this.eatenApples = this.eatenApples + 1; } } ``` @param dependencies Optional dependents to be tracked. */ function tracked(...args) { if (!isElementDescriptor(args)) { let propertyDesc = args[0]; let initializer = propertyDesc ? propertyDesc.initializer : undefined; let value = propertyDesc ? propertyDesc.value : undefined; let decorator = function (target, key, _desc, _meta, isClassicDecorator) { let fieldDesc = { initializer: initializer || (() => value) }; return descriptorForField([target, key, fieldDesc]); }; setClassicDecorator(decorator); return decorator; } return descriptorForField(args); } function descriptorForField([target, key, desc]) { let { getter, setter } = trackedData(key, desc ? desc.initializer : undefined); function get() { let value = getter(this); // Add the tag of the returned value if it is an array, since arrays // should always cause updates if they are consumed and then changed if (Array.isArray(value) || isEmberArray(value)) { consumeTag(tagFor(value, '[]')); } return value; } function set(newValue) { setter(this, newValue); dirtyTagFor(this, SELF_TAG); } let newDesc = { enumerable: true, configurable: true, isTracked: true, get, set }; COMPUTED_SETTERS.add(set); meta(target).writeDescriptors(key, new TrackedDescriptor(get, set)); return newDesc; } class TrackedDescriptor { constructor(_get, _set) { this._get = _get; this._set = _set; CHAIN_PASS_THROUGH.add(this); } get(obj) { return this._get.call(obj); } set(obj, _key, value) { this._set.call(obj, value); } } export { TrackedDescriptor as T, tracked as t };