1 | declare module '@ember/object/observable' {
|
2 | |
3 |
|
4 |
|
5 | import Mixin from '@ember/object/mixin';
|
6 | export type ObserverMethod<Target, Sender> =
|
7 | | keyof Target
|
8 | | ((this: Target, sender: Sender, key: string, value: any, rev: number) => void);
|
9 | /**
|
10 | ## Overview
|
11 |
|
12 | This mixin provides properties and property observing functionality, core
|
13 | features of the Ember object model.
|
14 |
|
15 | Properties and observers allow one object to observe changes to a
|
16 | property on another object. This is one of the fundamental ways that
|
17 | models, controllers and views communicate with each other in an Ember
|
18 | application.
|
19 |
|
20 | Any object that has this mixin applied can be used in observer
|
21 | operations. That includes `EmberObject` and most objects you will
|
22 | interact with as you write your Ember application.
|
23 |
|
24 | Note that you will not generally apply this mixin to classes yourself,
|
25 | but you will use the features provided by this module frequently, so it
|
26 | is important to understand how to use it.
|
27 |
|
28 | ## Using `get()` and `set()`
|
29 |
|
30 | Because of Ember's support for bindings and observers, you will always
|
31 | access properties using the get method, and set properties using the
|
32 | set method. This allows the observing objects to be notified and
|
33 | computed properties to be handled properly.
|
34 |
|
35 | More documentation about `get` and `set` are below.
|
36 |
|
37 | ## Observing Property Changes
|
38 |
|
39 | You typically observe property changes simply by using the `observer`
|
40 | function in classes that you write.
|
41 |
|
42 | For example:
|
43 |
|
44 | ```javascript
|
45 | import { observer } from '@ember/object';
|
46 | import EmberObject from '@ember/object';
|
47 |
|
48 | EmberObject.extend({
|
49 | valueObserver: observer('value', function(sender, key, value, rev) {
|
50 |
|
51 |
|
52 | })
|
53 | });
|
54 | ```
|
55 |
|
56 | Although this is the most common way to add an observer, this capability
|
57 | is actually built into the `EmberObject` class on top of two methods
|
58 | defined in this mixin: `addObserver` and `removeObserver`. You can use
|
59 | these two methods to add and remove observers yourself if you need to
|
60 | do so at runtime.
|
61 |
|
62 | To add an observer for a property, call:
|
63 |
|
64 | ```javascript
|
65 | object.addObserver('propertyKey', targetObject, targetAction)
|
66 | ```
|
67 |
|
68 | This will call the `targetAction` method on the `targetObject` whenever
|
69 | the value of the `propertyKey` changes.
|
70 |
|
71 | Note that if `propertyKey` is a computed property, the observer will be
|
72 | called when any of the property dependencies are changed, even if the
|
73 | resulting value of the computed property is unchanged. This is necessary
|
74 | because computed properties are not computed until `get` is called.
|
75 |
|
76 | @class Observable
|
77 | @public
|
78 | */
|
79 | interface Observable {
|
80 | /**
|
81 | Retrieves the value of a property from the object.
|
82 |
|
83 | This method is usually similar to using `object[keyName]` or `object.keyName`,
|
84 | however it supports both computed properties and the unknownProperty
|
85 | handler.
|
86 |
|
87 | Because `get` unifies the syntax for accessing all these kinds
|
88 | of properties, it can make many refactorings easier, such as replacing a
|
89 | simple property with a computed property, or vice versa.
|
90 |
|
91 | ### Computed Properties
|
92 |
|
93 | Computed properties are methods defined with the `property` modifier
|
94 | declared at the end, such as:
|
95 |
|
96 | ```javascript
|
97 | import { computed } from '@ember/object';
|
98 |
|
99 | fullName: computed('firstName', 'lastName', function() {
|
100 | return this.get('firstName') + ' ' + this.get('lastName');
|
101 | })
|
102 | ```
|
103 |
|
104 | When you call `get` on a computed property, the function will be
|
105 | called and the return value will be returned instead of the function
|
106 | itself.
|
107 |
|
108 | ### Unknown Properties
|
109 |
|
110 | Likewise, if you try to call `get` on a property whose value is
|
111 | `undefined`, the `unknownProperty()` method will be called on the object.
|
112 | If this method returns any value other than `undefined`, it will be returned
|
113 | instead. This allows you to implement "virtual" properties that are
|
114 | not defined upfront.
|
115 |
|
116 | @method get
|
117 | @param {String} keyName The property to retrieve
|
118 | @return {Object} The property value or undefined.
|
119 | @public
|
120 | */
|
121 | get<K extends keyof this>(key: K): this[K];
|
122 | get(key: string): unknown;
|
123 | /**
|
124 | To get the values of multiple properties at once, call `getProperties`
|
125 | with a list of strings or an array:
|
126 |
|
127 | ```javascript
|
128 | record.getProperties('firstName', 'lastName', 'zipCode');
|
129 | // { firstName: 'John', lastName: 'Doe', zipCode: '10011' }
|
130 | ```
|
131 |
|
132 | is equivalent to:
|
133 |
|
134 | ```javascript
|
135 | record.getProperties(['firstName', 'lastName', 'zipCode']);
|
136 | // { firstName: 'John', lastName: 'Doe', zipCode: '10011' }
|
137 | ```
|
138 |
|
139 | @method getProperties
|
140 | @param {String...|Array} list of keys to get
|
141 | @return {Object}
|
142 | @public
|
143 | */
|
144 | getProperties<L extends Array<keyof this>>(
|
145 | list: L
|
146 | ): {
|
147 | [Key in L[number]]: this[Key];
|
148 | };
|
149 | getProperties<L extends Array<keyof this>>(
|
150 | ...list: L
|
151 | ): {
|
152 | [Key in L[number]]: this[Key];
|
153 | };
|
154 | getProperties<L extends string[]>(
|
155 | list: L
|
156 | ): {
|
157 | [Key in L[number]]: unknown;
|
158 | };
|
159 | getProperties<L extends string[]>(
|
160 | ...list: L
|
161 | ): {
|
162 | [Key in L[number]]: unknown;
|
163 | };
|
164 | /**
|
165 | Sets the provided key or path to the value.
|
166 |
|
167 | ```javascript
|
168 | record.set("key", value);
|
169 | ```
|
170 |
|
171 | This method is generally very similar to calling `object["key"] = value` or
|
172 | `object.key = value`, except that it provides support for computed
|
173 | properties, the `setUnknownProperty()` method and property observers.
|
174 |
|
175 | ### Computed Properties
|
176 |
|
177 | If you try to set a value on a key that has a computed property handler
|
178 | defined (see the `get()` method for an example), then `set()` will call
|
179 | that method, passing both the value and key instead of simply changing
|
180 | the value itself. This is useful for those times when you need to
|
181 | implement a property that is composed of one or more member
|
182 | properties.
|
183 |
|
184 | ### Unknown Properties
|
185 |
|
186 | If you try to set a value on a key that is undefined in the target
|
187 | object, then the `setUnknownProperty()` handler will be called instead. This
|
188 | gives you an opportunity to implement complex "virtual" properties that
|
189 | are not predefined on the object. If `setUnknownProperty()` returns
|
190 | undefined, then `set()` will simply set the value on the object.
|
191 |
|
192 | ### Property Observers
|
193 |
|
194 | In addition to changing the property, `set()` will also register a property
|
195 | change with the object. Unless you have placed this call inside of a
|
196 | `beginPropertyChanges()` and `endPropertyChanges(),` any "local" observers
|
197 | (i.e. observer methods declared on the same object), will be called
|
198 | immediately. Any "remote" observers (i.e. observer methods declared on
|
199 | another object) will be placed in a queue and called at a later time in a
|
200 | coalesced manner.
|
201 |
|
202 | @method set
|
203 | @param {String} keyName The property to set
|
204 | @param {Object} value The value to set or `null`.
|
205 | @return {Object} The passed value
|
206 | @public
|
207 | */
|
208 | set<K extends keyof this, T extends this[K]>(key: K, value: T): T;
|
209 | set<T>(key: string, value: T): T;
|
210 | /**
|
211 | Sets a list of properties at once. These properties are set inside
|
212 | a single `beginPropertyChanges` and `endPropertyChanges` batch, so
|
213 | observers will be buffered.
|
214 |
|
215 | ```javascript
|
216 | record.setProperties({ firstName: 'Charles', lastName: 'Jolley' });
|
217 | ```
|
218 |
|
219 | @method setProperties
|
220 | @param {Object} hash the hash of keys and values to set
|
221 | @return {Object} The passed in hash
|
222 | @public
|
223 | */
|
224 | setProperties<
|
225 | K extends keyof this,
|
226 | P extends {
|
227 | [Key in K]: this[Key];
|
228 | }
|
229 | >(
|
230 | hash: P
|
231 | ): P;
|
232 | setProperties<T extends Record<string, unknown>>(hash: T): T;
|
233 | /**
|
234 | Convenience method to call `propertyWillChange` and `propertyDidChange` in
|
235 | succession.
|
236 |
|
237 | Notify the observer system that a property has just changed.
|
238 |
|
239 | Sometimes you need to change a value directly or indirectly without
|
240 | actually calling `get()` or `set()` on it. In this case, you can use this
|
241 | method instead. Calling this method will notify all observers that the
|
242 | property has potentially changed value.
|
243 |
|
244 | @method notifyPropertyChange
|
245 | @param {String} keyName The property key to be notified about.
|
246 | @return {Observable}
|
247 | @public
|
248 | */
|
249 | notifyPropertyChange(keyName: string): this;
|
250 | /**
|
251 | Adds an observer on a property.
|
252 |
|
253 | This is the core method used to register an observer for a property.
|
254 |
|
255 | Once you call this method, any time the key's value is set, your observer
|
256 | will be notified. Note that the observers are triggered any time the
|
257 | value is set, regardless of whether it has actually changed. Your
|
258 | observer should be prepared to handle that.
|
259 |
|
260 | There are two common invocation patterns for `.addObserver()`:
|
261 |
|
262 | - Passing two arguments:
|
263 | - the name of the property to observe (as a string)
|
264 | - the function to invoke (an actual function)
|
265 | - Passing three arguments:
|
266 | - the name of the property to observe (as a string)
|
267 | - the target object (will be used to look up and invoke a
|
268 | function on)
|
269 | - the name of the function to invoke on the target object
|
270 | (as a string).
|
271 |
|
272 | ```app/components/my-component.js
|
273 | import Component from '@ember/component';
|
274 |
|
275 | export default Component.extend({
|
276 | init() {
|
277 | this._super(...arguments);
|
278 |
|
279 |
|
280 |
|
281 |
|
282 | this.addObserver('foo', this, 'fooDidChange');
|
283 |
|
284 |
|
285 | this.addObserver('foo', (...args) => {
|
286 | this.fooDidChange(...args);
|
287 | });
|
288 | },
|
289 |
|
290 | fooDidChange() {
|
291 |
|
292 | }
|
293 | });
|
294 | ```
|
295 |
|
296 | ### Observer Methods
|
297 |
|
298 | Observer methods have the following signature:
|
299 |
|
300 | ```app/components/my-component.js
|
301 | import Component from '@ember/component';
|
302 |
|
303 | export default Component.extend({
|
304 | init() {
|
305 | this._super(...arguments);
|
306 | this.addObserver('foo', this, 'fooDidChange');
|
307 | },
|
308 |
|
309 | fooDidChange(sender, key, value, rev) {
|
310 |
|
311 | }
|
312 | });
|
313 | ```
|
314 |
|
315 | The `sender` is the object that changed. The `key` is the property that
|
316 | changes. The `value` property is currently reserved and unused. The `rev`
|
317 | is the last property revision of the object when it changed, which you can
|
318 | use to detect if the key value has really changed or not.
|
319 |
|
320 | Usually you will not need the value or revision parameters at
|
321 | the end. In this case, it is common to write observer methods that take
|
322 | only a sender and key value as parameters or, if you aren't interested in
|
323 | any of these values, to write an observer that has no parameters at all.
|
324 |
|
325 | @method addObserver
|
326 | @param {String} key The key to observe
|
327 | @param {Object} target The target object to invoke
|
328 | @param {String|Function} method The method to invoke
|
329 | @param {Boolean} sync Whether the observer is sync or not
|
330 | @return {Observable}
|
331 | @public
|
332 | */
|
333 | addObserver<Target>(
|
334 | key: keyof this,
|
335 | target: Target,
|
336 | method: ObserverMethod<Target, this>
|
337 | ): this;
|
338 | addObserver(key: keyof this, method: ObserverMethod<this, this>): this;
|
339 | /**
|
340 | Remove an observer you have previously registered on this object. Pass
|
341 | the same key, target, and method you passed to `addObserver()` and your
|
342 | target will no longer receive notifications.
|
343 |
|
344 | @method removeObserver
|
345 | @param {String} key The key to observe
|
346 | @param {Object} target The target object to invoke
|
347 | @param {String|Function} method The method to invoke
|
348 | @param {Boolean} sync Whether the observer is async or not
|
349 | @return {Observable}
|
350 | @public
|
351 | */
|
352 | removeObserver<Target>(
|
353 | key: keyof this,
|
354 | target: Target,
|
355 | method: ObserverMethod<Target, this>
|
356 | ): this;
|
357 | removeObserver(key: keyof this, method: ObserverMethod<this, this>): this;
|
358 | /**
|
359 | Set the value of a property to the current value plus some amount.
|
360 |
|
361 | ```javascript
|
362 | person.incrementProperty('age');
|
363 | team.incrementProperty('score', 2);
|
364 | ```
|
365 |
|
366 | @method incrementProperty
|
367 | @param {String} keyName The name of the property to increment
|
368 | @param {Number} increment The amount to increment by. Defaults to 1
|
369 | @return {Number} The new property value
|
370 | @public
|
371 | */
|
372 | incrementProperty(keyName: keyof this, increment?: number): number;
|
373 | /**
|
374 | Set the value of a property to the current value minus some amount.
|
375 |
|
376 | ```javascript
|
377 | player.decrementProperty('lives');
|
378 | orc.decrementProperty('health', 5);
|
379 | ```
|
380 |
|
381 | @method decrementProperty
|
382 | @param {String} keyName The name of the property to decrement
|
383 | @param {Number} decrement The amount to decrement by. Defaults to 1
|
384 | @return {Number} The new property value
|
385 | @public
|
386 | */
|
387 | decrementProperty(keyName: keyof this, decrement?: number): number;
|
388 | /**
|
389 | Set the value of a boolean property to the opposite of its
|
390 | current value.
|
391 |
|
392 | ```javascript
|
393 | starship.toggleProperty('warpDriveEngaged');
|
394 | ```
|
395 |
|
396 | @method toggleProperty
|
397 | @param {String} keyName The name of the property to toggle
|
398 | @return {Boolean} The new property value
|
399 | @public
|
400 | */
|
401 | toggleProperty(keyName: keyof this): boolean;
|
402 | /**
|
403 | Returns the cached value of a computed property, if it exists.
|
404 | This allows you to inspect the value of a computed property
|
405 | without accidentally invoking it if it is intended to be
|
406 | generated lazily.
|
407 |
|
408 | @method cacheFor
|
409 | @param {String} keyName
|
410 | @return {Object} The cached value of the computed property, if any
|
411 | @public
|
412 | */
|
413 | cacheFor<K extends keyof this>(key: K): unknown;
|
414 | }
|
415 | const Observable: Mixin;
|
416 | export default Observable;
|
417 | }
|
418 |
|
\ | No newline at end of file |