|
6 | 6 | * found in the LICENSE file at https://angular.io/license
|
7 | 7 | */
|
8 | 8 |
|
9 |
| -import {SimpleChange} from '../../change_detection/change_detection_util'; |
10 |
| -import {SimpleChanges} from '../../interface/simple_change'; |
11 | 9 | import {OnChanges} from '../../interface/lifecycle_hooks';
|
| 10 | +import {SimpleChange, SimpleChanges} from '../../interface/simple_change'; |
| 11 | +import {EMPTY_OBJ} from '../empty'; |
12 | 12 | import {DirectiveDef, DirectiveDefFeature} from '../interfaces/definition';
|
13 | 13 |
|
14 | 14 | const PRIVATE_PREFIX = '__ngOnChanges_';
|
@@ -40,86 +40,63 @@ type OnChangesExpando = OnChanges & {
|
40 | 40 | * ```
|
41 | 41 | */
|
42 | 42 | export function NgOnChangesFeature<T>(definition: DirectiveDef<T>): void {
|
43 |
| - const publicToDeclaredInputs = definition.declaredInputs; |
44 |
| - const publicToMinifiedInputs = definition.inputs; |
45 |
| - const proto = definition.type.prototype; |
46 |
| - for (const publicName in publicToDeclaredInputs) { |
47 |
| - if (publicToDeclaredInputs.hasOwnProperty(publicName)) { |
48 |
| - const minifiedKey = publicToMinifiedInputs[publicName]; |
49 |
| - const declaredKey = publicToDeclaredInputs[publicName]; |
50 |
| - const privateMinKey = PRIVATE_PREFIX + minifiedKey; |
51 |
| - |
52 |
| - // Walk the prototype chain to see if we find a property descriptor |
53 |
| - // That way we can honor setters and getters that were inherited. |
54 |
| - let originalProperty: PropertyDescriptor|undefined = undefined; |
55 |
| - let checkProto = proto; |
56 |
| - while (!originalProperty && checkProto && |
57 |
| - Object.getPrototypeOf(checkProto) !== Object.getPrototypeOf(Object.prototype)) { |
58 |
| - originalProperty = Object.getOwnPropertyDescriptor(checkProto, minifiedKey); |
59 |
| - checkProto = Object.getPrototypeOf(checkProto); |
60 |
| - } |
61 |
| - |
62 |
| - const getter = originalProperty && originalProperty.get; |
63 |
| - const setter = originalProperty && originalProperty.set; |
64 |
| - |
65 |
| - // create a getter and setter for property |
66 |
| - Object.defineProperty(proto, minifiedKey, { |
67 |
| - get: getter || |
68 |
| - (setter ? undefined : function(this: OnChangesExpando) { return this[privateMinKey]; }), |
69 |
| - set<T>(this: OnChangesExpando, value: T) { |
70 |
| - let simpleChanges = this[PRIVATE_PREFIX]; |
71 |
| - if (!simpleChanges) { |
72 |
| - simpleChanges = {}; |
73 |
| - // Place where we will store SimpleChanges if there is a change |
74 |
| - Object.defineProperty(this, PRIVATE_PREFIX, {value: simpleChanges, writable: true}); |
75 |
| - } |
76 |
| - |
77 |
| - const isFirstChange = !this.hasOwnProperty(privateMinKey); |
78 |
| - const currentChange = simpleChanges[declaredKey]; |
79 |
| - |
80 |
| - if (currentChange) { |
81 |
| - currentChange.currentValue = value; |
82 |
| - } else { |
83 |
| - simpleChanges[declaredKey] = |
84 |
| - new SimpleChange(this[privateMinKey], value, isFirstChange); |
85 |
| - } |
86 |
| - |
87 |
| - if (isFirstChange) { |
88 |
| - // Create a place where the actual value will be stored and make it non-enumerable |
89 |
| - Object.defineProperty(this, privateMinKey, {value, writable: true}); |
90 |
| - } else { |
91 |
| - this[privateMinKey] = value; |
92 |
| - } |
93 |
| - |
94 |
| - if (setter) setter.call(this, value); |
95 |
| - }, |
96 |
| - // Make the property configurable in dev mode to allow overriding in tests |
97 |
| - configurable: !!ngDevMode |
98 |
| - }); |
99 |
| - } |
100 |
| - } |
| 43 | + if (definition.type.prototype.ngOnChanges) { |
| 44 | + definition.setInput = ngOnChangesSetInput; |
| 45 | + |
| 46 | + const prevDoCheck = definition.doCheck; |
| 47 | + const prevOnInit = definition.onInit; |
101 | 48 |
|
102 |
| - // If an onInit hook is defined, it will need to wrap the ngOnChanges call |
103 |
| - // so the call order is changes-init-check in creation mode. In subsequent |
104 |
| - // change detection runs, only the check wrapper will be called. |
105 |
| - if (definition.onInit != null) { |
106 |
| - definition.onInit = onChangesWrapper(definition.onInit); |
| 49 | + definition.onInit = wrapOnChanges(prevOnInit); |
| 50 | + definition.doCheck = wrapOnChanges(prevDoCheck); |
107 | 51 | }
|
| 52 | +} |
| 53 | + |
| 54 | +function wrapOnChanges(hook: (() => void) | null) { |
| 55 | + return function(this: OnChanges) { |
| 56 | + const simpleChangesStore = getSimpleChangesStore(this); |
| 57 | + const current = simpleChangesStore && simpleChangesStore.current; |
| 58 | + |
| 59 | + if (current) { |
| 60 | + simpleChangesStore !.previous = current; |
| 61 | + simpleChangesStore !.current = null; |
| 62 | + this.ngOnChanges(current); |
| 63 | + } |
| 64 | + |
| 65 | + hook && hook.call(this); |
| 66 | + }; |
| 67 | +} |
108 | 68 |
|
109 |
| - definition.doCheck = onChangesWrapper(definition.doCheck); |
| 69 | +function ngOnChangesSetInput<T>( |
| 70 | + this: DirectiveDef<T>, instance: T, value: any, publicName: string, privateName: string): void { |
| 71 | + const simpleChangesStore = getSimpleChangesStore(instance) || |
| 72 | + setSimpleChangesStore(instance, {previous: EMPTY_OBJ, current: null}); |
| 73 | + const current = simpleChangesStore.current || (simpleChangesStore.current = {}); |
| 74 | + const previous = simpleChangesStore.previous; |
| 75 | + |
| 76 | + const declaredName = (this.declaredInputs as{[key: string]: string})[publicName]; |
| 77 | + const previousChange = previous[declaredName]; |
| 78 | + current[declaredName] = new SimpleChange( |
| 79 | + previousChange && previousChange.currentValue, value, previous === EMPTY_OBJ); |
| 80 | + |
| 81 | + (instance as any)[privateName] = value; |
| 82 | +} |
| 83 | + |
| 84 | +const SIMPLE_CHANGES_STORE = '__ngSimpleChanges__'; |
| 85 | + |
| 86 | +function getSimpleChangesStore(instance: any): null|NgSimpleChangesStore { |
| 87 | + return instance[SIMPLE_CHANGES_STORE] || null; |
| 88 | +} |
| 89 | + |
| 90 | +function setSimpleChangesStore(instance: any, store: NgSimpleChangesStore): NgSimpleChangesStore { |
| 91 | + return instance[SIMPLE_CHANGES_STORE] = store; |
110 | 92 | }
|
111 | 93 |
|
112 | 94 | // This option ensures that the ngOnChanges lifecycle hook will be inherited
|
113 | 95 | // from superclasses (in InheritDefinitionFeature).
|
114 | 96 | (NgOnChangesFeature as DirectiveDefFeature).ngInherit = true;
|
115 | 97 |
|
116 |
| -function onChangesWrapper(delegateHook: (() => void) | null) { |
117 |
| - return function(this: OnChangesExpando) { |
118 |
| - const simpleChanges = this[PRIVATE_PREFIX]; |
119 |
| - if (simpleChanges != null) { |
120 |
| - this.ngOnChanges(simpleChanges); |
121 |
| - this[PRIVATE_PREFIX] = null; |
122 |
| - } |
123 |
| - if (delegateHook) delegateHook.apply(this); |
124 |
| - }; |
| 98 | + |
| 99 | +interface NgSimpleChangesStore { |
| 100 | + previous: SimpleChanges; |
| 101 | + current: SimpleChanges|null; |
125 | 102 | }
|
0 commit comments