Intent to Implement: AMP Bind Macros
See original GitHub issueBackground
In using amp-bind, there are often times where it is necessary to use the same expression fragment in several different places. These expressions can be difficult to read because they are often long one line expressions. And they are difficult to maintain because the same expression fragment is repeated in multiple places.
Proposal
In order to allow re-use of amp-bind expressions, we should create a new element (as part of the amp-bind extension) called amp-macro. Similar to amp-state, this element wouldn’t actually display on the page but would simply be parsed by the amp-bind extension. The element would have three attributes:
name
, the name of the macro used to reference this macro in amp-bind expressionsarguments
, an ordered comma separated list of argument names for this macroexpression
, the expression to be evaluated when this macro is used
Example
<amp-state id="travelers">
<script type="application/json">
[
{
"firstName": "John",
"lastName": "Doe",
"email": "johndoe@example.com",
"phoneNumber": "123-456-7890"
}
]
</script>
</amp-state>
<amp-macro name="isTravelerValid"
args="traveler" expression="traveler.firstName && traveler.lastName && traveler.email && traveler.phoneNumber" />
<amp-macro name="areAllTravelersValid" expression="travelers.some(t => !isTravelerValid(t))" />
<div [text]="areAllTravelersValid() ? 'Valid' : 'Invalid'">
Valid
</div>
Details
Scope
A macro can reference any of its arguments in the expression, as well as the entire state object. If an argument has a name that clashes with the id of an amp-state on the page, that part of the state won’t be accessible in the macro since the arguments have priority.
Arguments
All arguments in a macro are always required. If a macro is called with fewer or more arguments than it expects, an error will be thrown.
Composability
Macros should be able to use other macros (as shown above), as long as the callee is defined above the caller in the document. Recursion will not be allowed, so a macro can never call itself.
Expression Size
amp-bind enforces that the AST for any given expression should not have more than 50 nodes. For macro calls within expressions, we should include the size of the callee expression in the size of the caller to ensure that amp-macro does not allow more complicated expressions to be used. We should also ensure that refactoring an expression into one or more macros does not result in a larger AST, so we don’t discourage or prevent modularizing the expression into macros.
Examples
<amp-state id="triangle">
<script type="application/json">
{
"base": 6,
"height": 8
}
</script>
</amp-state>
<!-- Expression size = 9 -->
<div class="triangle-area" [text]="0.5 * triangle.base * triangle.height"></div>
<!-- Expression size = 7 -->
<amp-macro name="triangleArea" arguments="base, height" expression="3.14 * base * height"/>
<!-- Expression size = 9 -->
<div class="triangle-area" [text]="triangleArea(triangle.base, triangle.height)"></div>
cc @choumx
Issue Analytics
- State:
- Created 6 years ago
- Comments:7 (6 by maintainers)
@cramforce @choumx @aghassemi
I just had one thought on the arguments syntax (named vs unnamed). While named arguments are consistent with actions, unnamed arguments are consistent with built in functions in bind expressions. There aren’t currently any places in bind expressions where named arguments are used. So I’m starting to think unnamed arguments might be better for consistency. But I don’t have a very strong opinion so I’m happy to implement it either way. What do you think?
consistency with bind would be more important, so unnamed seems like the right choice.