ember-source 3.28.5 → 4.0.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +113 -10
- package/blueprints/acceptance-test/qunit-files/tests/acceptance/__name__-test.js +1 -1
- package/blueprints/acceptance-test/qunit-rfc-232-files/tests/acceptance/__name__-test.js +1 -1
- package/blueprints/component-test/qunit-files/__root__/__testType__/__path__/__test__.js +3 -3
- package/blueprints/helper-test/index.js +4 -22
- package/blueprints/helper-test/mocha-0.12-files/__root__/__testType__/__collection__/__name__-test.js +1 -13
- package/blueprints/helper-test/mocha-files/__root__/__testType__/__collection__/__name__-test.js +0 -13
- package/blueprints/helper-test/mocha-rfc-232-files/__root__/__testType__/__collection__/__name__-test.js +2 -12
- package/blueprints/helper-test/qunit-files/__root__/__testType__/__collection__/__name__-test.js +2 -13
- package/blueprints/helper-test/qunit-rfc-232-files/__root__/__testType__/__collection__/__name__-test.js +2 -14
- package/blueprints/initializer/files/__root__/initializers/__name__.js +0 -1
- package/blueprints/instance-initializer/files/__root__/instance-initializers/__name__.js +0 -1
- package/build-metadata.json +3 -3
- package/dist/dependencies/@glimmer/manager.js +19 -98
- package/dist/dependencies/@glimmer/opcode-compiler.js +9 -146
- package/dist/dependencies/@glimmer/runtime.js +1 -55
- package/dist/dependencies/@glimmer/validator.js +19 -51
- package/dist/ember-template-compiler.js +3692 -5667
- package/dist/ember-template-compiler.map +1 -1
- package/dist/ember-testing.js +12 -560
- package/dist/ember-testing.map +1 -1
- package/dist/ember.debug.js +4357 -12470
- package/dist/ember.debug.map +1 -1
- package/dist/header/license.js +1 -1
- package/dist/packages/@ember/-internals/bootstrap/index.js +2 -55
- package/dist/packages/@ember/-internals/container/index.js +35 -206
- package/dist/packages/@ember/-internals/environment/index.js +3 -46
- package/dist/packages/@ember/-internals/extension-support/lib/container_debug_adapter.js +7 -0
- package/dist/packages/@ember/-internals/extension-support/lib/data_adapter.js +1 -2
- package/dist/packages/@ember/-internals/glimmer/index.js +4774 -7035
- package/dist/packages/@ember/-internals/meta/lib/meta.js +2 -50
- package/dist/packages/@ember/-internals/metal/index.js +785 -1109
- package/dist/packages/@ember/-internals/overrides/index.js +1 -3
- package/dist/packages/@ember/-internals/owner/index.js +1 -19
- package/dist/packages/@ember/-internals/routing/lib/location/auto_location.js +1 -51
- package/dist/packages/@ember/-internals/routing/lib/location/hash_location.js +1 -0
- package/dist/packages/@ember/-internals/routing/lib/location/history_location.js +3 -1
- package/dist/packages/@ember/-internals/routing/lib/location/none_location.js +0 -7
- package/dist/packages/@ember/-internals/routing/lib/services/router.js +34 -2
- package/dist/packages/@ember/-internals/routing/lib/services/routing.js +2 -3
- package/dist/packages/@ember/-internals/routing/lib/system/dsl.js +5 -6
- package/dist/packages/@ember/-internals/routing/lib/system/route.js +192 -652
- package/dist/packages/@ember/-internals/routing/lib/system/router.js +119 -248
- package/dist/packages/@ember/-internals/routing/lib/system/router_state.js +1 -2
- package/dist/packages/@ember/-internals/routing/lib/utils.js +1 -2
- package/dist/packages/@ember/-internals/runtime/index.js +1 -4
- package/dist/packages/@ember/-internals/runtime/lib/mixins/array.js +4 -175
- package/dist/packages/@ember/-internals/runtime/lib/mixins/observable.js +1 -18
- package/dist/packages/@ember/-internals/runtime/lib/mixins/target_action_support.js +3 -43
- package/dist/packages/@ember/-internals/runtime/lib/system/array_proxy.js +8 -15
- package/dist/packages/@ember/-internals/runtime/lib/system/core_object.js +10 -141
- package/dist/packages/@ember/-internals/utils/index.js +3 -59
- package/dist/packages/@ember/-internals/views/index.js +0 -2
- package/dist/packages/@ember/-internals/views/lib/mixins/action_support.js +1 -121
- package/dist/packages/@ember/-internals/views/lib/mixins/view_support.js +4 -40
- package/dist/packages/@ember/-internals/views/lib/system/event_dispatcher.js +115 -190
- package/dist/packages/@ember/-internals/views/lib/views/states/destroying.js +1 -2
- package/dist/packages/@ember/-internals/views/lib/views/states/has_element.js +1 -2
- package/dist/packages/@ember/-internals/views/lib/views/states/in_dom.js +1 -2
- package/dist/packages/@ember/-internals/views/lib/views/states/pre_render.js +1 -2
- package/dist/packages/@ember/application/instance.js +3 -25
- package/dist/packages/@ember/application/lib/application.js +14 -32
- package/dist/packages/@ember/canary-features/index.js +5 -6
- package/dist/packages/@ember/component/index.js +1 -1
- package/dist/packages/@ember/component/template-only.js +2 -0
- package/dist/packages/@ember/debug/lib/deprecate.js +7 -41
- package/dist/packages/@ember/deprecated-features/index.js +1 -14
- package/dist/packages/@ember/engine/index.js +3 -17
- package/dist/packages/@ember/engine/instance.js +0 -4
- package/dist/packages/@ember/helper/index.js +12 -4
- package/dist/packages/@ember/object/index.js +3 -101
- package/dist/packages/@ember/object/internals.js +0 -1
- package/dist/packages/@ember/object/lib/computed/computed_macros.js +12 -384
- package/dist/packages/@ember/object/lib/computed/reduce_computed_macros.js +9 -360
- package/dist/packages/@ember/polyfills/index.js +1 -6
- package/dist/packages/@ember/polyfills/lib/assign.js +12 -28
- package/dist/packages/@ember/routing/index.js +1 -1
- package/dist/packages/@ember/runloop/index.js +1 -83
- package/dist/packages/@ember/string/index.js +1 -206
- package/dist/packages/@ember/utils/index.js +0 -1
- package/dist/packages/ember/index.js +17 -180
- package/dist/packages/ember/version.js +1 -1
- package/dist/packages/ember-testing/index.js +0 -2
- package/dist/packages/ember-testing/lib/helpers.js +0 -12
- package/dist/packages/ember-testing/lib/setup_for_testing.js +0 -10
- package/docs/data.json +1637 -3659
- package/lib/index.js +14 -59
- package/lib/overrides.js +0 -61
- package/lib/transforms/inject-babel-helpers.js +1 -3
- package/package.json +27 -23
- package/dist/packages/@ember/-internals/console/index.js +0 -190
- package/dist/packages/@ember/-internals/runtime/lib/copy.js +0 -119
- package/dist/packages/@ember/-internals/runtime/lib/ext/function.js +0 -155
- package/dist/packages/@ember/-internals/runtime/lib/mixins/copyable.js +0 -31
- package/dist/packages/@ember/-internals/views/lib/mixins/text_support.js +0 -357
- package/dist/packages/@ember/-internals/views/lib/system/jquery.js +0 -26
- package/dist/packages/@ember/-internals/views/lib/system/jquery_event_deprecation.js +0 -60
- package/dist/packages/@ember/application/deprecations.js +0 -25
- package/dist/packages/@ember/application/globals-resolver.js +0 -434
- package/dist/packages/@ember/application/resolver.js +0 -1
- package/dist/packages/@ember/component/checkbox.js +0 -17
- package/dist/packages/@ember/component/text-area.js +0 -17
- package/dist/packages/@ember/component/text-field.js +0 -17
- package/dist/packages/@ember/polyfills/lib/merge.js +0 -50
- package/dist/packages/@ember/routing/link-component.js +0 -17
- package/dist/packages/ember-testing/lib/events.js +0 -102
- package/dist/packages/ember-testing/lib/helpers/-is-form-control.js +0 -19
- package/dist/packages/ember-testing/lib/helpers/click.js +0 -32
- package/dist/packages/ember-testing/lib/helpers/fill_in.js +0 -47
- package/dist/packages/ember-testing/lib/helpers/find.js +0 -40
- package/dist/packages/ember-testing/lib/helpers/find_with_assert.js +0 -37
- package/dist/packages/ember-testing/lib/helpers/key_event.js +0 -37
- package/dist/packages/ember-testing/lib/helpers/trigger_event.js +0 -61
- package/dist/packages/ember-testing/lib/support.js +0 -57
- package/dist/packages/jquery/index.js +0 -2
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { meta, peekMeta } from '@ember/-internals/meta';
|
|
2
|
-
import { setListeners, isObject, setupMandatorySetter, symbol, toString, enumerableSymbol,
|
|
3
|
-
import { assert, deprecate,
|
|
2
|
+
import { setListeners, isObject, setupMandatorySetter, symbol, toString, enumerableSymbol, inspect, setWithMandatorySetter, Cache, isEmberArray, setProxy, lookupDescriptor, getName, setName, guidFor, makeArray, observerListenerMetaFor, ROOT, setObservers, wrap } from '@ember/-internals/utils';
|
|
3
|
+
import { assert, deprecate, debug, warn } from '@ember/debug';
|
|
4
4
|
import { ENV, context } from '@ember/-internals/environment';
|
|
5
5
|
import { schedule } from '@ember/runloop';
|
|
6
6
|
import { registerDestructor, isDestroyed } from '@glimmer/destroyable';
|
|
7
|
-
import { CURRENT_TAG, tagMetaFor, validateTag, valueForTag, CONSTANT_TAG, dirtyTagFor, tagFor, combine, createUpdatableTag, updateTag,
|
|
7
|
+
import { CURRENT_TAG, tagMetaFor, validateTag, valueForTag, CONSTANT_TAG, dirtyTagFor, tagFor, combine, createUpdatableTag, updateTag, ALLOW_CYCLES, consumeTag, track, untrack, isTracking, trackedData } from '@glimmer/validator';
|
|
8
8
|
export { createCache, getValue, isConst } from '@glimmer/validator';
|
|
9
9
|
import { DEBUG } from '@glimmer/env';
|
|
10
10
|
import { getCustomTagFor } from '@glimmer/manager';
|
|
@@ -12,8 +12,6 @@ import { _WeakSet } from '@glimmer/util';
|
|
|
12
12
|
import EmberError from '@ember/error';
|
|
13
13
|
import VERSION from 'ember/version';
|
|
14
14
|
import { INIT_FACTORY } from '@ember/-internals/container';
|
|
15
|
-
import { ALIAS_METHOD } from '@ember/deprecated-features';
|
|
16
|
-
import { assign } from '@ember/polyfills';
|
|
17
15
|
import { getOwner } from '@ember/-internals/owner';
|
|
18
16
|
|
|
19
17
|
/**
|
|
@@ -653,43 +651,29 @@ function replaceInNativeArray(array, start, deleteCount, items) {
|
|
|
653
651
|
arrayContentDidChange(array, start, deleteCount, items.length);
|
|
654
652
|
}
|
|
655
653
|
|
|
656
|
-
function arrayObserversHelper(obj, target, opts, operation
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
let
|
|
654
|
+
function arrayObserversHelper(obj, target, opts, operation) {
|
|
655
|
+
var _a;
|
|
656
|
+
|
|
657
|
+
let {
|
|
658
|
+
willChange,
|
|
659
|
+
didChange
|
|
660
|
+
} = opts;
|
|
660
661
|
operation(obj, '@array:before', target, willChange);
|
|
661
662
|
operation(obj, '@array:change', target, didChange);
|
|
663
|
+
/*
|
|
664
|
+
* Array proxies have a `_revalidate` method which must be called to set
|
|
665
|
+
* up their internal array observation systems.
|
|
666
|
+
*/
|
|
662
667
|
|
|
663
|
-
|
|
664
|
-
notifyPropertyChange(obj, 'hasArrayObservers');
|
|
665
|
-
}
|
|
666
|
-
|
|
668
|
+
(_a = obj._revalidate) === null || _a === void 0 ? void 0 : _a.call(obj);
|
|
667
669
|
return obj;
|
|
668
670
|
}
|
|
669
671
|
|
|
670
|
-
function addArrayObserver(array, target, opts
|
|
671
|
-
|
|
672
|
-
id: 'array-observers',
|
|
673
|
-
url: 'https://deprecations.emberjs.com/v3.x#toc_array-observers',
|
|
674
|
-
until: '4.0.0',
|
|
675
|
-
for: 'ember-source',
|
|
676
|
-
since: {
|
|
677
|
-
enabled: '3.26.0-beta.1'
|
|
678
|
-
}
|
|
679
|
-
});
|
|
680
|
-
return arrayObserversHelper(array, target, opts, addListener, false);
|
|
672
|
+
function addArrayObserver(array, target, opts) {
|
|
673
|
+
return arrayObserversHelper(array, target, opts, addListener);
|
|
681
674
|
}
|
|
682
|
-
function removeArrayObserver(array, target, opts
|
|
683
|
-
|
|
684
|
-
id: 'array-observers',
|
|
685
|
-
url: 'https://deprecations.emberjs.com/v3.x#toc_array-observers',
|
|
686
|
-
until: '4.0.0',
|
|
687
|
-
for: 'ember-source',
|
|
688
|
-
since: {
|
|
689
|
-
enabled: '3.26.0-beta.1'
|
|
690
|
-
}
|
|
691
|
-
});
|
|
692
|
-
return arrayObserversHelper(array, target, opts, removeListener, true);
|
|
675
|
+
function removeArrayObserver(array, target, opts) {
|
|
676
|
+
return arrayObserversHelper(array, target, opts, removeListener);
|
|
693
677
|
}
|
|
694
678
|
|
|
695
679
|
const CHAIN_PASS_THROUGH = new _WeakSet();
|
|
@@ -743,16 +727,7 @@ function getChainTags(chainTags, obj, path, tagMeta, meta$$1) {
|
|
|
743
727
|
|
|
744
728
|
if (segment === '@each' && segmentEnd !== pathLength) {
|
|
745
729
|
lastSegmentEnd = segmentEnd + 1;
|
|
746
|
-
segmentEnd = path.indexOf('.', lastSegmentEnd);
|
|
747
|
-
|
|
748
|
-
deprecate(`When using @each in a dependent-key or an observer, ` + `you can only chain one property level deep after ` + `the @each. That is, \`${path.slice(0, segmentEnd)}\` ` + `is allowed but \`${path}\` (which is what you passed) ` + `is not.\n\n` + `This was never supported. Currently, the extra segments ` + `are silently ignored, i.e. \`${path}\` behaves exactly ` + `the same as \`${path.slice(0, segmentEnd)}\`. ` + `In the future, this will throw an error.\n\n` + `If the current behavior is acceptable for your use case, ` + `please remove the extraneous segments by changing your ` + `key to \`${path.slice(0, segmentEnd)}\`. ` + `Otherwise, please create an intermediary computed property ` + `or switch to using tracked properties.`, segmentEnd === -1, {
|
|
749
|
-
until: '3.17.0',
|
|
750
|
-
id: 'ember-metal.computed-deep-each',
|
|
751
|
-
for: 'ember-source',
|
|
752
|
-
since: {
|
|
753
|
-
enabled: '3.13.0-beta.3'
|
|
754
|
-
}
|
|
755
|
-
});
|
|
730
|
+
segmentEnd = path.indexOf('.', lastSegmentEnd);
|
|
756
731
|
let arrLength = current.length;
|
|
757
732
|
|
|
758
733
|
if (typeof arrLength !== 'number' || // TODO: should the second test be `isEmberArray` instead?
|
|
@@ -1068,1161 +1043,967 @@ function dive(prefix, pattern, start, callback) {
|
|
|
1068
1043
|
/**
|
|
1069
1044
|
@module @ember/object
|
|
1070
1045
|
*/
|
|
1071
|
-
/**
|
|
1072
|
-
NOTE: This is a low-level method used by other parts of the API. You almost
|
|
1073
|
-
never want to call this method directly. Instead you should use
|
|
1074
|
-
`mixin()` to define new properties.
|
|
1075
1046
|
|
|
1076
|
-
|
|
1077
|
-
`Object.defineProperty()` method except that it can also accept computed
|
|
1078
|
-
properties and other special descriptors.
|
|
1047
|
+
const DEEP_EACH_REGEX = /\.@each\.[^.]+\./;
|
|
1079
1048
|
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1049
|
+
function noop() {}
|
|
1050
|
+
/**
|
|
1051
|
+
`@computed` is a decorator that turns a JavaScript getter and setter into a
|
|
1052
|
+
computed property, which is a _cached, trackable value_. By default the getter
|
|
1053
|
+
will only be called once and the result will be cached. You can specify
|
|
1054
|
+
various properties that your computed property depends on. This will force the
|
|
1055
|
+
cached result to be cleared if the dependencies are modified, and lazily recomputed the next time something asks for it.
|
|
1084
1056
|
|
|
1085
|
-
|
|
1057
|
+
In the following example we decorate a getter - `fullName` - by calling
|
|
1058
|
+
`computed` with the property dependencies (`firstName` and `lastName`) as
|
|
1059
|
+
arguments. The `fullName` getter will be called once (regardless of how many
|
|
1060
|
+
times it is accessed) as long as its dependencies do not change. Once
|
|
1061
|
+
`firstName` or `lastName` are updated any future calls to `fullName` will
|
|
1062
|
+
incorporate the new values, and any watchers of the value such as templates
|
|
1063
|
+
will be updated:
|
|
1086
1064
|
|
|
1087
1065
|
```javascript
|
|
1088
|
-
import {
|
|
1066
|
+
import { computed, set } from '@ember/object';
|
|
1089
1067
|
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1068
|
+
class Person {
|
|
1069
|
+
constructor(firstName, lastName) {
|
|
1070
|
+
set(this, 'firstName', firstName);
|
|
1071
|
+
set(this, 'lastName', lastName);
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
@computed('firstName', 'lastName')
|
|
1075
|
+
get fullName() {
|
|
1076
|
+
return `${this.firstName} ${this.lastName}`;
|
|
1077
|
+
}
|
|
1096
1078
|
});
|
|
1097
1079
|
|
|
1098
|
-
|
|
1099
|
-
defineProperty(contact, 'lastName', undefined, 'Jolley');
|
|
1080
|
+
let tom = new Person('Tom', 'Dale');
|
|
1100
1081
|
|
|
1101
|
-
//
|
|
1102
|
-
defineProperty(contact, 'fullName', computed('firstName', 'lastName', function() {
|
|
1103
|
-
return this.firstName+' '+this.lastName;
|
|
1104
|
-
}));
|
|
1082
|
+
tom.fullName; // 'Tom Dale'
|
|
1105
1083
|
```
|
|
1106
1084
|
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
@for @ember/object
|
|
1111
|
-
@param {Object} obj the object to define this property on. This may be a prototype.
|
|
1112
|
-
@param {String} keyName the name of the property
|
|
1113
|
-
@param {Descriptor} [desc] an instance of `Descriptor` (typically a
|
|
1114
|
-
computed property) or an ES5 descriptor.
|
|
1115
|
-
You must provide this or `data` but not both.
|
|
1116
|
-
@param {*} [data] something other than a descriptor, that will
|
|
1117
|
-
become the explicit value of this property.
|
|
1118
|
-
*/
|
|
1085
|
+
You can also provide a setter, which will be used when updating the computed
|
|
1086
|
+
property. Ember's `set` function must be used to update the property
|
|
1087
|
+
since it will also notify observers of the property:
|
|
1119
1088
|
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
let previousDesc = descriptorForProperty(obj, keyName, meta$$1);
|
|
1123
|
-
let wasDescriptor = previousDesc !== undefined;
|
|
1089
|
+
```javascript
|
|
1090
|
+
import { computed, set } from '@ember/object';
|
|
1124
1091
|
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1092
|
+
class Person {
|
|
1093
|
+
constructor(firstName, lastName) {
|
|
1094
|
+
set(this, 'firstName', firstName);
|
|
1095
|
+
set(this, 'lastName', lastName);
|
|
1096
|
+
}
|
|
1128
1097
|
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
} else {
|
|
1134
|
-
// fallback to ES5
|
|
1135
|
-
Object.defineProperty(obj, keyName, desc);
|
|
1136
|
-
} // if key is being watched, override chains that
|
|
1137
|
-
// were initialized with the prototype
|
|
1098
|
+
@computed('firstName', 'lastName')
|
|
1099
|
+
get fullName() {
|
|
1100
|
+
return `${this.firstName} ${this.lastName}`;
|
|
1101
|
+
}
|
|
1138
1102
|
|
|
1103
|
+
set fullName(value) {
|
|
1104
|
+
let [firstName, lastName] = value.split(' ');
|
|
1139
1105
|
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
}
|
|
1144
|
-
function defineDecorator(obj, keyName, desc, meta$$1) {
|
|
1145
|
-
let propertyDesc;
|
|
1106
|
+
set(this, 'firstName', firstName);
|
|
1107
|
+
set(this, 'lastName', lastName);
|
|
1108
|
+
}
|
|
1109
|
+
});
|
|
1146
1110
|
|
|
1147
|
-
|
|
1148
|
-
propertyDesc = desc(obj, keyName, undefined, meta$$1, true);
|
|
1149
|
-
} else {
|
|
1150
|
-
propertyDesc = desc(obj, keyName, undefined, meta$$1);
|
|
1151
|
-
}
|
|
1111
|
+
let person = new Person();
|
|
1152
1112
|
|
|
1153
|
-
|
|
1113
|
+
set(person, 'fullName', 'Peter Wagenet');
|
|
1114
|
+
person.firstName; // 'Peter'
|
|
1115
|
+
person.lastName; // 'Wagenet'
|
|
1116
|
+
```
|
|
1154
1117
|
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1118
|
+
You can also pass a getter function or object with `get` and `set` functions
|
|
1119
|
+
as the last argument to the computed decorator. This allows you to define
|
|
1120
|
+
computed property _macros_:
|
|
1121
|
+
|
|
1122
|
+
```js
|
|
1123
|
+
import { computed } from '@ember/object';
|
|
1124
|
+
|
|
1125
|
+
function join(...keys) {
|
|
1126
|
+
return computed(...keys, function() {
|
|
1127
|
+
return keys.map(key => this[key]).join(' ');
|
|
1164
1128
|
});
|
|
1165
|
-
} else {
|
|
1166
|
-
if (DEBUG) {
|
|
1167
|
-
setWithMandatorySetter(obj, keyName, value);
|
|
1168
|
-
} else {
|
|
1169
|
-
obj[keyName] = value;
|
|
1170
|
-
}
|
|
1171
1129
|
}
|
|
1172
1130
|
|
|
1173
|
-
|
|
1174
|
-
|
|
1131
|
+
class Person {
|
|
1132
|
+
@join('firstName', 'lastName')
|
|
1133
|
+
fullName;
|
|
1134
|
+
}
|
|
1135
|
+
```
|
|
1175
1136
|
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1137
|
+
Note that when defined this way, getters and setters receive the _key_ of the
|
|
1138
|
+
property they are decorating as the first argument. Setters receive the value
|
|
1139
|
+
they are setting to as the second argument instead. Additionally, setters must
|
|
1140
|
+
_return_ the value that should be cached:
|
|
1180
1141
|
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
*/
|
|
1184
|
-
const PROXY_CONTENT = symbol('PROXY_CONTENT');
|
|
1185
|
-
let getPossibleMandatoryProxyValue;
|
|
1142
|
+
```javascript
|
|
1143
|
+
import { computed, set } from '@ember/object';
|
|
1186
1144
|
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1145
|
+
function fullNameMacro(firstNameKey, lastNameKey) {
|
|
1146
|
+
return computed(firstNameKey, lastNameKey, {
|
|
1147
|
+
get() {
|
|
1148
|
+
return `${this[firstNameKey]} ${this[lastNameKey]}`;
|
|
1149
|
+
}
|
|
1190
1150
|
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1151
|
+
set(key, value) {
|
|
1152
|
+
let [firstName, lastName] = value.split(' ');
|
|
1153
|
+
|
|
1154
|
+
set(this, firstNameKey, firstName);
|
|
1155
|
+
set(this, lastNameKey, lastName);
|
|
1156
|
+
|
|
1157
|
+
return value;
|
|
1158
|
+
}
|
|
1159
|
+
});
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
class Person {
|
|
1163
|
+
constructor(firstName, lastName) {
|
|
1164
|
+
set(this, 'firstName', firstName);
|
|
1165
|
+
set(this, 'lastName', lastName);
|
|
1196
1166
|
}
|
|
1197
|
-
};
|
|
1198
|
-
} // ..........................................................
|
|
1199
|
-
// GET AND SET
|
|
1200
|
-
//
|
|
1201
|
-
// If we are on a platform that supports accessors we can use those.
|
|
1202
|
-
// Otherwise simulate accessors by looking up the property directly on the
|
|
1203
|
-
// object.
|
|
1204
1167
|
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
the function will be invoked. If the property is not defined but the
|
|
1208
|
-
object implements the `unknownProperty` method then that will be invoked.
|
|
1168
|
+
@fullNameMacro('firstName', 'lastName') fullName;
|
|
1169
|
+
});
|
|
1209
1170
|
|
|
1210
|
-
|
|
1211
|
-
import { get } from '@ember/object';
|
|
1212
|
-
get(obj, "name");
|
|
1213
|
-
```
|
|
1171
|
+
let person = new Person();
|
|
1214
1172
|
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1173
|
+
set(person, 'fullName', 'Peter Wagenet');
|
|
1174
|
+
person.firstName; // 'Peter'
|
|
1175
|
+
person.lastName; // 'Wagenet'
|
|
1176
|
+
```
|
|
1219
1177
|
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
to
|
|
1223
|
-
|
|
1178
|
+
Computed properties can also be used in classic classes. To do this, we
|
|
1179
|
+
provide the getter and setter as the last argument like we would for a macro,
|
|
1180
|
+
and we assign it to a property on the class definition. This is an _anonymous_
|
|
1181
|
+
computed macro:
|
|
1224
1182
|
|
|
1225
|
-
|
|
1226
|
-
|
|
1183
|
+
```javascript
|
|
1184
|
+
import EmberObject, { computed, set } from '@ember/object';
|
|
1227
1185
|
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
@param {String} keyName The property key to retrieve
|
|
1233
|
-
@return {Object} the property value or `null`.
|
|
1234
|
-
@public
|
|
1235
|
-
*/
|
|
1186
|
+
let Person = EmberObject.extend({
|
|
1187
|
+
// these will be supplied by `create`
|
|
1188
|
+
firstName: null,
|
|
1189
|
+
lastName: null,
|
|
1236
1190
|
|
|
1191
|
+
fullName: computed('firstName', 'lastName', {
|
|
1192
|
+
get() {
|
|
1193
|
+
return `${this.firstName} ${this.lastName}`;
|
|
1194
|
+
}
|
|
1237
1195
|
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
assert(`Cannot call get with '${keyName}' on an undefined object.`, obj !== undefined && obj !== null);
|
|
1241
|
-
assert(`The key provided to get must be a string or number, you passed ${keyName}`, typeof keyName === 'string' || typeof keyName === 'number' && !isNaN(keyName));
|
|
1242
|
-
assert(`'this' in paths is not supported`, typeof keyName !== 'string' || keyName.lastIndexOf('this.', 0) !== 0);
|
|
1243
|
-
return isPath(keyName) ? _getPath(obj, keyName) : _getProp(obj, keyName);
|
|
1244
|
-
}
|
|
1245
|
-
function _getProp(obj, keyName) {
|
|
1246
|
-
let type = typeof obj;
|
|
1247
|
-
let isObject$$1 = type === 'object';
|
|
1248
|
-
let isFunction = type === 'function';
|
|
1249
|
-
let isObjectLike = isObject$$1 || isFunction;
|
|
1250
|
-
let value;
|
|
1196
|
+
set(key, value) {
|
|
1197
|
+
let [firstName, lastName] = value.split(' ');
|
|
1251
1198
|
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
value = getPossibleMandatoryProxyValue(obj, keyName);
|
|
1255
|
-
} else {
|
|
1256
|
-
value = obj[keyName];
|
|
1257
|
-
}
|
|
1199
|
+
set(this, 'firstName', firstName);
|
|
1200
|
+
set(this, 'lastName', lastName);
|
|
1258
1201
|
|
|
1259
|
-
|
|
1260
|
-
if (DEBUG) {
|
|
1261
|
-
deprecateMutationsInTrackingTransaction(() => {
|
|
1262
|
-
value = obj.unknownProperty(keyName);
|
|
1263
|
-
});
|
|
1264
|
-
} else {
|
|
1265
|
-
value = obj.unknownProperty(keyName);
|
|
1202
|
+
return value;
|
|
1266
1203
|
}
|
|
1267
|
-
}
|
|
1204
|
+
})
|
|
1205
|
+
});
|
|
1268
1206
|
|
|
1269
|
-
|
|
1270
|
-
|
|
1207
|
+
let tom = Person.create({
|
|
1208
|
+
firstName: 'Tom',
|
|
1209
|
+
lastName: 'Dale'
|
|
1210
|
+
});
|
|
1271
1211
|
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
// should always cause updates if they are consumed and then changed
|
|
1275
|
-
consumeTag(tagFor(value, '[]'));
|
|
1276
|
-
}
|
|
1277
|
-
}
|
|
1278
|
-
} else {
|
|
1279
|
-
value = obj[keyName];
|
|
1280
|
-
}
|
|
1212
|
+
tom.get('fullName') // 'Tom Dale'
|
|
1213
|
+
```
|
|
1281
1214
|
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
let obj = root;
|
|
1286
|
-
let parts = typeof path === 'string' ? path.split('.') : path;
|
|
1215
|
+
You can overwrite computed property without setters with a normal property (no
|
|
1216
|
+
longer computed) that won't change if dependencies change. You can also mark
|
|
1217
|
+
computed property as `.readOnly()` and block all attempts to set it.
|
|
1287
1218
|
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
return undefined;
|
|
1291
|
-
}
|
|
1219
|
+
```javascript
|
|
1220
|
+
import { computed, set } from '@ember/object';
|
|
1292
1221
|
|
|
1293
|
-
|
|
1294
|
-
|
|
1222
|
+
class Person {
|
|
1223
|
+
constructor(firstName, lastName) {
|
|
1224
|
+
set(this, 'firstName', firstName);
|
|
1225
|
+
set(this, 'lastName', lastName);
|
|
1226
|
+
}
|
|
1295
1227
|
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1228
|
+
@computed('firstName', 'lastName').readOnly()
|
|
1229
|
+
get fullName() {
|
|
1230
|
+
return `${this.firstName} ${this.lastName}`;
|
|
1231
|
+
}
|
|
1232
|
+
});
|
|
1301
1233
|
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
getWithDefault(person, 'lastName', 'Doe');
|
|
1234
|
+
let person = new Person();
|
|
1235
|
+
person.set('fullName', 'Peter Wagenet'); // Uncaught Error: Cannot set read-only property "fullName" on object: <(...):emberXXX>
|
|
1305
1236
|
```
|
|
1306
1237
|
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
@
|
|
1313
|
-
@return {Object} The property value or the defaultValue.
|
|
1238
|
+
Additional resources:
|
|
1239
|
+
- [Decorators RFC](https://github.com/emberjs/rfcs/blob/master/text/0408-decorators.md)
|
|
1240
|
+
- [New CP syntax RFC](https://github.com/emberjs/rfcs/blob/master/text/0011-improved-cp-syntax.md)
|
|
1241
|
+
- [New computed syntax explained in "Ember 1.12 released" ](https://emberjs.com/blog/2015/05/13/ember-1-12-released.html#toc_new-computed-syntax)
|
|
1242
|
+
|
|
1243
|
+
@class ComputedProperty
|
|
1314
1244
|
@public
|
|
1315
|
-
@deprecated
|
|
1316
1245
|
*/
|
|
1317
1246
|
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1247
|
+
|
|
1248
|
+
class ComputedProperty extends ComputedDescriptor {
|
|
1249
|
+
constructor(args) {
|
|
1250
|
+
super();
|
|
1251
|
+
this._readOnly = false;
|
|
1252
|
+
this._hasConfig = false;
|
|
1253
|
+
this._getter = undefined;
|
|
1254
|
+
this._setter = undefined;
|
|
1255
|
+
let maybeConfig = args[args.length - 1];
|
|
1256
|
+
|
|
1257
|
+
if (typeof maybeConfig === 'function' || maybeConfig !== null && typeof maybeConfig === 'object') {
|
|
1258
|
+
this._hasConfig = true;
|
|
1259
|
+
let config = args.pop();
|
|
1260
|
+
|
|
1261
|
+
if (typeof config === 'function') {
|
|
1262
|
+
assert(`You attempted to pass a computed property instance to computed(). Computed property instances are decorator functions, and cannot be passed to computed() because they cannot be turned into decorators twice`, !isClassicDecorator(config));
|
|
1263
|
+
this._getter = config;
|
|
1264
|
+
} else {
|
|
1265
|
+
const objectConfig = config;
|
|
1266
|
+
assert('computed expects a function or an object as last argument.', typeof objectConfig === 'object' && !Array.isArray(objectConfig));
|
|
1267
|
+
assert('Config object passed to computed can only contain `get` and `set` keys.', Object.keys(objectConfig).every(key => key === 'get' || key === 'set'));
|
|
1268
|
+
assert('Computed properties must receive a getter or a setter, you passed none.', Boolean(objectConfig.get) || Boolean(objectConfig.set));
|
|
1269
|
+
this._getter = objectConfig.get || noop;
|
|
1270
|
+
this._setter = objectConfig.set;
|
|
1271
|
+
}
|
|
1326
1272
|
}
|
|
1327
|
-
});
|
|
1328
|
-
let value = get(root, key);
|
|
1329
1273
|
|
|
1330
|
-
|
|
1331
|
-
|
|
1274
|
+
if (args.length > 0) {
|
|
1275
|
+
this._property(...args);
|
|
1276
|
+
}
|
|
1332
1277
|
}
|
|
1333
1278
|
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1279
|
+
setup(obj, keyName, propertyDesc, meta$$1) {
|
|
1280
|
+
super.setup(obj, keyName, propertyDesc, meta$$1);
|
|
1281
|
+
assert(`@computed can only be used on accessors or fields, attempted to use it with ${keyName} but that was a method. Try converting it to a getter (e.g. \`get ${keyName}() {}\`)`, !(propertyDesc && typeof propertyDesc.value === 'function'));
|
|
1282
|
+
assert(`@computed can only be used on empty fields. ${keyName} has an initial value (e.g. \`${keyName} = someValue\`)`, !propertyDesc || !propertyDesc.initializer);
|
|
1283
|
+
assert(`Attempted to apply a computed property that already has a getter/setter to a ${keyName}, but it is a method or an accessor. If you passed @computed a function or getter/setter (e.g. \`@computed({ get() { ... } })\`), then it must be applied to a field`, !(this._hasConfig && propertyDesc && (typeof propertyDesc.get === 'function' || typeof propertyDesc.set === 'function')));
|
|
1338
1284
|
|
|
1339
|
-
|
|
1285
|
+
if (this._hasConfig === false) {
|
|
1286
|
+
assert(`Attempted to use @computed on ${keyName}, but it did not have a getter or a setter. You must either pass a get a function or getter/setter to @computed directly (e.g. \`@computed({ get() { ... } })\`) or apply @computed directly to a getter/setter`, propertyDesc && (typeof propertyDesc.get === 'function' || typeof propertyDesc.set === 'function'));
|
|
1287
|
+
let {
|
|
1288
|
+
get,
|
|
1289
|
+
set
|
|
1290
|
+
} = propertyDesc;
|
|
1340
1291
|
|
|
1341
|
-
|
|
1292
|
+
if (get !== undefined) {
|
|
1293
|
+
this._getter = get;
|
|
1294
|
+
}
|
|
1342
1295
|
|
|
1343
|
-
|
|
1296
|
+
if (set !== undefined) {
|
|
1297
|
+
this._setter = function setterWrapper(_key, value) {
|
|
1298
|
+
let ret = set.call(this, value);
|
|
1344
1299
|
|
|
1345
|
-
|
|
1346
|
-
|
|
1300
|
+
if (get !== undefined) {
|
|
1301
|
+
return typeof ret === 'undefined' ? get.call(this) : ret;
|
|
1302
|
+
}
|
|
1347
1303
|
|
|
1348
|
-
|
|
1304
|
+
return ret;
|
|
1305
|
+
};
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1349
1309
|
|
|
1350
|
-
|
|
1351
|
-
|
|
1310
|
+
_property(...passedArgs) {
|
|
1311
|
+
let args = [];
|
|
1352
1312
|
|
|
1353
|
-
|
|
1313
|
+
function addArg(property) {
|
|
1314
|
+
assert(`Dependent keys containing @each only work one level deep. ` + `You used the key "${property}" which is invalid. ` + `Please create an intermediary computed property or ` + `switch to using tracked properties.`, DEEP_EACH_REGEX.test(property) === false);
|
|
1315
|
+
args.push(property);
|
|
1316
|
+
}
|
|
1354
1317
|
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
setProxy(fakeProxy);
|
|
1359
|
-
track(() => _getProp({}, 'a'));
|
|
1360
|
-
track(() => _getProp({}, 1));
|
|
1361
|
-
track(() => _getProp({
|
|
1362
|
-
a: []
|
|
1363
|
-
}, 'a'));
|
|
1364
|
-
track(() => _getProp({
|
|
1365
|
-
a: fakeProxy
|
|
1366
|
-
}, 'a'));
|
|
1318
|
+
for (let i = 0; i < passedArgs.length; i++) {
|
|
1319
|
+
expandProperties(passedArgs[i], addArg);
|
|
1320
|
+
}
|
|
1367
1321
|
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
*/
|
|
1322
|
+
this._dependentKeys = args;
|
|
1323
|
+
}
|
|
1371
1324
|
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
will be invoked with the two parameters `keyName` and `value`.
|
|
1325
|
+
get(obj, keyName) {
|
|
1326
|
+
let meta$$1 = meta(obj);
|
|
1327
|
+
let tagMeta = tagMetaFor(obj);
|
|
1328
|
+
let propertyTag = tagFor(obj, keyName, tagMeta);
|
|
1329
|
+
let ret;
|
|
1330
|
+
let revision = meta$$1.revisionFor(keyName);
|
|
1379
1331
|
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1332
|
+
if (revision !== undefined && validateTag(propertyTag, revision)) {
|
|
1333
|
+
ret = meta$$1.valueFor(keyName);
|
|
1334
|
+
} else {
|
|
1335
|
+
// For backwards compatibility, we only throw if the CP has any dependencies. CPs without dependencies
|
|
1336
|
+
// should be allowed, even after the object has been destroyed, which is why we check _dependentKeys.
|
|
1337
|
+
assert(`Attempted to access the computed ${obj}.${keyName} on a destroyed object, which is not allowed`, this._dependentKeys === undefined || !isDestroyed(obj));
|
|
1338
|
+
let {
|
|
1339
|
+
_getter,
|
|
1340
|
+
_dependentKeys
|
|
1341
|
+
} = this; // Create a tracker that absorbs any trackable actions inside the CP
|
|
1384
1342
|
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
@param {Object} obj The object to modify.
|
|
1389
|
-
@param {String} keyName The property key to set
|
|
1390
|
-
@param {Object} value The value to set
|
|
1391
|
-
@return {Object} the passed value.
|
|
1392
|
-
@public
|
|
1393
|
-
*/
|
|
1343
|
+
untrack(() => {
|
|
1344
|
+
ret = _getter.call(obj, keyName);
|
|
1345
|
+
});
|
|
1394
1346
|
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
assert(`Cannot call set with '${keyName}' on an undefined object.`, obj && typeof obj === 'object' || typeof obj === 'function');
|
|
1398
|
-
assert(`The key provided to set must be a string or number, you passed ${keyName}`, typeof keyName === 'string' || typeof keyName === 'number' && !isNaN(keyName));
|
|
1399
|
-
assert(`'this' in paths is not supported`, typeof keyName !== 'string' || keyName.lastIndexOf('this.', 0) !== 0);
|
|
1347
|
+
if (_dependentKeys !== undefined) {
|
|
1348
|
+
updateTag(propertyTag, getChainTagsForKeys(obj, _dependentKeys, tagMeta, meta$$1));
|
|
1400
1349
|
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1350
|
+
if (DEBUG) {
|
|
1351
|
+
ALLOW_CYCLES.set(propertyTag, true);
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1405
1354
|
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1355
|
+
meta$$1.setValueFor(keyName, ret);
|
|
1356
|
+
meta$$1.setRevisionFor(keyName, valueForTag(propertyTag));
|
|
1357
|
+
finishLazyChains(meta$$1, keyName, ret);
|
|
1358
|
+
}
|
|
1410
1359
|
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
return value;
|
|
1414
|
-
}
|
|
1360
|
+
consumeTag(propertyTag); // Add the tag of the returned value if it is an array, since arrays
|
|
1361
|
+
// should always cause updates if they are consumed and then changed
|
|
1415
1362
|
|
|
1416
|
-
|
|
1363
|
+
if (Array.isArray(ret)) {
|
|
1364
|
+
consumeTag(tagFor(ret, '[]'));
|
|
1365
|
+
}
|
|
1417
1366
|
|
|
1418
|
-
|
|
1419
|
-
currentValue = getPossibleMandatoryProxyValue(obj, keyName);
|
|
1420
|
-
} else {
|
|
1421
|
-
currentValue = obj[keyName];
|
|
1367
|
+
return ret;
|
|
1422
1368
|
}
|
|
1423
1369
|
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
} else {
|
|
1428
|
-
if (DEBUG) {
|
|
1429
|
-
setWithMandatorySetter(obj, keyName, value);
|
|
1430
|
-
} else {
|
|
1431
|
-
obj[keyName] = value;
|
|
1432
|
-
}
|
|
1433
|
-
|
|
1434
|
-
if (currentValue !== value) {
|
|
1435
|
-
notifyPropertyChange(obj, keyName);
|
|
1370
|
+
set(obj, keyName, value) {
|
|
1371
|
+
if (this._readOnly) {
|
|
1372
|
+
this._throwReadOnlyError(obj, keyName);
|
|
1436
1373
|
}
|
|
1437
|
-
}
|
|
1438
1374
|
|
|
1439
|
-
|
|
1440
|
-
|
|
1375
|
+
assert(`Cannot override the computed property \`${keyName}\` on ${toString(obj)}.`, this._setter !== undefined);
|
|
1376
|
+
let meta$$1 = meta(obj); // ensure two way binding works when the component has defined a computed
|
|
1377
|
+
// property with both a setter and dependent keys, in that scenario without
|
|
1378
|
+
// the sync observer added below the caller's value will never be updated
|
|
1379
|
+
//
|
|
1380
|
+
// See GH#18147 / GH#19028 for details.
|
|
1441
1381
|
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1382
|
+
if ( // ensure that we only run this once, while the component is being instantiated
|
|
1383
|
+
meta$$1.isInitializing() && this._dependentKeys !== undefined && this._dependentKeys.length > 0 && // These two properties are set on Ember.Component
|
|
1384
|
+
typeof obj[PROPERTY_DID_CHANGE] === 'function' && obj.isComponent) {
|
|
1385
|
+
addObserver(obj, keyName, () => {
|
|
1386
|
+
obj[PROPERTY_DID_CHANGE](keyName);
|
|
1387
|
+
}, undefined, true);
|
|
1388
|
+
}
|
|
1447
1389
|
|
|
1448
|
-
|
|
1449
|
-
return set(newRoot, keyName, value);
|
|
1450
|
-
} else if (!tolerant) {
|
|
1451
|
-
throw new EmberError(`Property set failed: object in path "${parts.join('.')}" could not be found.`);
|
|
1452
|
-
}
|
|
1453
|
-
}
|
|
1454
|
-
/**
|
|
1455
|
-
Error-tolerant form of `set`. Will not blow up if any part of the
|
|
1456
|
-
chain is `undefined`, `null`, or destroyed.
|
|
1390
|
+
let ret;
|
|
1457
1391
|
|
|
1458
|
-
|
|
1459
|
-
|
|
1392
|
+
try {
|
|
1393
|
+
beginPropertyChanges();
|
|
1394
|
+
ret = this._set(obj, keyName, value, meta$$1);
|
|
1395
|
+
finishLazyChains(meta$$1, keyName, ret);
|
|
1396
|
+
let tagMeta = tagMetaFor(obj);
|
|
1397
|
+
let propertyTag = tagFor(obj, keyName, tagMeta);
|
|
1398
|
+
let {
|
|
1399
|
+
_dependentKeys
|
|
1400
|
+
} = this;
|
|
1460
1401
|
|
|
1461
|
-
|
|
1462
|
-
|
|
1402
|
+
if (_dependentKeys !== undefined) {
|
|
1403
|
+
updateTag(propertyTag, getChainTagsForKeys(obj, _dependentKeys, tagMeta, meta$$1));
|
|
1463
1404
|
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1405
|
+
if (DEBUG) {
|
|
1406
|
+
ALLOW_CYCLES.set(propertyTag, true);
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1467
1409
|
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
@param {String} path The property path to set
|
|
1473
|
-
@param {Object} value The value to set
|
|
1474
|
-
@public
|
|
1475
|
-
*/
|
|
1410
|
+
meta$$1.setRevisionFor(keyName, valueForTag(propertyTag));
|
|
1411
|
+
} finally {
|
|
1412
|
+
endPropertyChanges();
|
|
1413
|
+
}
|
|
1476
1414
|
|
|
1415
|
+
return ret;
|
|
1416
|
+
}
|
|
1477
1417
|
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
}
|
|
1418
|
+
_throwReadOnlyError(obj, keyName) {
|
|
1419
|
+
throw new EmberError(`Cannot set read-only property "${keyName}" on object: ${inspect(obj)}`);
|
|
1420
|
+
}
|
|
1481
1421
|
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1422
|
+
_set(obj, keyName, value, meta$$1) {
|
|
1423
|
+
let hadCachedValue = meta$$1.revisionFor(keyName) !== undefined;
|
|
1424
|
+
let cachedValue = meta$$1.valueFor(keyName);
|
|
1425
|
+
let ret;
|
|
1426
|
+
let {
|
|
1427
|
+
_setter
|
|
1428
|
+
} = this;
|
|
1429
|
+
setObserverSuspended(obj, keyName, true);
|
|
1485
1430
|
|
|
1486
|
-
|
|
1431
|
+
try {
|
|
1432
|
+
ret = _setter.call(obj, keyName, value, cachedValue);
|
|
1433
|
+
} finally {
|
|
1434
|
+
setObserverSuspended(obj, keyName, false);
|
|
1435
|
+
} // allows setter to return the same value that is cached already
|
|
1487
1436
|
|
|
1488
|
-
function noop() {}
|
|
1489
|
-
/**
|
|
1490
|
-
`@computed` is a decorator that turns a JavaScript getter and setter into a
|
|
1491
|
-
computed property, which is a _cached, trackable value_. By default the getter
|
|
1492
|
-
will only be called once and the result will be cached. You can specify
|
|
1493
|
-
various properties that your computed property depends on. This will force the
|
|
1494
|
-
cached result to be cleared if the dependencies are modified, and lazily recomputed the next time something asks for it.
|
|
1495
1437
|
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
times it is accessed) as long as its dependencies do not change. Once
|
|
1500
|
-
`firstName` or `lastName` are updated any future calls to `fullName` will
|
|
1501
|
-
incorporate the new values, and any watchers of the value such as templates
|
|
1502
|
-
will be updated:
|
|
1438
|
+
if (hadCachedValue && cachedValue === ret) {
|
|
1439
|
+
return ret;
|
|
1440
|
+
}
|
|
1503
1441
|
|
|
1504
|
-
|
|
1505
|
-
|
|
1442
|
+
meta$$1.setValueFor(keyName, ret);
|
|
1443
|
+
notifyPropertyChange(obj, keyName, meta$$1, value);
|
|
1444
|
+
return ret;
|
|
1445
|
+
}
|
|
1446
|
+
/* called before property is overridden */
|
|
1506
1447
|
|
|
1507
|
-
class Person {
|
|
1508
|
-
constructor(firstName, lastName) {
|
|
1509
|
-
set(this, 'firstName', firstName);
|
|
1510
|
-
set(this, 'lastName', lastName);
|
|
1511
|
-
}
|
|
1512
1448
|
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1449
|
+
teardown(obj, keyName, meta$$1) {
|
|
1450
|
+
if (meta$$1.revisionFor(keyName) !== undefined) {
|
|
1451
|
+
meta$$1.setRevisionFor(keyName, undefined);
|
|
1452
|
+
meta$$1.setValueFor(keyName, undefined);
|
|
1516
1453
|
}
|
|
1517
|
-
});
|
|
1518
|
-
|
|
1519
|
-
let tom = new Person('Tom', 'Dale');
|
|
1520
1454
|
|
|
1521
|
-
|
|
1522
|
-
|
|
1455
|
+
super.teardown(obj, keyName, meta$$1);
|
|
1456
|
+
}
|
|
1523
1457
|
|
|
1524
|
-
|
|
1525
|
-
property. Ember's `set` function must be used to update the property
|
|
1526
|
-
since it will also notify observers of the property:
|
|
1458
|
+
}
|
|
1527
1459
|
|
|
1528
|
-
|
|
1529
|
-
|
|
1460
|
+
class AutoComputedProperty extends ComputedProperty {
|
|
1461
|
+
get(obj, keyName) {
|
|
1462
|
+
let meta$$1 = meta(obj);
|
|
1463
|
+
let tagMeta = tagMetaFor(obj);
|
|
1464
|
+
let propertyTag = tagFor(obj, keyName, tagMeta);
|
|
1465
|
+
let ret;
|
|
1466
|
+
let revision = meta$$1.revisionFor(keyName);
|
|
1530
1467
|
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1468
|
+
if (revision !== undefined && validateTag(propertyTag, revision)) {
|
|
1469
|
+
ret = meta$$1.valueFor(keyName);
|
|
1470
|
+
} else {
|
|
1471
|
+
assert(`Attempted to access the computed ${obj}.${keyName} on a destroyed object, which is not allowed`, !isDestroyed(obj));
|
|
1472
|
+
let {
|
|
1473
|
+
_getter
|
|
1474
|
+
} = this; // Create a tracker that absorbs any trackable actions inside the CP
|
|
1536
1475
|
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1476
|
+
let tag = track(() => {
|
|
1477
|
+
ret = _getter.call(obj, keyName);
|
|
1478
|
+
});
|
|
1479
|
+
updateTag(propertyTag, tag);
|
|
1480
|
+
meta$$1.setValueFor(keyName, ret);
|
|
1481
|
+
meta$$1.setRevisionFor(keyName, valueForTag(propertyTag));
|
|
1482
|
+
finishLazyChains(meta$$1, keyName, ret);
|
|
1540
1483
|
}
|
|
1541
1484
|
|
|
1542
|
-
|
|
1543
|
-
|
|
1485
|
+
consumeTag(propertyTag); // Add the tag of the returned value if it is an array, since arrays
|
|
1486
|
+
// should always cause updates if they are consumed and then changed
|
|
1544
1487
|
|
|
1545
|
-
|
|
1546
|
-
|
|
1488
|
+
if (Array.isArray(ret)) {
|
|
1489
|
+
consumeTag(tagFor(ret, '[]', tagMeta));
|
|
1547
1490
|
}
|
|
1548
|
-
});
|
|
1549
|
-
|
|
1550
|
-
let person = new Person();
|
|
1551
|
-
|
|
1552
|
-
set(person, 'fullName', 'Peter Wagenet');
|
|
1553
|
-
person.firstName; // 'Peter'
|
|
1554
|
-
person.lastName; // 'Wagenet'
|
|
1555
|
-
```
|
|
1556
|
-
|
|
1557
|
-
You can also pass a getter function or object with `get` and `set` functions
|
|
1558
|
-
as the last argument to the computed decorator. This allows you to define
|
|
1559
|
-
computed property _macros_:
|
|
1560
|
-
|
|
1561
|
-
```js
|
|
1562
|
-
import { computed } from '@ember/object';
|
|
1563
|
-
|
|
1564
|
-
function join(...keys) {
|
|
1565
|
-
return computed(...keys, function() {
|
|
1566
|
-
return keys.map(key => this[key]).join(' ');
|
|
1567
|
-
});
|
|
1568
|
-
}
|
|
1569
1491
|
|
|
1570
|
-
|
|
1571
|
-
@join('firstName', 'lastName')
|
|
1572
|
-
fullName;
|
|
1492
|
+
return ret;
|
|
1573
1493
|
}
|
|
1574
|
-
```
|
|
1575
|
-
|
|
1576
|
-
Note that when defined this way, getters and setters receive the _key_ of the
|
|
1577
|
-
property they are decorating as the first argument. Setters receive the value
|
|
1578
|
-
they are setting to as the second argument instead. Additionally, setters must
|
|
1579
|
-
_return_ the value that should be cached:
|
|
1580
|
-
|
|
1581
|
-
```javascript
|
|
1582
|
-
import { computed, set } from '@ember/object';
|
|
1583
|
-
|
|
1584
|
-
function fullNameMacro(firstNameKey, lastNameKey) {
|
|
1585
|
-
return computed(firstNameKey, lastNameKey, {
|
|
1586
|
-
get() {
|
|
1587
|
-
return `${this[firstNameKey]} ${this[lastNameKey]}`;
|
|
1588
|
-
}
|
|
1589
1494
|
|
|
1590
|
-
|
|
1591
|
-
let [firstName, lastName] = value.split(' ');
|
|
1495
|
+
} // TODO: This class can be svelted once `meta` has been deprecated
|
|
1592
1496
|
|
|
1593
|
-
set(this, firstNameKey, firstName);
|
|
1594
|
-
set(this, lastNameKey, lastName);
|
|
1595
1497
|
|
|
1596
|
-
|
|
1498
|
+
class ComputedDecoratorImpl extends Function {
|
|
1499
|
+
/**
|
|
1500
|
+
Call on a computed property to set it into read-only mode. When in this
|
|
1501
|
+
mode the computed property will throw an error when set.
|
|
1502
|
+
Example:
|
|
1503
|
+
```javascript
|
|
1504
|
+
import { computed, set } from '@ember/object';
|
|
1505
|
+
class Person {
|
|
1506
|
+
@computed().readOnly()
|
|
1507
|
+
get guid() {
|
|
1508
|
+
return 'guid-guid-guid';
|
|
1597
1509
|
}
|
|
1598
|
-
});
|
|
1599
|
-
}
|
|
1600
|
-
|
|
1601
|
-
class Person {
|
|
1602
|
-
constructor(firstName, lastName) {
|
|
1603
|
-
set(this, 'firstName', firstName);
|
|
1604
|
-
set(this, 'lastName', lastName);
|
|
1605
1510
|
}
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1511
|
+
let person = new Person();
|
|
1512
|
+
set(person, 'guid', 'new-guid'); // will throw an exception
|
|
1513
|
+
```
|
|
1514
|
+
Classic Class Example:
|
|
1515
|
+
```javascript
|
|
1516
|
+
import EmberObject, { computed } from '@ember/object';
|
|
1517
|
+
let Person = EmberObject.extend({
|
|
1518
|
+
guid: computed(function() {
|
|
1519
|
+
return 'guid-guid-guid';
|
|
1520
|
+
}).readOnly()
|
|
1521
|
+
});
|
|
1522
|
+
let person = Person.create();
|
|
1523
|
+
person.set('guid', 'new-guid'); // will throw an exception
|
|
1524
|
+
```
|
|
1525
|
+
@method readOnly
|
|
1526
|
+
@return {ComputedProperty} this
|
|
1527
|
+
@chainable
|
|
1528
|
+
@public
|
|
1529
|
+
*/
|
|
1530
|
+
readOnly() {
|
|
1531
|
+
let desc = descriptorForDecorator(this);
|
|
1532
|
+
assert('Computed properties that define a setter using the new syntax cannot be read-only', !(desc._setter && desc._setter !== desc._getter));
|
|
1533
|
+
desc._readOnly = true;
|
|
1534
|
+
return this;
|
|
1535
|
+
}
|
|
1536
|
+
/**
|
|
1537
|
+
In some cases, you may want to annotate computed properties with additional
|
|
1538
|
+
metadata about how they function or what values they operate on. For example,
|
|
1539
|
+
computed property functions may close over variables that are then no longer
|
|
1540
|
+
available for introspection. You can pass a hash of these values to a
|
|
1541
|
+
computed property.
|
|
1542
|
+
Example:
|
|
1543
|
+
```javascript
|
|
1544
|
+
import { computed } from '@ember/object';
|
|
1545
|
+
import Person from 'my-app/utils/person';
|
|
1546
|
+
class Store {
|
|
1547
|
+
@computed().meta({ type: Person })
|
|
1548
|
+
get person() {
|
|
1549
|
+
let personId = this.personId;
|
|
1550
|
+
return Person.create({ id: personId });
|
|
1642
1551
|
}
|
|
1643
|
-
})
|
|
1644
|
-
});
|
|
1645
|
-
|
|
1646
|
-
let tom = Person.create({
|
|
1647
|
-
firstName: 'Tom',
|
|
1648
|
-
lastName: 'Dale'
|
|
1649
|
-
});
|
|
1650
|
-
|
|
1651
|
-
tom.get('fullName') // 'Tom Dale'
|
|
1652
|
-
```
|
|
1653
|
-
|
|
1654
|
-
You can overwrite computed property without setters with a normal property (no
|
|
1655
|
-
longer computed) that won't change if dependencies change. You can also mark
|
|
1656
|
-
computed property as `.readOnly()` and block all attempts to set it.
|
|
1657
|
-
|
|
1658
|
-
```javascript
|
|
1659
|
-
import { computed, set } from '@ember/object';
|
|
1660
|
-
|
|
1661
|
-
class Person {
|
|
1662
|
-
constructor(firstName, lastName) {
|
|
1663
|
-
set(this, 'firstName', firstName);
|
|
1664
|
-
set(this, 'lastName', lastName);
|
|
1665
1552
|
}
|
|
1553
|
+
```
|
|
1554
|
+
Classic Class Example:
|
|
1555
|
+
```javascript
|
|
1556
|
+
import { computed } from '@ember/object';
|
|
1557
|
+
import Person from 'my-app/utils/person';
|
|
1558
|
+
const Store = EmberObject.extend({
|
|
1559
|
+
person: computed(function() {
|
|
1560
|
+
let personId = this.get('personId');
|
|
1561
|
+
return Person.create({ id: personId });
|
|
1562
|
+
}).meta({ type: Person })
|
|
1563
|
+
});
|
|
1564
|
+
```
|
|
1565
|
+
The hash that you pass to the `meta()` function will be saved on the
|
|
1566
|
+
computed property descriptor under the `_meta` key. Ember runtime
|
|
1567
|
+
exposes a public API for retrieving these values from classes,
|
|
1568
|
+
via the `metaForProperty()` function.
|
|
1569
|
+
@method meta
|
|
1570
|
+
@param {Object} meta
|
|
1571
|
+
@chainable
|
|
1572
|
+
@public
|
|
1573
|
+
*/
|
|
1666
1574
|
|
|
1667
|
-
@computed('firstName', 'lastName').readOnly()
|
|
1668
|
-
get fullName() {
|
|
1669
|
-
return `${this.firstName} ${this.lastName}`;
|
|
1670
|
-
}
|
|
1671
|
-
});
|
|
1672
|
-
|
|
1673
|
-
let person = new Person();
|
|
1674
|
-
person.set('fullName', 'Peter Wagenet'); // Uncaught Error: Cannot set read-only property "fullName" on object: <(...):emberXXX>
|
|
1675
|
-
```
|
|
1676
|
-
|
|
1677
|
-
Additional resources:
|
|
1678
|
-
- [Decorators RFC](https://github.com/emberjs/rfcs/blob/master/text/0408-decorators.md)
|
|
1679
|
-
- [New CP syntax RFC](https://github.com/emberjs/rfcs/blob/master/text/0011-improved-cp-syntax.md)
|
|
1680
|
-
- [New computed syntax explained in "Ember 1.12 released" ](https://emberjs.com/blog/2015/05/13/ember-1-12-released.html#toc_new-computed-syntax)
|
|
1681
|
-
|
|
1682
|
-
@class ComputedProperty
|
|
1683
|
-
@public
|
|
1684
|
-
*/
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
class ComputedProperty extends ComputedDescriptor {
|
|
1688
|
-
constructor(args) {
|
|
1689
|
-
super();
|
|
1690
|
-
this._volatile = false;
|
|
1691
|
-
this._readOnly = false;
|
|
1692
|
-
this._hasConfig = false;
|
|
1693
|
-
this._getter = undefined;
|
|
1694
|
-
this._setter = undefined;
|
|
1695
|
-
let maybeConfig = args[args.length - 1];
|
|
1696
|
-
|
|
1697
|
-
if (typeof maybeConfig === 'function' || maybeConfig !== null && typeof maybeConfig === 'object') {
|
|
1698
|
-
this._hasConfig = true;
|
|
1699
|
-
let config = args.pop();
|
|
1700
1575
|
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
this._getter = config;
|
|
1704
|
-
} else {
|
|
1705
|
-
const objectConfig = config;
|
|
1706
|
-
assert('computed expects a function or an object as last argument.', typeof objectConfig === 'object' && !Array.isArray(objectConfig));
|
|
1707
|
-
assert('Config object passed to computed can only contain `get` and `set` keys.', Object.keys(objectConfig).every(key => key === 'get' || key === 'set'));
|
|
1708
|
-
assert('Computed properties must receive a getter or a setter, you passed none.', Boolean(objectConfig.get) || Boolean(objectConfig.set));
|
|
1709
|
-
this._getter = objectConfig.get || noop;
|
|
1710
|
-
this._setter = objectConfig.set;
|
|
1711
|
-
}
|
|
1712
|
-
}
|
|
1576
|
+
meta(meta$$1) {
|
|
1577
|
+
let prop = descriptorForDecorator(this);
|
|
1713
1578
|
|
|
1714
|
-
if (
|
|
1715
|
-
|
|
1579
|
+
if (arguments.length === 0) {
|
|
1580
|
+
return prop._meta || {};
|
|
1581
|
+
} else {
|
|
1582
|
+
prop._meta = meta$$1;
|
|
1583
|
+
return this;
|
|
1716
1584
|
}
|
|
1717
|
-
}
|
|
1718
|
-
|
|
1719
|
-
setup(obj, keyName, propertyDesc, meta$$1) {
|
|
1720
|
-
super.setup(obj, keyName, propertyDesc, meta$$1);
|
|
1721
|
-
assert(`@computed can only be used on accessors or fields, attempted to use it with ${keyName} but that was a method. Try converting it to a getter (e.g. \`get ${keyName}() {}\`)`, !(propertyDesc && typeof propertyDesc.value === 'function'));
|
|
1722
|
-
assert(`@computed can only be used on empty fields. ${keyName} has an initial value (e.g. \`${keyName} = someValue\`)`, !propertyDesc || !propertyDesc.initializer);
|
|
1723
|
-
assert(`Attempted to apply a computed property that already has a getter/setter to a ${keyName}, but it is a method or an accessor. If you passed @computed a function or getter/setter (e.g. \`@computed({ get() { ... } })\`), then it must be applied to a field`, !(this._hasConfig && propertyDesc && (typeof propertyDesc.get === 'function' || typeof propertyDesc.set === 'function')));
|
|
1724
|
-
|
|
1725
|
-
if (this._hasConfig === false) {
|
|
1726
|
-
assert(`Attempted to use @computed on ${keyName}, but it did not have a getter or a setter. You must either pass a get a function or getter/setter to @computed directly (e.g. \`@computed({ get() { ... } })\`) or apply @computed directly to a getter/setter`, propertyDesc && (typeof propertyDesc.get === 'function' || typeof propertyDesc.set === 'function'));
|
|
1727
|
-
let {
|
|
1728
|
-
get,
|
|
1729
|
-
set: set$$1
|
|
1730
|
-
} = propertyDesc;
|
|
1585
|
+
} // TODO: Remove this when we can provide alternatives in the ecosystem to
|
|
1586
|
+
// addons such as ember-macro-helpers that use it.
|
|
1731
1587
|
|
|
1732
|
-
if (get !== undefined) {
|
|
1733
|
-
this._getter = get;
|
|
1734
|
-
}
|
|
1735
1588
|
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1589
|
+
get _getter() {
|
|
1590
|
+
return descriptorForDecorator(this)._getter;
|
|
1591
|
+
} // TODO: Refactor this, this is an internal API only
|
|
1739
1592
|
|
|
1740
|
-
if (get !== undefined) {
|
|
1741
|
-
return typeof ret === 'undefined' ? get.call(this) : ret;
|
|
1742
|
-
}
|
|
1743
1593
|
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
}
|
|
1747
|
-
}
|
|
1594
|
+
set enumerable(value) {
|
|
1595
|
+
descriptorForDecorator(this).enumerable = value;
|
|
1748
1596
|
}
|
|
1749
1597
|
|
|
1750
|
-
|
|
1751
|
-
let args = [];
|
|
1752
|
-
|
|
1753
|
-
function addArg(property) {
|
|
1754
|
-
warn(`Dependent keys containing @each only work one level deep. ` + `You used the key "${property}" which is invalid. ` + `Please create an intermediary computed property.`, DEEP_EACH_REGEX.test(property) === false, {
|
|
1755
|
-
id: 'ember-metal.computed-deep-each'
|
|
1756
|
-
});
|
|
1757
|
-
args.push(property);
|
|
1758
|
-
}
|
|
1598
|
+
}
|
|
1759
1599
|
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
}
|
|
1600
|
+
function computed(...args) {
|
|
1601
|
+
assert(`@computed can only be used directly as a native decorator. If you're using tracked in classic classes, add parenthesis to call it like a function: computed()`, !(isElementDescriptor(args.slice(0, 3)) && args.length === 5 && args[4] === true));
|
|
1763
1602
|
|
|
1764
|
-
|
|
1603
|
+
if (isElementDescriptor(args)) {
|
|
1604
|
+
let decorator = makeComputedDecorator(new ComputedProperty([]), ComputedDecoratorImpl);
|
|
1605
|
+
return decorator(args[0], args[1], args[2]);
|
|
1765
1606
|
}
|
|
1766
1607
|
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1608
|
+
return makeComputedDecorator(new ComputedProperty(args), ComputedDecoratorImpl);
|
|
1609
|
+
}
|
|
1610
|
+
function autoComputed(...config) {
|
|
1611
|
+
return makeComputedDecorator(new AutoComputedProperty(config), ComputedDecoratorImpl);
|
|
1612
|
+
}
|
|
1613
|
+
/**
|
|
1614
|
+
Allows checking if a given property on an object is a computed property. For the most part,
|
|
1615
|
+
this doesn't matter (you would normally just access the property directly and use its value),
|
|
1616
|
+
but for some tooling specific scenarios (e.g. the ember-inspector) it is important to
|
|
1617
|
+
differentiate if a property is a computed property or a "normal" property.
|
|
1777
1618
|
|
|
1778
|
-
|
|
1779
|
-
ret = meta$$1.valueFor(keyName);
|
|
1780
|
-
} else {
|
|
1781
|
-
// For backwards compatibility, we only throw if the CP has any dependencies. CPs without dependencies
|
|
1782
|
-
// should be allowed, even after the object has been destroyed, which is why we check _dependentKeys.
|
|
1783
|
-
assert(`Attempted to access the computed ${obj}.${keyName} on a destroyed object, which is not allowed`, this._dependentKeys === undefined || !isDestroyed(obj));
|
|
1784
|
-
let {
|
|
1785
|
-
_getter,
|
|
1786
|
-
_dependentKeys
|
|
1787
|
-
} = this; // Create a tracker that absorbs any trackable actions inside the CP
|
|
1619
|
+
This will work on either a class's prototype or an instance itself.
|
|
1788
1620
|
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1621
|
+
@static
|
|
1622
|
+
@method isComputed
|
|
1623
|
+
@for @ember/debug
|
|
1624
|
+
@private
|
|
1625
|
+
*/
|
|
1792
1626
|
|
|
1793
|
-
|
|
1794
|
-
|
|
1627
|
+
function isComputed(obj, key) {
|
|
1628
|
+
return Boolean(descriptorForProperty(obj, key));
|
|
1629
|
+
}
|
|
1795
1630
|
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
}
|
|
1799
|
-
}
|
|
1631
|
+
function getCachedValueFor(obj, key) {
|
|
1632
|
+
let meta$$1 = peekMeta(obj);
|
|
1800
1633
|
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1634
|
+
if (meta$$1) {
|
|
1635
|
+
return meta$$1.valueFor(key);
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1805
1638
|
|
|
1806
|
-
|
|
1807
|
-
|
|
1639
|
+
/**
|
|
1640
|
+
@module @ember/object
|
|
1641
|
+
*/
|
|
1642
|
+
/**
|
|
1643
|
+
NOTE: This is a low-level method used by other parts of the API. You almost
|
|
1644
|
+
never want to call this method directly. Instead you should use
|
|
1645
|
+
`mixin()` to define new properties.
|
|
1808
1646
|
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1647
|
+
Defines a property on an object. This method works much like the ES5
|
|
1648
|
+
`Object.defineProperty()` method except that it can also accept computed
|
|
1649
|
+
properties and other special descriptors.
|
|
1812
1650
|
|
|
1813
|
-
|
|
1814
|
-
|
|
1651
|
+
Normally this method takes only three parameters. However if you pass an
|
|
1652
|
+
instance of `Descriptor` as the third param then you can pass an
|
|
1653
|
+
optional value as the fourth parameter. This is often more efficient than
|
|
1654
|
+
creating new descriptor hashes for each property.
|
|
1815
1655
|
|
|
1816
|
-
|
|
1817
|
-
if (this._readOnly) {
|
|
1818
|
-
this._throwReadOnlyError(obj, keyName);
|
|
1819
|
-
}
|
|
1656
|
+
## Examples
|
|
1820
1657
|
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
}
|
|
1658
|
+
```javascript
|
|
1659
|
+
import { defineProperty, computed } from '@ember/object';
|
|
1824
1660
|
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1661
|
+
// ES5 compatible mode
|
|
1662
|
+
defineProperty(contact, 'firstName', {
|
|
1663
|
+
writable: true,
|
|
1664
|
+
configurable: false,
|
|
1665
|
+
enumerable: true,
|
|
1666
|
+
value: 'Charles'
|
|
1667
|
+
});
|
|
1828
1668
|
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
// the sync observer added below the caller's value will never be updated
|
|
1832
|
-
//
|
|
1833
|
-
// See GH#18147 / GH#19028 for details.
|
|
1669
|
+
// define a simple property
|
|
1670
|
+
defineProperty(contact, 'lastName', undefined, 'Jolley');
|
|
1834
1671
|
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
}, undefined, true);
|
|
1841
|
-
}
|
|
1672
|
+
// define a computed property
|
|
1673
|
+
defineProperty(contact, 'fullName', computed('firstName', 'lastName', function() {
|
|
1674
|
+
return this.firstName+' '+this.lastName;
|
|
1675
|
+
}));
|
|
1676
|
+
```
|
|
1842
1677
|
|
|
1843
|
-
|
|
1678
|
+
@public
|
|
1679
|
+
@method defineProperty
|
|
1680
|
+
@static
|
|
1681
|
+
@for @ember/object
|
|
1682
|
+
@param {Object} obj the object to define this property on. This may be a prototype.
|
|
1683
|
+
@param {String} keyName the name of the property
|
|
1684
|
+
@param {Descriptor} [desc] an instance of `Descriptor` (typically a
|
|
1685
|
+
computed property) or an ES5 descriptor.
|
|
1686
|
+
You must provide this or `data` but not both.
|
|
1687
|
+
@param {*} [data] something other than a descriptor, that will
|
|
1688
|
+
become the explicit value of this property.
|
|
1689
|
+
*/
|
|
1844
1690
|
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
let tagMeta = tagMetaFor(obj);
|
|
1850
|
-
let propertyTag = tagFor(obj, keyName, tagMeta);
|
|
1851
|
-
let {
|
|
1852
|
-
_dependentKeys
|
|
1853
|
-
} = this;
|
|
1691
|
+
function defineProperty(obj, keyName, desc, data, _meta) {
|
|
1692
|
+
let meta$$1 = _meta === undefined ? meta(obj) : _meta;
|
|
1693
|
+
let previousDesc = descriptorForProperty(obj, keyName, meta$$1);
|
|
1694
|
+
let wasDescriptor = previousDesc !== undefined;
|
|
1854
1695
|
|
|
1855
|
-
|
|
1856
|
-
|
|
1696
|
+
if (wasDescriptor) {
|
|
1697
|
+
previousDesc.teardown(obj, keyName, meta$$1);
|
|
1698
|
+
}
|
|
1857
1699
|
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1700
|
+
if (isClassicDecorator(desc)) {
|
|
1701
|
+
defineDecorator(obj, keyName, desc, meta$$1);
|
|
1702
|
+
} else if (desc === null || desc === undefined) {
|
|
1703
|
+
defineValue(obj, keyName, data, wasDescriptor, true);
|
|
1704
|
+
} else {
|
|
1705
|
+
// fallback to ES5
|
|
1706
|
+
Object.defineProperty(obj, keyName, desc);
|
|
1707
|
+
} // if key is being watched, override chains that
|
|
1708
|
+
// were initialized with the prototype
|
|
1862
1709
|
|
|
1863
|
-
meta$$1.setRevisionFor(keyName, valueForTag(propertyTag));
|
|
1864
|
-
} finally {
|
|
1865
|
-
endPropertyChanges();
|
|
1866
|
-
}
|
|
1867
1710
|
|
|
1868
|
-
|
|
1711
|
+
if (!meta$$1.isPrototypeMeta(obj)) {
|
|
1712
|
+
revalidateObservers(obj);
|
|
1869
1713
|
}
|
|
1714
|
+
}
|
|
1715
|
+
function defineDecorator(obj, keyName, desc, meta$$1) {
|
|
1716
|
+
let propertyDesc;
|
|
1870
1717
|
|
|
1871
|
-
|
|
1872
|
-
|
|
1718
|
+
if (DEBUG) {
|
|
1719
|
+
propertyDesc = desc(obj, keyName, undefined, meta$$1, true);
|
|
1720
|
+
} else {
|
|
1721
|
+
propertyDesc = desc(obj, keyName, undefined, meta$$1);
|
|
1873
1722
|
}
|
|
1874
1723
|
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1724
|
+
Object.defineProperty(obj, keyName, propertyDesc); // pass the decorator function forward for backwards compat
|
|
1725
|
+
|
|
1726
|
+
return desc;
|
|
1727
|
+
}
|
|
1728
|
+
function defineValue(obj, keyName, value, wasDescriptor, enumerable = true) {
|
|
1729
|
+
if (wasDescriptor === true || enumerable === false) {
|
|
1730
|
+
Object.defineProperty(obj, keyName, {
|
|
1731
|
+
configurable: true,
|
|
1732
|
+
enumerable,
|
|
1733
|
+
writable: true,
|
|
1734
|
+
value
|
|
1884
1735
|
});
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1736
|
+
} else {
|
|
1737
|
+
if (DEBUG) {
|
|
1738
|
+
setWithMandatorySetter(obj, keyName, value);
|
|
1739
|
+
} else {
|
|
1740
|
+
obj[keyName] = value;
|
|
1741
|
+
}
|
|
1889
1742
|
}
|
|
1890
1743
|
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
}
|
|
1744
|
+
return value;
|
|
1745
|
+
}
|
|
1894
1746
|
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
let {
|
|
1900
|
-
_setter
|
|
1901
|
-
} = this;
|
|
1902
|
-
setObserverSuspended(obj, keyName, true);
|
|
1747
|
+
const firstDotIndexCache = new Cache(1000, key => key.indexOf('.'));
|
|
1748
|
+
function isPath(path) {
|
|
1749
|
+
return typeof path === 'string' && firstDotIndexCache.get(path) !== -1;
|
|
1750
|
+
}
|
|
1903
1751
|
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1752
|
+
/**
|
|
1753
|
+
@module @ember/object
|
|
1754
|
+
*/
|
|
1755
|
+
const PROXY_CONTENT = symbol('PROXY_CONTENT');
|
|
1756
|
+
let getPossibleMandatoryProxyValue;
|
|
1909
1757
|
|
|
1758
|
+
if (DEBUG) {
|
|
1759
|
+
getPossibleMandatoryProxyValue = function getPossibleMandatoryProxyValue(obj, keyName) {
|
|
1760
|
+
let content = obj[PROXY_CONTENT];
|
|
1910
1761
|
|
|
1911
|
-
if (
|
|
1912
|
-
return
|
|
1762
|
+
if (content === undefined) {
|
|
1763
|
+
return obj[keyName];
|
|
1764
|
+
} else {
|
|
1765
|
+
/* global Reflect */
|
|
1766
|
+
return Reflect.get(content, keyName, obj);
|
|
1913
1767
|
}
|
|
1768
|
+
};
|
|
1769
|
+
} // ..........................................................
|
|
1770
|
+
// GET AND SET
|
|
1771
|
+
//
|
|
1772
|
+
// If we are on a platform that supports accessors we can use those.
|
|
1773
|
+
// Otherwise simulate accessors by looking up the property directly on the
|
|
1774
|
+
// object.
|
|
1914
1775
|
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
/* called before property is overridden */
|
|
1776
|
+
/**
|
|
1777
|
+
Gets the value of a property on an object. If the property is computed,
|
|
1778
|
+
the function will be invoked. If the property is not defined but the
|
|
1779
|
+
object implements the `unknownProperty` method then that will be invoked.
|
|
1920
1780
|
|
|
1781
|
+
```javascript
|
|
1782
|
+
import { get } from '@ember/object';
|
|
1783
|
+
get(obj, "name");
|
|
1784
|
+
```
|
|
1921
1785
|
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
meta$$1.setValueFor(keyName, undefined);
|
|
1927
|
-
}
|
|
1928
|
-
}
|
|
1786
|
+
If you plan to run on IE8 and older browsers then you should use this
|
|
1787
|
+
method anytime you want to retrieve a property on an object that you don't
|
|
1788
|
+
know for sure is private. (Properties beginning with an underscore '_'
|
|
1789
|
+
are considered private.)
|
|
1929
1790
|
|
|
1930
|
-
|
|
1931
|
-
|
|
1791
|
+
On all newer browsers, you only need to use this method to retrieve
|
|
1792
|
+
properties if the property might not be defined on the object and you want
|
|
1793
|
+
to respect the `unknownProperty` handler. Otherwise you can ignore this
|
|
1794
|
+
method.
|
|
1932
1795
|
|
|
1933
|
-
|
|
1796
|
+
Note that if the object itself is `undefined`, this method will throw
|
|
1797
|
+
an error.
|
|
1934
1798
|
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1799
|
+
@method get
|
|
1800
|
+
@for @ember/object
|
|
1801
|
+
@static
|
|
1802
|
+
@param {Object} obj The object to retrieve from.
|
|
1803
|
+
@param {String} keyName The property key to retrieve
|
|
1804
|
+
@return {Object} the property value or `null`.
|
|
1805
|
+
@public
|
|
1806
|
+
*/
|
|
1940
1807
|
|
|
1941
|
-
let meta$$1 = meta(obj);
|
|
1942
|
-
let tagMeta = tagMetaFor(obj);
|
|
1943
|
-
let propertyTag = tagFor(obj, keyName, tagMeta);
|
|
1944
|
-
let ret;
|
|
1945
|
-
let revision = meta$$1.revisionFor(keyName);
|
|
1946
1808
|
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1809
|
+
function get(obj, keyName) {
|
|
1810
|
+
assert(`Get must be called with two arguments; an object and a property key`, arguments.length === 2);
|
|
1811
|
+
assert(`Cannot call get with '${keyName}' on an undefined object.`, obj !== undefined && obj !== null);
|
|
1812
|
+
assert(`The key provided to get must be a string or number, you passed ${keyName}`, typeof keyName === 'string' || typeof keyName === 'number' && !isNaN(keyName));
|
|
1813
|
+
assert(`'this' in paths is not supported`, typeof keyName !== 'string' || keyName.lastIndexOf('this.', 0) !== 0);
|
|
1814
|
+
return isPath(keyName) ? _getPath(obj, keyName) : _getProp(obj, keyName);
|
|
1815
|
+
}
|
|
1816
|
+
function _getProp(obj, keyName) {
|
|
1817
|
+
let type = typeof obj;
|
|
1818
|
+
let isObject$$1 = type === 'object';
|
|
1819
|
+
let isFunction = type === 'function';
|
|
1820
|
+
let isObjectLike = isObject$$1 || isFunction;
|
|
1821
|
+
let value;
|
|
1954
1822
|
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
meta$$1.setRevisionFor(keyName, valueForTag(propertyTag));
|
|
1961
|
-
finishLazyChains(meta$$1, keyName, ret);
|
|
1823
|
+
if (isObjectLike) {
|
|
1824
|
+
if (DEBUG) {
|
|
1825
|
+
value = getPossibleMandatoryProxyValue(obj, keyName);
|
|
1826
|
+
} else {
|
|
1827
|
+
value = obj[keyName];
|
|
1962
1828
|
}
|
|
1963
1829
|
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
if (Array.isArray(ret)) {
|
|
1968
|
-
consumeTag(tagFor(ret, '[]', tagMeta));
|
|
1830
|
+
if (value === undefined && isObject$$1 && !(keyName in obj) && typeof obj.unknownProperty === 'function') {
|
|
1831
|
+
value = obj.unknownProperty(keyName);
|
|
1969
1832
|
}
|
|
1970
1833
|
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
} // TODO: This class can be svelted once `meta` has been deprecated
|
|
1975
|
-
|
|
1834
|
+
if (isTracking()) {
|
|
1835
|
+
consumeTag(tagFor(obj, keyName));
|
|
1976
1836
|
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
Example:
|
|
1982
|
-
```javascript
|
|
1983
|
-
import { computed, set } from '@ember/object';
|
|
1984
|
-
class Person {
|
|
1985
|
-
@computed().readOnly()
|
|
1986
|
-
get guid() {
|
|
1987
|
-
return 'guid-guid-guid';
|
|
1837
|
+
if (Array.isArray(value) || isEmberArray(value)) {
|
|
1838
|
+
// Add the tag of the returned value if it is an array, since arrays
|
|
1839
|
+
// should always cause updates if they are consumed and then changed
|
|
1840
|
+
consumeTag(tagFor(value, '[]'));
|
|
1988
1841
|
}
|
|
1989
1842
|
}
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
```
|
|
1993
|
-
Classic Class Example:
|
|
1994
|
-
```javascript
|
|
1995
|
-
import EmberObject, { computed } from '@ember/object';
|
|
1996
|
-
let Person = EmberObject.extend({
|
|
1997
|
-
guid: computed(function() {
|
|
1998
|
-
return 'guid-guid-guid';
|
|
1999
|
-
}).readOnly()
|
|
2000
|
-
});
|
|
2001
|
-
let person = Person.create();
|
|
2002
|
-
person.set('guid', 'new-guid'); // will throw an exception
|
|
2003
|
-
```
|
|
2004
|
-
@method readOnly
|
|
2005
|
-
@return {ComputedProperty} this
|
|
2006
|
-
@chainable
|
|
2007
|
-
@public
|
|
2008
|
-
*/
|
|
2009
|
-
readOnly() {
|
|
2010
|
-
let desc = descriptorForDecorator(this);
|
|
2011
|
-
assert('Computed properties that define a setter using the new syntax cannot be read-only', !(desc._setter && desc._setter !== desc._getter));
|
|
2012
|
-
desc._readOnly = true;
|
|
2013
|
-
return this;
|
|
1843
|
+
} else {
|
|
1844
|
+
value = obj[keyName];
|
|
2014
1845
|
}
|
|
2015
|
-
/**
|
|
2016
|
-
Call on a computed property to set it into non-cached mode. When in this
|
|
2017
|
-
mode the computed property will not automatically cache the return value.
|
|
2018
|
-
It also does not automatically fire any change events. You must manually notify
|
|
2019
|
-
any changes if you want to observe this property.
|
|
2020
|
-
Dependency keys have no effect on volatile properties as they are for cache
|
|
2021
|
-
invalidation and notification when cached value is invalidated.
|
|
2022
|
-
Example:
|
|
2023
|
-
```javascript
|
|
2024
|
-
import { computed } from '@ember/object';
|
|
2025
|
-
class CallCounter {
|
|
2026
|
-
_calledCount = 0;
|
|
2027
|
-
@computed().volatile()
|
|
2028
|
-
get calledCount() {
|
|
2029
|
-
return this._calledCount++;
|
|
2030
|
-
}
|
|
2031
|
-
}
|
|
2032
|
-
```
|
|
2033
|
-
Classic Class Example:
|
|
2034
|
-
```javascript
|
|
2035
|
-
import EmberObject, { computed } from '@ember/object';
|
|
2036
|
-
let CallCounter = EmberObject.extend({
|
|
2037
|
-
_calledCount: 0,
|
|
2038
|
-
value: computed(function() {
|
|
2039
|
-
return this._calledCount++;
|
|
2040
|
-
}).volatile()
|
|
2041
|
-
});
|
|
2042
|
-
```
|
|
2043
|
-
@method volatile
|
|
2044
|
-
@deprecated
|
|
2045
|
-
@return {ComputedProperty} this
|
|
2046
|
-
@chainable
|
|
2047
|
-
@public
|
|
2048
|
-
*/
|
|
2049
1846
|
|
|
1847
|
+
return value;
|
|
1848
|
+
}
|
|
1849
|
+
function _getPath(root, path) {
|
|
1850
|
+
let obj = root;
|
|
1851
|
+
let parts = typeof path === 'string' ? path.split('.') : path;
|
|
2050
1852
|
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
url: 'https://deprecations.emberjs.com/v3.x#toc_computed-property-volatile',
|
|
2056
|
-
for: 'ember-source',
|
|
2057
|
-
since: {
|
|
2058
|
-
enabled: '3.9.0-beta.1'
|
|
2059
|
-
}
|
|
2060
|
-
});
|
|
2061
|
-
descriptorForDecorator(this)._volatile = true;
|
|
2062
|
-
return this;
|
|
2063
|
-
}
|
|
2064
|
-
/**
|
|
2065
|
-
Sets the dependent keys on this computed property. Pass any number of
|
|
2066
|
-
arguments containing key paths that this computed property depends on.
|
|
2067
|
-
Example:
|
|
2068
|
-
```javascript
|
|
2069
|
-
import EmberObject, { computed } from '@ember/object';
|
|
2070
|
-
class President {
|
|
2071
|
-
constructor(firstName, lastName) {
|
|
2072
|
-
set(this, 'firstName', firstName);
|
|
2073
|
-
set(this, 'lastName', lastName);
|
|
2074
|
-
}
|
|
2075
|
-
// Tell Ember that this computed property depends on firstName
|
|
2076
|
-
// and lastName
|
|
2077
|
-
@computed().property('firstName', 'lastName')
|
|
2078
|
-
get fullName() {
|
|
2079
|
-
return `${this.firstName} ${this.lastName}`;
|
|
2080
|
-
}
|
|
2081
|
-
}
|
|
2082
|
-
let president = new President('Barack', 'Obama');
|
|
2083
|
-
president.fullName; // 'Barack Obama'
|
|
2084
|
-
```
|
|
2085
|
-
Classic Class Example:
|
|
2086
|
-
```javascript
|
|
2087
|
-
import EmberObject, { computed } from '@ember/object';
|
|
2088
|
-
let President = EmberObject.extend({
|
|
2089
|
-
fullName: computed(function() {
|
|
2090
|
-
return this.get('firstName') + ' ' + this.get('lastName');
|
|
2091
|
-
// Tell Ember that this computed property depends on firstName
|
|
2092
|
-
// and lastName
|
|
2093
|
-
}).property('firstName', 'lastName')
|
|
2094
|
-
});
|
|
2095
|
-
let president = President.create({
|
|
2096
|
-
firstName: 'Barack',
|
|
2097
|
-
lastName: 'Obama'
|
|
2098
|
-
});
|
|
2099
|
-
president.get('fullName'); // 'Barack Obama'
|
|
2100
|
-
```
|
|
2101
|
-
@method property
|
|
2102
|
-
@deprecated
|
|
2103
|
-
@param {String} path* zero or more property paths
|
|
2104
|
-
@return {ComputedProperty} this
|
|
2105
|
-
@chainable
|
|
2106
|
-
@public
|
|
2107
|
-
*/
|
|
1853
|
+
for (let i = 0; i < parts.length; i++) {
|
|
1854
|
+
if (obj === undefined || obj === null || obj.isDestroyed) {
|
|
1855
|
+
return undefined;
|
|
1856
|
+
}
|
|
2108
1857
|
|
|
1858
|
+
obj = _getProp(obj, parts[i]);
|
|
1859
|
+
}
|
|
2109
1860
|
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
id: 'computed-property.property',
|
|
2113
|
-
until: '4.0.0',
|
|
2114
|
-
url: 'https://deprecations.emberjs.com/v3.x#toc_computed-property-property',
|
|
2115
|
-
for: 'ember-source',
|
|
2116
|
-
since: {
|
|
2117
|
-
enabled: '3.9.0-beta.1'
|
|
2118
|
-
}
|
|
2119
|
-
});
|
|
1861
|
+
return obj;
|
|
1862
|
+
}
|
|
2120
1863
|
|
|
2121
|
-
|
|
1864
|
+
_getProp('foo', 'a');
|
|
2122
1865
|
|
|
2123
|
-
|
|
2124
|
-
}
|
|
2125
|
-
/**
|
|
2126
|
-
In some cases, you may want to annotate computed properties with additional
|
|
2127
|
-
metadata about how they function or what values they operate on. For example,
|
|
2128
|
-
computed property functions may close over variables that are then no longer
|
|
2129
|
-
available for introspection. You can pass a hash of these values to a
|
|
2130
|
-
computed property.
|
|
2131
|
-
Example:
|
|
2132
|
-
```javascript
|
|
2133
|
-
import { computed } from '@ember/object';
|
|
2134
|
-
import Person from 'my-app/utils/person';
|
|
2135
|
-
class Store {
|
|
2136
|
-
@computed().meta({ type: Person })
|
|
2137
|
-
get person() {
|
|
2138
|
-
let personId = this.personId;
|
|
2139
|
-
return Person.create({ id: personId });
|
|
2140
|
-
}
|
|
2141
|
-
}
|
|
2142
|
-
```
|
|
2143
|
-
Classic Class Example:
|
|
2144
|
-
```javascript
|
|
2145
|
-
import { computed } from '@ember/object';
|
|
2146
|
-
import Person from 'my-app/utils/person';
|
|
2147
|
-
const Store = EmberObject.extend({
|
|
2148
|
-
person: computed(function() {
|
|
2149
|
-
let personId = this.get('personId');
|
|
2150
|
-
return Person.create({ id: personId });
|
|
2151
|
-
}).meta({ type: Person })
|
|
2152
|
-
});
|
|
2153
|
-
```
|
|
2154
|
-
The hash that you pass to the `meta()` function will be saved on the
|
|
2155
|
-
computed property descriptor under the `_meta` key. Ember runtime
|
|
2156
|
-
exposes a public API for retrieving these values from classes,
|
|
2157
|
-
via the `metaForProperty()` function.
|
|
2158
|
-
@method meta
|
|
2159
|
-
@param {Object} meta
|
|
2160
|
-
@chainable
|
|
2161
|
-
@public
|
|
2162
|
-
*/
|
|
1866
|
+
_getProp('foo', 1);
|
|
2163
1867
|
|
|
1868
|
+
_getProp({}, 'a');
|
|
2164
1869
|
|
|
2165
|
-
|
|
2166
|
-
let prop = descriptorForDecorator(this);
|
|
1870
|
+
_getProp({}, 1);
|
|
2167
1871
|
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
} else {
|
|
2171
|
-
prop._meta = meta$$1;
|
|
2172
|
-
return this;
|
|
2173
|
-
}
|
|
2174
|
-
} // TODO: Remove this when we can provide alternatives in the ecosystem to
|
|
2175
|
-
// addons such as ember-macro-helpers that use it.
|
|
1872
|
+
_getProp({
|
|
1873
|
+
unkonwnProperty() {}
|
|
2176
1874
|
|
|
1875
|
+
}, 'a');
|
|
2177
1876
|
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
} // TODO: Refactor this, this is an internal API only
|
|
1877
|
+
_getProp({
|
|
1878
|
+
unkonwnProperty() {}
|
|
2181
1879
|
|
|
1880
|
+
}, 1);
|
|
2182
1881
|
|
|
2183
|
-
|
|
2184
|
-
|
|
1882
|
+
get({}, 'foo');
|
|
1883
|
+
get({}, 'foo.bar');
|
|
1884
|
+
let fakeProxy = {};
|
|
1885
|
+
setProxy(fakeProxy);
|
|
1886
|
+
track(() => _getProp({}, 'a'));
|
|
1887
|
+
track(() => _getProp({}, 1));
|
|
1888
|
+
track(() => _getProp({
|
|
1889
|
+
a: []
|
|
1890
|
+
}, 'a'));
|
|
1891
|
+
track(() => _getProp({
|
|
1892
|
+
a: fakeProxy
|
|
1893
|
+
}, 'a'));
|
|
1894
|
+
|
|
1895
|
+
/**
|
|
1896
|
+
@module @ember/object
|
|
1897
|
+
*/
|
|
1898
|
+
|
|
1899
|
+
/**
|
|
1900
|
+
Sets the value of a property on an object, respecting computed properties
|
|
1901
|
+
and notifying observers and other listeners of the change.
|
|
1902
|
+
If the specified property is not defined on the object and the object
|
|
1903
|
+
implements the `setUnknownProperty` method, then instead of setting the
|
|
1904
|
+
value of the property on the object, its `setUnknownProperty` handler
|
|
1905
|
+
will be invoked with the two parameters `keyName` and `value`.
|
|
1906
|
+
|
|
1907
|
+
```javascript
|
|
1908
|
+
import { set } from '@ember/object';
|
|
1909
|
+
set(obj, "name", value);
|
|
1910
|
+
```
|
|
1911
|
+
|
|
1912
|
+
@method set
|
|
1913
|
+
@static
|
|
1914
|
+
@for @ember/object
|
|
1915
|
+
@param {Object} obj The object to modify.
|
|
1916
|
+
@param {String} keyName The property key to set
|
|
1917
|
+
@param {Object} value The value to set
|
|
1918
|
+
@return {Object} the passed value.
|
|
1919
|
+
@public
|
|
1920
|
+
*/
|
|
1921
|
+
|
|
1922
|
+
function set(obj, keyName, value, tolerant) {
|
|
1923
|
+
assert(`Set must be called with three or four arguments; an object, a property key, a value and tolerant true/false`, arguments.length === 3 || arguments.length === 4);
|
|
1924
|
+
assert(`Cannot call set with '${keyName}' on an undefined object.`, obj && typeof obj === 'object' || typeof obj === 'function');
|
|
1925
|
+
assert(`The key provided to set must be a string or number, you passed ${keyName}`, typeof keyName === 'string' || typeof keyName === 'number' && !isNaN(keyName));
|
|
1926
|
+
assert(`'this' in paths is not supported`, typeof keyName !== 'string' || keyName.lastIndexOf('this.', 0) !== 0);
|
|
1927
|
+
|
|
1928
|
+
if (obj.isDestroyed) {
|
|
1929
|
+
assert(`calling set on destroyed object: ${toString(obj)}.${keyName} = ${toString(value)}`, tolerant);
|
|
1930
|
+
return value;
|
|
2185
1931
|
}
|
|
2186
1932
|
|
|
1933
|
+
return isPath(keyName) ? _setPath(obj, keyName, value, tolerant) : _setProp(obj, keyName, value);
|
|
2187
1934
|
}
|
|
1935
|
+
function _setProp(obj, keyName, value) {
|
|
1936
|
+
let descriptor = lookupDescriptor(obj, keyName);
|
|
2188
1937
|
|
|
2189
|
-
|
|
2190
|
-
|
|
1938
|
+
if (descriptor !== null && COMPUTED_SETTERS.has(descriptor.set)) {
|
|
1939
|
+
obj[keyName] = value;
|
|
1940
|
+
return value;
|
|
1941
|
+
}
|
|
2191
1942
|
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
1943
|
+
let currentValue;
|
|
1944
|
+
|
|
1945
|
+
if (DEBUG) {
|
|
1946
|
+
currentValue = getPossibleMandatoryProxyValue(obj, keyName);
|
|
1947
|
+
} else {
|
|
1948
|
+
currentValue = obj[keyName];
|
|
2195
1949
|
}
|
|
2196
1950
|
|
|
2197
|
-
|
|
1951
|
+
if (currentValue === undefined && 'object' === typeof obj && !(keyName in obj) && typeof obj.setUnknownProperty === 'function') {
|
|
1952
|
+
/* unknown property */
|
|
1953
|
+
obj.setUnknownProperty(keyName, value);
|
|
1954
|
+
} else {
|
|
1955
|
+
if (DEBUG) {
|
|
1956
|
+
setWithMandatorySetter(obj, keyName, value);
|
|
1957
|
+
} else {
|
|
1958
|
+
obj[keyName] = value;
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
if (currentValue !== value) {
|
|
1962
|
+
notifyPropertyChange(obj, keyName);
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
return value;
|
|
2198
1967
|
}
|
|
2199
|
-
|
|
2200
|
-
|
|
1968
|
+
|
|
1969
|
+
function _setPath(root, path, value, tolerant) {
|
|
1970
|
+
let parts = path.split('.');
|
|
1971
|
+
let keyName = parts.pop();
|
|
1972
|
+
assert('Property set failed: You passed an empty path', keyName.trim().length > 0);
|
|
1973
|
+
let newRoot = _getPath(root, parts);
|
|
1974
|
+
|
|
1975
|
+
if (newRoot !== null && newRoot !== undefined) {
|
|
1976
|
+
return set(newRoot, keyName, value);
|
|
1977
|
+
} else if (!tolerant) {
|
|
1978
|
+
throw new EmberError(`Property set failed: object in path "${parts.join('.')}" could not be found.`);
|
|
1979
|
+
}
|
|
2201
1980
|
}
|
|
2202
1981
|
/**
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
but for some tooling specific scenarios (e.g. the ember-inspector) it is important to
|
|
2206
|
-
differentiate if a property is a computed property or a "normal" property.
|
|
1982
|
+
Error-tolerant form of `set`. Will not blow up if any part of the
|
|
1983
|
+
chain is `undefined`, `null`, or destroyed.
|
|
2207
1984
|
|
|
2208
|
-
This
|
|
1985
|
+
This is primarily used when syncing bindings, which may try to update after
|
|
1986
|
+
an object has been destroyed.
|
|
2209
1987
|
|
|
2210
|
-
|
|
2211
|
-
@
|
|
2212
|
-
@for @ember/debug
|
|
2213
|
-
@private
|
|
2214
|
-
*/
|
|
1988
|
+
```javascript
|
|
1989
|
+
import { trySet } from '@ember/object';
|
|
2215
1990
|
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
1991
|
+
let obj = { name: "Zoey" };
|
|
1992
|
+
trySet(obj, "contacts.twitter", "@emberjs");
|
|
1993
|
+
```
|
|
2219
1994
|
|
|
2220
|
-
|
|
2221
|
-
|
|
1995
|
+
@method trySet
|
|
1996
|
+
@static
|
|
1997
|
+
@for @ember/object
|
|
1998
|
+
@param {Object} root The object to modify.
|
|
1999
|
+
@param {String} path The property path to set
|
|
2000
|
+
@param {Object} value The value to set
|
|
2001
|
+
@public
|
|
2002
|
+
*/
|
|
2222
2003
|
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2004
|
+
|
|
2005
|
+
function trySet(root, path, value) {
|
|
2006
|
+
return set(root, path, value, true);
|
|
2226
2007
|
}
|
|
2227
2008
|
|
|
2228
2009
|
function alias(altKey) {
|
|
@@ -2966,7 +2747,6 @@ function giveDecoratorSuper(key, decorator, property, descs) {
|
|
|
2966
2747
|
set
|
|
2967
2748
|
}]);
|
|
2968
2749
|
newProperty._readOnly = property._readOnly;
|
|
2969
|
-
newProperty._volatile = property._volatile;
|
|
2970
2750
|
newProperty._meta = property._meta;
|
|
2971
2751
|
newProperty.enumerable = property.enumerable;
|
|
2972
2752
|
return makeComputedDecorator(newProperty, ComputedProperty);
|
|
@@ -3015,7 +2795,7 @@ function applyMergedProperties(key, value, values) {
|
|
|
3015
2795
|
return value;
|
|
3016
2796
|
}
|
|
3017
2797
|
|
|
3018
|
-
let newBase = assign({}, baseValue);
|
|
2798
|
+
let newBase = Object.assign({}, baseValue);
|
|
3019
2799
|
let hasFunction = false;
|
|
3020
2800
|
let props = Object.keys(value);
|
|
3021
2801
|
|
|
@@ -3136,31 +2916,6 @@ function mergeProps(meta$$1, props, descs, values, base, keys, keysWithSuper) {
|
|
|
3136
2916
|
}
|
|
3137
2917
|
}
|
|
3138
2918
|
|
|
3139
|
-
let followMethodAlias;
|
|
3140
|
-
|
|
3141
|
-
if (ALIAS_METHOD) {
|
|
3142
|
-
followMethodAlias = function (obj, alias, descs, values) {
|
|
3143
|
-
let altKey = alias.methodName;
|
|
3144
|
-
let possibleDesc;
|
|
3145
|
-
let desc = descs[altKey];
|
|
3146
|
-
let value = values[altKey];
|
|
3147
|
-
|
|
3148
|
-
if (desc !== undefined || value !== undefined) {// do nothing
|
|
3149
|
-
} else if ((possibleDesc = descriptorForProperty(obj, altKey)) !== undefined) {
|
|
3150
|
-
desc = possibleDesc;
|
|
3151
|
-
value = undefined;
|
|
3152
|
-
} else {
|
|
3153
|
-
desc = undefined;
|
|
3154
|
-
value = obj[altKey];
|
|
3155
|
-
}
|
|
3156
|
-
|
|
3157
|
-
return {
|
|
3158
|
-
desc,
|
|
3159
|
-
value
|
|
3160
|
-
};
|
|
3161
|
-
};
|
|
3162
|
-
}
|
|
3163
|
-
|
|
3164
2919
|
function updateObserversAndListeners(obj, key, fn, add) {
|
|
3165
2920
|
let meta$$1 = observerListenerMetaFor(fn);
|
|
3166
2921
|
if (meta$$1 === undefined) return;
|
|
@@ -3207,14 +2962,6 @@ function applyMixin(obj, mixins, _hideKeys = false) {
|
|
|
3207
2962
|
let value = values[key];
|
|
3208
2963
|
let desc = descs[key];
|
|
3209
2964
|
|
|
3210
|
-
if (ALIAS_METHOD) {
|
|
3211
|
-
while (value !== undefined && isAlias(value)) {
|
|
3212
|
-
let followed = followMethodAlias(obj, value, descs, values);
|
|
3213
|
-
desc = followed.desc;
|
|
3214
|
-
value = followed.value;
|
|
3215
|
-
}
|
|
3216
|
-
}
|
|
3217
|
-
|
|
3218
2965
|
if (value !== undefined) {
|
|
3219
2966
|
if (typeof value === 'function') {
|
|
3220
2967
|
updateObserversAndListeners(obj, key, value, true);
|
|
@@ -3348,9 +3095,7 @@ class Mixin {
|
|
|
3348
3095
|
|
|
3349
3096
|
guidFor(this);
|
|
3350
3097
|
|
|
3351
|
-
if (true
|
|
3352
|
-
/* EMBER_MODERNIZED_BUILT_IN_COMPONENTS */
|
|
3353
|
-
&& Mixin._disableDebugSeal !== true) {
|
|
3098
|
+
if (Mixin._disableDebugSeal !== true) {
|
|
3354
3099
|
Object.seal(this);
|
|
3355
3100
|
}
|
|
3356
3101
|
}
|
|
@@ -3473,9 +3218,7 @@ class Mixin {
|
|
|
3473
3218
|
|
|
3474
3219
|
}
|
|
3475
3220
|
|
|
3476
|
-
if (DEBUG
|
|
3477
|
-
/* EMBER_MODERNIZED_BUILT_IN_COMPONENTS */
|
|
3478
|
-
) {
|
|
3221
|
+
if (DEBUG) {
|
|
3479
3222
|
Object.defineProperty(Mixin, '_disableDebugSeal', {
|
|
3480
3223
|
configurable: true,
|
|
3481
3224
|
enumerable: false,
|
|
@@ -3550,73 +3293,6 @@ function _keys(mixin, ret = new Set(), seen = new Set()) {
|
|
|
3550
3293
|
return ret;
|
|
3551
3294
|
}
|
|
3552
3295
|
|
|
3553
|
-
let AliasImpl;
|
|
3554
|
-
let isAlias;
|
|
3555
|
-
|
|
3556
|
-
if (ALIAS_METHOD) {
|
|
3557
|
-
const ALIASES = new _WeakSet();
|
|
3558
|
-
|
|
3559
|
-
isAlias = alias => {
|
|
3560
|
-
return ALIASES.has(alias);
|
|
3561
|
-
};
|
|
3562
|
-
|
|
3563
|
-
AliasImpl = class AliasImpl {
|
|
3564
|
-
constructor(methodName) {
|
|
3565
|
-
this.methodName = methodName;
|
|
3566
|
-
ALIASES.add(this);
|
|
3567
|
-
}
|
|
3568
|
-
|
|
3569
|
-
};
|
|
3570
|
-
}
|
|
3571
|
-
/**
|
|
3572
|
-
Makes a method available via an additional name.
|
|
3573
|
-
|
|
3574
|
-
```app/utils/person.js
|
|
3575
|
-
import EmberObject, {
|
|
3576
|
-
aliasMethod
|
|
3577
|
-
} from '@ember/object';
|
|
3578
|
-
|
|
3579
|
-
export default EmberObject.extend({
|
|
3580
|
-
name() {
|
|
3581
|
-
return 'Tomhuda Katzdale';
|
|
3582
|
-
},
|
|
3583
|
-
moniker: aliasMethod('name')
|
|
3584
|
-
});
|
|
3585
|
-
```
|
|
3586
|
-
|
|
3587
|
-
```javascript
|
|
3588
|
-
let goodGuy = Person.create();
|
|
3589
|
-
|
|
3590
|
-
goodGuy.name(); // 'Tomhuda Katzdale'
|
|
3591
|
-
goodGuy.moniker(); // 'Tomhuda Katzdale'
|
|
3592
|
-
```
|
|
3593
|
-
|
|
3594
|
-
@method aliasMethod
|
|
3595
|
-
@static
|
|
3596
|
-
@deprecated Use a shared utility method instead
|
|
3597
|
-
@for @ember/object
|
|
3598
|
-
@param {String} methodName name of the method to alias
|
|
3599
|
-
@public
|
|
3600
|
-
*/
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
let aliasMethod;
|
|
3604
|
-
|
|
3605
|
-
if (ALIAS_METHOD) {
|
|
3606
|
-
aliasMethod = function aliasMethod(methodName) {
|
|
3607
|
-
deprecate(`You attempted to alias '${methodName}, but aliasMethod has been deprecated. Consider extracting the method into a shared utility function.`, false, {
|
|
3608
|
-
id: 'object.alias-method',
|
|
3609
|
-
until: '4.0.0',
|
|
3610
|
-
url: 'https://deprecations.emberjs.com/v3.x#toc_object-alias-method',
|
|
3611
|
-
for: 'ember-source',
|
|
3612
|
-
since: {
|
|
3613
|
-
enabled: '3.9.0'
|
|
3614
|
-
}
|
|
3615
|
-
});
|
|
3616
|
-
return new AliasImpl(methodName);
|
|
3617
|
-
};
|
|
3618
|
-
}
|
|
3619
|
-
|
|
3620
3296
|
function observer(...args) {
|
|
3621
3297
|
let funcOrDef = args.pop();
|
|
3622
3298
|
assert('observer must be provided a function or an observer definition', typeof funcOrDef === 'function' || typeof funcOrDef === 'object' && funcOrDef !== null);
|
|
@@ -3900,4 +3576,4 @@ class TrackedDescriptor {
|
|
|
3900
3576
|
@public
|
|
3901
3577
|
*/
|
|
3902
3578
|
|
|
3903
|
-
export { computed, autoComputed, isComputed, ComputedProperty, getCachedValueFor, alias, deprecateProperty, PROXY_CONTENT, _getPath, get,
|
|
3579
|
+
export { computed, autoComputed, isComputed, ComputedProperty, getCachedValueFor, alias, deprecateProperty, PROXY_CONTENT, _getPath, get, _getProp, set, _setProp, trySet, objectAt, replace, replaceInNativeArray, addArrayObserver, removeArrayObserver, arrayContentWillChange, arrayContentDidChange, eachProxyArrayWillChange, eachProxyArrayDidChange, addListener, hasListeners, on, removeListener, sendEvent, isNone, isEmpty, isBlank, isPresent, beginPropertyChanges, changeProperties, endPropertyChanges, notifyPropertyChange, PROPERTY_DID_CHANGE, defineProperty, isElementDescriptor, nativeDescDecorator, descriptorForDecorator, descriptorForProperty, isClassicDecorator, setClassicDecorator, LIBRARIES as libraries, Libraries, getProperties, setProperties, expandProperties, ASYNC_OBSERVERS, SYNC_OBSERVERS, addObserver, activateObserver, removeObserver, flushAsyncObservers, Mixin, mixin, observer, applyMixin, inject, DEBUG_INJECTION_FUNCTIONS, tagForProperty, tagForObject, markObjectAsDirty, tracked, TrackedDescriptor, NAMESPACES, NAMESPACES_BY_ID, addNamespace, findNamespace, findNamespaces, processNamespace, processAllNamespaces, removeNamespace, isSearchDisabled as isNamespaceSearchDisabled, setSearchDisabled as setNamespaceSearchDisabled };
|