UNPKG

ember-source

Version:

A JavaScript framework for creating ambitious web applications

216 lines (190 loc) 7.5 kB
import '../debug/index.js'; import { E as ENV } from '../../shared-chunks/env-CwR5CFCu.js'; import { s as setClassicDecorator, i as isElementDescriptor, e as expandProperties } from '../../shared-chunks/cache-Djf2I3Za.js'; export { c as computed, d as defineProperty, g as get, n as notifyPropertyChange } from '../../shared-chunks/cache-Djf2I3Za.js'; import '../-internals/meta/lib/meta.js'; import '../../@glimmer/validator/index.js'; import { s as setObservers } from '../../shared-chunks/mandatory-setter-BiXq-dpN.js'; import { isDevelopingApp } from '@embroider/macros'; import '../../@glimmer/destroyable/index.js'; import '../../@glimmer/manager/index.js'; export { s as set, t as trySet } from '../../shared-chunks/property_set-DS4X3Soy.js'; export { g as getProperties, s as setProperties } from '../../shared-chunks/set_properties-CeGJ0G9T.js'; import { g as getFactoryFor } from '../../shared-chunks/registry-B8WARvkP.js'; import CoreObject from './core.js'; import Observable from './observable.js'; import { assert } from '../debug/lib/assert.js'; /** @module @ember/object */ /** `EmberObject` is the main base class for all Ember objects. It is a subclass of `CoreObject` with the `Observable` mixin applied. For details, see the documentation for each of these. @class EmberObject @extends CoreObject @uses Observable @public */ // eslint-disable-next-line @typescript-eslint/no-empty-interface class EmberObject extends CoreObject.extend(Observable) { get _debugContainerKey() { let factory = getFactoryFor(this); return factory !== undefined && factory.fullName; } } /** Decorator that turns the target function into an Action which can be accessed directly by reference. ```js import Component from '@ember/component'; import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; export default class Tooltip extends Component { @tracked isShowing = false; @action toggleShowing() { this.isShowing = !this.isShowing; } } ``` ```hbs <!-- template.hbs --> <button {{on "click" this.toggleShowing}}>Show tooltip</button> {{#if isShowing}} <div class="tooltip"> I'm a tooltip! </div> {{/if}} ``` It also binds the function directly to the instance, so it can be used in any context and will correctly refer to the class it came from: ```js import Component from '@ember/component'; import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; export default class Tooltip extends Component { constructor() { super(...arguments); // this.toggleShowing is still bound correctly when added to // the event listener document.addEventListener('click', this.toggleShowing); } @tracked isShowing = false; @action toggleShowing() { this.isShowing = !this.isShowing; } } ``` @public @method action @for @ember/object @static @param {Function|undefined} callback The function to turn into an action, when used in classic classes @return {PropertyDecorator} property decorator instance */ const BINDINGS_MAP = new WeakMap(); function hasProto(obj) { return obj != null && obj.constructor !== undefined && typeof obj.constructor.proto === 'function'; } function setupAction(target, key, actionFn) { if (hasProto(target)) { target.constructor.proto(); } if (!Object.prototype.hasOwnProperty.call(target, 'actions')) { let parentActions = target.actions; // we need to assign because of the way mixins copy actions down when inheriting target.actions = parentActions ? Object.assign({}, parentActions) : {}; } (isDevelopingApp() && !(target.actions != null) && assert("[BUG] Somehow the target doesn't have actions!", target.actions != null)); target.actions[key] = actionFn; return { get() { let bindings = BINDINGS_MAP.get(this); if (bindings === undefined) { bindings = new Map(); BINDINGS_MAP.set(this, bindings); } let fn = bindings.get(actionFn); if (fn === undefined) { fn = actionFn.bind(this); bindings.set(actionFn, fn); } return fn; } }; } function action(...args) { let actionFn; if (!isElementDescriptor(args)) { actionFn = args[0]; let decorator = function (target, key, _desc, _meta, isClassicDecorator) { (isDevelopingApp() && !(isClassicDecorator) && assert('The @action decorator may only be passed a method when used in classic classes. You should decorate methods directly in native classes', isClassicDecorator)); (isDevelopingApp() && !(typeof actionFn === 'function') && assert('The action() decorator must be passed a method when used in classic classes', typeof actionFn === 'function')); return setupAction(target, key, actionFn); }; setClassicDecorator(decorator); return decorator; } let [target, key, desc] = args; actionFn = desc?.value; (isDevelopingApp() && !(typeof actionFn === 'function') && assert('The @action decorator must be applied to methods when used in native classes', typeof actionFn === 'function')); // SAFETY: TS types are weird with decorators. This should work. return setupAction(target, key, actionFn); } // SAFETY: TS types are weird with decorators. This should work. setClassicDecorator(action); // .......................................................... // OBSERVER HELPER // /** Specify a method that observes property changes. ```javascript import EmberObject from '@ember/object'; import { observer } from '@ember/object'; export default EmberObject.extend({ valueObserver: observer('value', function() { // Executes whenever the "value" property changes }) }); ``` Also available as `Function.prototype.observes` if prototype extensions are enabled. @method observer @for @ember/object @param {String} propertyNames* @param {Function} func @return func @public @static */ function observer(...args) { let funcOrDef = args.pop(); (isDevelopingApp() && !(typeof funcOrDef === 'function' || typeof funcOrDef === 'object' && funcOrDef !== null) && assert('observer must be provided a function or an observer definition', typeof funcOrDef === 'function' || typeof funcOrDef === 'object' && funcOrDef !== null)); let func; let dependentKeys; let sync; if (typeof funcOrDef === 'function') { func = funcOrDef; dependentKeys = args; sync = !ENV._DEFAULT_ASYNC_OBSERVERS; } else { func = funcOrDef.fn; dependentKeys = funcOrDef.dependentKeys; sync = funcOrDef.sync; } (isDevelopingApp() && !(typeof func === 'function') && assert('observer called without a function', typeof func === 'function')); (isDevelopingApp() && !(Array.isArray(dependentKeys) && dependentKeys.length > 0 && dependentKeys.every(p => typeof p === 'string' && Boolean(p.length))) && assert('observer called without valid path', Array.isArray(dependentKeys) && dependentKeys.length > 0 && dependentKeys.every(p => typeof p === 'string' && Boolean(p.length)))); (isDevelopingApp() && !(typeof sync === 'boolean') && assert('observer called without sync', typeof sync === 'boolean')); let paths = []; for (let dependentKey of dependentKeys) { expandProperties(dependentKey, path => paths.push(path)); } setObservers(func, { paths, sync }); return func; } export { action, EmberObject as default, observer };