ember-source
Version:
A JavaScript framework for creating ambitious web applications
216 lines (190 loc) • 7.5 kB
JavaScript
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 };