-
Notifications
You must be signed in to change notification settings - Fork 772
fix(api): defer getComputedStyle() calls until ngOnInit phase #374
Conversation
@crisbeto - would you mind reviewing this ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it feasible to have a unit test for this?
*/ | ||
ngOnInit() { | ||
this._display = this._getDisplayStyle(); | ||
this._hasInitialized = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hasInitialized
is only used in a ngDoCheck
hook. According to this chart, it shouldn't be possible for ngDoCheck
to fire before ngOnInit
, making the flag redundant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what you mean. Many of the directives using ngOnInit()
, the hasInitialized() is a custom method used in the Flex directives.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean that the only place I see it being used is https://github.com/angular/flex-layout/pull/374/files#diff-5bc48b81f8309cb16bb22da0a329e466R140 which shouldn't be possible to fire before ngOnInit
. I might be missing some context here, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are correct that ShowHideDirective::ngOnChanges()
uses this.hasInitialized
.
ngOnChanges()
is called 1x before ngOnInit() and then later when the @input values are externally changed. The intention of that logic is to SKIP any responses to changes until after ngOnInit()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, makes sense. I suppose it also depends on the component tree.
src/lib/flexbox/api/base.ts
Outdated
@@ -127,7 +135,7 @@ export abstract class BaseFxDirective implements OnDestroy, OnChanges { | |||
let value = this._lookupStyle(element, 'display'); | |||
|
|||
// Note: 'inline' is the default of all elements, unless UA stylesheet overrides | |||
return value ? value.trim() : 'inline'; | |||
return value ? value.trim() : ((element.nodeType === 1) ? 'block' : 'inline'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a huge fan of nested ternaries. Consider breaking it out into an if
and a ternary? E.g.
if (value) {
return value;
}
return element.nodeType === 1 ? 'block' : 'inline';
src/lib/flexbox/api/base.ts
Outdated
@@ -127,7 +135,7 @@ export abstract class BaseFxDirective implements OnDestroy, OnChanges { | |||
let value = this._lookupStyle(element, 'display'); | |||
|
|||
// Note: 'inline' is the default of all elements, unless UA stylesheet overrides | |||
return value ? value.trim() : 'inline'; | |||
return value ? value.trim() : ((element.nodeType === 1) ? 'block' : 'inline'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure that checking that the nodeType === 1
is a valid way to verify that the element is block-level. 1 only means that it's an Element
node which is true for almost everything except cases like comments or raw text nodes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm. That is true. Any suggestions?
Perhaps, we should KISS with return value ? value.trim() : 'block'
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That won't be true necessarily for all cases. One potential solution can be to have a list of the default block-level elements and verify it against the element.nodeName
, assuming that everything else is inline
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That does not seem reasonable for this library.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_lookupStyle()
checks in-line styles or calls getComputedStyle()
. So the expected response should usually be defined.
'inline' is the default of all elements, unless UA stylesheet overrides; which case getComputedStyle() should determine a valid value.
I think return value ? value.trim() : 'inline'
is a reasonable fallback when _lookupStyle()
fails.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will catch most cases, but not the ones that are set to block
by the UA, which are:
p
h1, h2, h3, h4, h5, h6
ol, ul
pre
address
blockquote
dl
div
fieldset
form
hr
noscript
table
198f458
to
a57fa3f
Compare
@crisbeto - working on 1-2 unit tests. |
2c549a3
to
430069e
Compare
@crisbeto - unit-tests provided. Can you review last bits? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly LGTM, a few more nits.
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
import {Renderer2} from '@angular/core'; | ||
import {ɵgetDOM as getDom} from '@angular/platform-browser'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this intended (ɵgetDOM
)? It looks like something generated by the AoT compiler that shouldn't be consumed directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that is intended. Same as in Angular.
src/lib/utils/style-utils.ts
Outdated
export function lookupStyle(element: HTMLElement, styleName: string, inlineOnly = false): any { | ||
let value = ''; | ||
if (element) { | ||
try { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIK try/catch has a slight performance overhead. This one could be worked around by checking whether typeof getComputedStyle !== 'undefined'
.
src/lib/utils/style-utils.ts
Outdated
/** | ||
* Determine the inline or inherited CSS style | ||
*/ | ||
export function lookupStyle(element: HTMLElement, styleName: string, inlineOnly = false): any { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The return value for this one is a string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed.
src/lib/utils/testing/dom-tools.ts
Outdated
styleName: string, | ||
styleValue: string = null, | ||
inlineOnly = true): boolean { | ||
let searchComputed = () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is only used once, it seems redundant to define a local function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed.
430069e
to
fbafab5
Compare
fbafab5
to
75a4112
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@crisbeto - Thank you sir for your help and reviews. |
Previously, the Show/Hide directives would query the `display` style during directive instantiation. For elements instantiated during `*ngFor`, constructor calls to getComputedStyle() will incorrectly return “” due to failures in queries for styles in CSS stylesheets or inline styles. Deferring these types of queries to the ngOnInit() lifecycle event, enables valid response to queries for both inline styles and stylesheets. Fixes #310.
75a4112
to
da83bde
Compare
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
Previously, the Show/Hide directives would query the
display
style during directive instantiation.For elements instantiated during
*ngFor
, constructor calls to getComputedStyle() will incorrectly return “” due to failures in queries for styles in CSS stylesheets or inline styles. Deferring these types of queries to the ngOnInit() lifecycle event, enables valid response to queries for both inline styles and stylesheets.Fixes #310.