@@ -62,6 +62,45 @@ export const proxyComponent = (Cstr: d.ComponentConstructor, cmpMeta: d.Componen
62
62
prototype . attributeChangedCallback = function ( attrName : string , _oldValue : string , newValue : string ) {
63
63
plt . jmp ( ( ) => {
64
64
const propName = attrNameToPropName . get ( attrName ) ;
65
+
66
+ // In a webcomponent lifecyle the attributeChangedCallback runs prior to connectedCallback
67
+ // in the case where an attribute was set inline.
68
+ // ```html
69
+ // <my-component some-attribute="some-value"></my-component>
70
+ // ```
71
+ //
72
+ // There is an edge case where a developer sets the attribute inline on a custom element and then programatically
73
+ // changes it before it has been upgraded as shown below:
74
+ //
75
+ // ```html
76
+ // <!-- this component has _not_ been upgraded yet -->
77
+ // <my-component id="test" some-attribute="some-value"></my-component>
78
+ // <script>
79
+ // // grab non-upgraded component
80
+ // el = document.querySelector("#test");
81
+ // el.someAttribute = "another-value";
82
+ // // upgrade component
83
+ // cutsomElements.define('my-component', MyComponent);
84
+ // </script>
85
+ // ```
86
+ // In this case if we do not unshadow here and use the value of the shadowing property, attributeChangedCallback
87
+ // will be called with `newValue = "some-value"` and will set the shadowed property (this.someAttribute = "another-value")
88
+ // to the value that was set inline i.e. "some-value" from above example. When
89
+ // the connectedCallback attempts to unshadow it will use "some-value" as the intial value rather than "another-value"
90
+ //
91
+ // The case where the attribute was NOT set inline but was not set programmatically shall be handled/unshadowed
92
+ // by connectedCallback as this attributeChangedCallback will not fire.
93
+ //
94
+ // https://developers.google.com/web/fundamentals/web-components/best-practices#lazy-properties
95
+ //
96
+ // TODO(STENCIL-16) we should think about whether or not we actually want to be reflecting the attributes to
97
+ // properties here given that this goes against best practices outlined here
98
+ // https://developers.google.com/web/fundamentals/web-components/best-practices#avoid-reentrancy
99
+ if ( this . hasOwnProperty ( propName ) ) {
100
+ newValue = this [ propName ] ;
101
+ delete this [ propName ] ;
102
+ }
103
+
65
104
this [ propName ] = newValue === null && typeof this [ propName ] === 'boolean' ? false : newValue ;
66
105
} ) ;
67
106
} ;
0 commit comments