-
Notifications
You must be signed in to change notification settings - Fork 3.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bind service and state component #6449
Changes from 1 commit
aa35ab3
7bc1806
e201862
521790a
6e3366f
350836c
41fd24f
1c3ea61
0bb8352
09e6276
530da99
d2efb01
7cca3a9
16746d9
49ade02
505615b
4397029
40074ae
d9f872c
446d4ca
c1ca7aa
808c283
d1120dd
1c5df14
3fefcab
f54de34
ce4ea22
80678ef
9b751b3
0660cfe
491c8e0
1b04da1
b247b8e
651c241
5c637aa
7fe4c19
0cf2c8a
5b3d71d
6c98f3a
7875beb
344dc19
6be79b1
14b1d3d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,17 +17,20 @@ | |
import {evaluateBindExpr} from './bind-expr'; | ||
import {getMode} from '../../../src/mode'; | ||
import {isExperimentOn} from '../../../src/experiments'; | ||
import {isFiniteNumber} from '../../../src/types'; | ||
import {dev, user} from '../../../src/log'; | ||
import {vsyncFor} from '../../../src/vsync'; | ||
|
||
const TAG = 'AMP-BIND'; | ||
|
||
/** | ||
* A single binding, e.g. <element [property]="expression"></element>. | ||
* `previousResult` is the result of this expression during the last digest. | ||
* @typedef {{ | ||
* property: !string, | ||
* expression: !string, | ||
* element: !Element | ||
* element: !Element, | ||
* previousResult: (BindExpressionResultDef|undefined) | ||
* }} | ||
*/ | ||
let BindingDef; | ||
|
@@ -67,13 +70,6 @@ export class Bind { | |
/** {!Array<BindingDef>} */ | ||
this.bindings_ = []; | ||
|
||
/** | ||
* Expression results during the previous digest cycle, where the i'th | ||
* element is the last result of `this.bindings_[i].expression`. | ||
* @type {!Array<BindExpressionResultDef>} | ||
*/ | ||
this.previousResults_ = []; | ||
|
||
/** @const {!Object} */ | ||
this.scope_ = Object.create(null); | ||
|
||
|
@@ -86,6 +82,16 @@ export class Bind { | |
/** @const {!Function} */ | ||
this.boundMutate_ = this.mutate_.bind(this); | ||
|
||
/** | ||
* Keys correspond to valid attribute value types. | ||
* @const {!Object<string,boolean>} | ||
*/ | ||
this.attributeValueTypes_ = { | ||
'string': true, | ||
'boolean': true, | ||
'number': true, | ||
}; | ||
|
||
this.ampdoc.whenBodyAvailable().then(body => { | ||
this.bindings_ = this.scanForBindings_(body); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This alone is not enough and can't handle dynamic content (e.g. rendered via amp-mustache templates). Ideally we could use MutationObserver to run this on newly added DOM but aside from not being supported everywhere, I am not sure of its performance. Maybe a good compromise it to have the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. Current approach is to add support for other AMP extensions one-by-one and make adjustments as necessary.
SGTM. |
||
|
||
|
@@ -172,7 +178,7 @@ export class Bind { | |
const task = {measure: this.boundMeasure_, mutate: this.boundMutate_}; | ||
this.vsync_.run(task, { | ||
results: [], | ||
verifyOnly: opt_verifyOnly, | ||
verifyOnly: !!opt_verifyOnly, | ||
}); | ||
} | ||
|
||
|
@@ -199,10 +205,10 @@ export class Bind { | |
const result = state.results[i]; | ||
|
||
// Don't apply mutation if the result hasn't changed. | ||
if (this.shallowEquals_(result, this.previousResults_[i])) { | ||
if (this.shallowEquals_(result, binding.previousResult)) { | ||
continue; | ||
} else { | ||
this.previousResults_[i] = result; | ||
binding.previousResult = result; | ||
} | ||
|
||
if (state.verifyOnly) { | ||
|
@@ -269,11 +275,11 @@ export class Bind { | |
if (element.classList.contains('-amp-element')) { | ||
const resources = element.getResources(); | ||
if (property === 'width') { | ||
user().assert(typeof attributeValue === 'number', | ||
user().assert(isFiniteNumber(attributeValue), | ||
'Invalid result for [width]: %s', attributeValue); | ||
resources./*OK*/changeSize(element, undefined, attributeValue); | ||
} else if (property === 'height') { | ||
user().assert(typeof attributeValue === 'number', | ||
user().assert(isFiniteNumber(attributeValue), | ||
'Invalid result for [height]: %s', attributeValue); | ||
resources./*OK*/changeSize(element, attributeValue, undefined); | ||
} | ||
|
@@ -319,13 +325,14 @@ export class Bind { | |
match = this.compareStringArrays_(initialValue, classes); | ||
} else { | ||
const attribute = element.getAttribute(property); | ||
initialValue = attribute; | ||
if (typeof expectedValue === 'boolean') { | ||
initialValue = !!attribute; | ||
// Boolean attributes return values of either '' or null. | ||
match = (expectedValue && initialValue === '') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should allow There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO this is probably not a common use case and muddles the API a bit. Also, the expression would have to be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't allow toplevel boolean attributes? Never mind. |
||
|| (!expectedValue && initialValue === null); | ||
} else { | ||
initialValue = attribute; | ||
match = (initialValue === expectedValue); | ||
} | ||
// Use abstract equality since boolean attributes return value of ''. | ||
match = (initialValue == expectedValue); | ||
} | ||
|
||
if (!match) { | ||
|
@@ -363,8 +370,8 @@ export class Bind { | |
* @return {(string|boolean|number|null)} | ||
*/ | ||
attributeValueOf_(value) { | ||
if (typeof value === 'string' || typeof value === 'boolean' | ||
|| typeof value === 'number') { | ||
const type = typeof(value); | ||
if (this.attributeValueTypes_[type] !== undefined) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need for the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
return value; | ||
} else { | ||
return null; | ||
|
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.
apply
nodisplay
layout to it instead? (otherwise it will create a line break as it is a block element)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.
Can you modify
layout_
within the element subclass? I don't see any examples of this. It shouldn't cause a newline since wetoggle
it off before layout completes.