Angular.js: Request full support for the Web Components Custom Elements spec in version 1.7

Created on 30 Jan 2018  路  9Comments  路  Source: angular/angular.js

I'm submitting a ...

  • [ ] bug report
  • [x] feature request
  • [ ] other

Current behavior:
Web Components aim to be a standards based web framework for browsers very soon. The Custom Elements spec within it is crucial. From Custom Elements Everywhere one can see that AngularJS 1.6 passes all basic tests but none of the advanced tests. This feature request is to provide full support in version 1.7 and pass the advanced tests as well.

Expected / new behavior:
Long term support for 1.7 has been announced to extend for three years. Hence a goal in this request is to make the transition from AngularJS 1.7 to Web Components directly possible, instead of an intermediate step to another framework.

Minimal reproduction of the problem with instructions:

AngularJS version: 1.7.0

Browser: [all]

Anything else:

$compile feature

Most helpful comment

It is definitely an interesting feature to consider. I don't think it would be too complicated (I expect most complications to arise around sanitization - if we choose to have it :wink:).

It is worth investigating more imo.

#

BTW, you can implement two basic (somewhat hacky) directives to handle the missing features for you (with somewhat awkward syntax). Basically, you need a way to declaratively bind to arbitrary properties:

// Usage:
// ```html
// <!-- Bind `$ctrl.bar` to the `foo` property of `<my-element>`. -->
// <my-element ng-prop="foo->$ctrl.bar">
//
// <!-- Bind multiple to properties. -->
// <my-element ng-prop="foo->$ctrl.bar|||baz->$ctrl.qux+'xyz'">
// ```
.directive('ngProp', function ngPropDirective() {
  // Post-link
  return (scope, elem, attrs) => {
    attrs.ngProp.
      split('|||').
      map(x => /^(.+?)->(.+)$/.exec(x).slice(1)).
      forEach(([propName, exp]) => {
        const watchListener = newValue => elem[0][propName] = newValue;
        scope.$watch(exp, watchListener);
      });
  };
}

Then, you also need a way to declaratively listen to arbitrary events:

// Usage:
// ```html
// <!-- Listen to `foo` event and call `$ctrl.onFoo()`. -->
// <my-element ng-on="foo->$ctrl.bar()">
//
// <!-- Pass the event object to the listener. -->
// <my-element ng-on="foo->$ctrl.bar($event)">
//
// <!-- Listen to multiple events. -->
// <my-element ng-on="foo->$ctrl.bar()|||baz->$ctrl.qux = $ctrl.qux + 1">
// ```
.directive('ngOn', function ngOnDirective($parse) {
  // Post-link
  return (scope, elem, attrs) => {
    attrs.ngOn.
      split('|||').
      map(x => /^(.+?)->(.+)$/.exec(x).slice(1)).
      forEach(([eventName, exp]) => {
        const parsedExp = $parse(exp);
        const listener = $event => scope.$apply(() => parsedExp(scope, {$event}));
        elem.on(eventName, listener);
      });
  };
}

Here is a demo.

All 9 comments

This would be quite a complex task at this point, because AngularJS misses the possibility to bind to arbitrary attributes / listen to arbitrary events, see https://custom-elements-everywhere.com/

I think @gkalpak knows more about this.

It is definitely an interesting feature to consider. I don't think it would be too complicated (I expect most complications to arise around sanitization - if we choose to have it :wink:).

It is worth investigating more imo.

#

BTW, you can implement two basic (somewhat hacky) directives to handle the missing features for you (with somewhat awkward syntax). Basically, you need a way to declaratively bind to arbitrary properties:

// Usage:
// ```html
// <!-- Bind `$ctrl.bar` to the `foo` property of `<my-element>`. -->
// <my-element ng-prop="foo->$ctrl.bar">
//
// <!-- Bind multiple to properties. -->
// <my-element ng-prop="foo->$ctrl.bar|||baz->$ctrl.qux+'xyz'">
// ```
.directive('ngProp', function ngPropDirective() {
  // Post-link
  return (scope, elem, attrs) => {
    attrs.ngProp.
      split('|||').
      map(x => /^(.+?)->(.+)$/.exec(x).slice(1)).
      forEach(([propName, exp]) => {
        const watchListener = newValue => elem[0][propName] = newValue;
        scope.$watch(exp, watchListener);
      });
  };
}

Then, you also need a way to declaratively listen to arbitrary events:

// Usage:
// ```html
// <!-- Listen to `foo` event and call `$ctrl.onFoo()`. -->
// <my-element ng-on="foo->$ctrl.bar()">
//
// <!-- Pass the event object to the listener. -->
// <my-element ng-on="foo->$ctrl.bar($event)">
//
// <!-- Listen to multiple events. -->
// <my-element ng-on="foo->$ctrl.bar()|||baz->$ctrl.qux = $ctrl.qux + 1">
// ```
.directive('ngOn', function ngOnDirective($parse) {
  // Post-link
  return (scope, elem, attrs) => {
    attrs.ngOn.
      split('|||').
      map(x => /^(.+?)->(.+)$/.exec(x).slice(1)).
      forEach(([eventName, exp]) => {
        const parsedExp = $parse(exp);
        const listener = $event => scope.$apply(() => parsedExp(scope, {$event}));
        elem.on(eventName, listener);
      });
  };
}

Here is a demo.

For reference, the other library I've used to do arbitrary prop bindings: https://github.com/robdodson/angular-custom-elements

Hi folks,
I think, if it is added in 1.7 it will help to migrate core features of existing apps into platform/library agnostic, standard compliant solution. I could imagine of porting part of UI into custom elements and later use them in any other application.
Thanks!

Notes:

  • we can add this to the compiler - the cost shouldn't be too high, but we could always put this feature behind a flag
  • Angular sets a security context to specifc properties like innerHTML and sanitizes the input (something with a schemer)

Is there any chance this will make it onto LTS?

We would very much like it to make it. But given there is only one month left for feature development, we can't make any promises.
If anyone from the community wants to take a stub at it, that would be 馃憣

BTW, this is the schema used by Angular to assign security contexts to different elements: dom_security_schema.ts

BTW2, if we implement this (i.e. ng-bind and ng-on), then it would fairly straightforward to also implement ng-bindon (#15464) :grin:

*: Names are debateable 馃槢

Was this page helpful?
0 / 5 - 0 ratings