aurelia / templating-binding

An implementation of the templating engine's Binding Language abstraction which uses a pluggable command syntax.
MIT License
32 stars 26 forks source link

Support assignment of attribute litterals via interpolation #106

Open MeirionHughes opened 7 years ago

MeirionHughes commented 7 years ago

a lot of third-party functionality is achieved via adding attributes to an element. Some of these attribute values can be quite large and/or can be best assigned via interpolation. It would be useful to support this for the sake of better integration with standards + third-party libraries.

As per @jdanyow's suggestion, introduciton of .attr binding could facilitate these use cases.

For example:

<input aria-valuemin.attr="${current - 10}" aria-valuemax.attr="${current + 10}"></input>

would result in the outcome (assuming current = 0):

<input aria-valuemin="-10" aria-valuemax.attr="10" ... 
jdanyow commented 7 years ago

The problem with the & attr binding behavior is the behavior has no way to tell what the target attribute's original name is. All the attribute names pass through the AttributeMap which typically converts the kebab-case HTML attribute name to a camelCase property name.

Example:

<div foo-bar.bind="baz & attr"></div>

"foo-bar" will be translated to "fooBar". el.setAttribute('fooBar', ...) is going to create an HTML attribute named "foobar". The developer wanted "foo-bar".

grofit commented 7 years ago

I could really do with this feature, and just so there is a link to the use case which started it all: https://github.com/aurelia/framework/issues/669

jdanyow commented 7 years ago

Here's how to extend the binding syntax with a .attr command that will work like you expect:

import {SyntaxInterpreter} from 'aurelia-templating-binding';
import {BehaviorInstruction} from 'aurelia-templating';
import {BindingExpression, BindingBehavior} from 'aurelia-binding';

SyntaxInterpreter.prototype.attr = function(resources, element, info, existingInstruction, context) {
  const instruction = existingInstruction || BehaviorInstruction.attribute(info.attrName);

  const expression = new BindingBehavior(this.parser.parse(info.attrValue), 'attr', []);
  instruction.attributes[info.attrName] = new BindingExpression(
    this.observerLocator,
    info.attrName,
    expression,
    info.defaultBindingMode || this.determineDefaultBindingMode(element, info.attrName, context),
    resources.lookupFunctions
  );

  return instruction;
}

Usage: <foo bar-baz-beep.attr="something"> Result: <foo bar-baz-beep="hello world">

https://gist.run/?id=d736a4c59bed2032017fe286e74ec5ee