Skip to content
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

Use-case: annotation inheritance (source: Angular 2) #1

Open
yjbanov opened this issue Aug 22, 2015 · 2 comments
Open

Use-case: annotation inheritance (source: Angular 2) #1

yjbanov opened this issue Aug 22, 2015 · 2 comments

Comments

@yjbanov
Copy link

yjbanov commented Aug 22, 2015

I'm logging this to document an additional use-case for this DEP and to double-check that the use-case is covered.

Angular 2 defines an annotation for users to specify which directives are used by a component. To illustrate, here's a simplified version:

// Defined by Angular 2
class View {
  final List<Type> directives;

  const View(this.directives);
}

The user uses this annotation as follows:

// User code
@View(const [Foo, NgIf])
class MyComponent {}

Here the annotation means "I intend to use directives Foo and NgIf in MyComponent".

Problem

It turns out that the vast majority of components end up using NgIf, NgFor and other standard directives. Only very code size conscious users would want to hand pick every single directive for their component. We therefore would like to define two annotations, @BaseView and an extension of it @View. The latter would automatically include several popular directives so the user doesn't have to specify them. Here's how we would like to define them:

/// Use this if you want to specify every directive used by a component.
class BaseView {
  final List<Type> directives;

  const BaseView(this.directives);
}

/// Use this if you want to specify only non-standard directives. Standard
/// directives are included by default.
class View extends BaseView {
  static const STANDARD_DIRECTIVES = const [NgIf, NgFor, NgClass];

  const View(List<Type> directives)
    : super(const [STANDARD_DIRECTIVES, directives]);
}

The user would then use them as follows:

// User specifies _precisely_ what they want
@BaseView(const [Foo, NgIf])

// User doesn't care much about extra size in JS output but
// doesn't like boilerplate. It's ok include all common directives,
// even though they only use some of them
@View(const [Foo])

The catch

The catch is that the initializer list of View is invalid Dart, as it expects that View may be used in a non-const context. If I understand this DEP correctly, it addresses this problem by allowing the initializer list to be the following:

  const View(List<Type> directives)
    : super([STANDARD_DIRECTIVES, directives]);

It would "inject" const into the list literal expression depending on whether the invocation is in a const context (which it is for annotations) or not.

Alternative: const class

A const class indicates that a class may only be instantiated as part of a const expression. new is not allowed. Here's how it would be declared:

const class View {
  static const STANDARD_DIRECTIVES = const [NgIf, NgFor, NgClass];

  const View(List<Type> directives)
    : super(const [STANDARD_DIRECTIVES, directives]);
}

Because the const modifier guarantees that the constructor is always invoked in a const context, it is safe to assume that directives constructor parameter is also a const value thus making the list literal expression valid.

@munificent
Copy link

Could you just have the transformer or processor that's processing the @View annotations look for both @View and @BaseView and implicitly include those directives in the former? Does the @View annotation object itself actually need to store the standard directives in its list?

@yjbanov
Copy link
Author

yjbanov commented Aug 27, 2015

@munificent, there are some additional constraints:

  • Angular 2 can run in two modes: transformed and reflective. In reflective mode we get annotation data via mirrors. We would have to make both modes aware of @View and @BaseView. While not impossible, it would add some complexity to the code.
  • Angular 2 code is cross-transpiled to JS and Dart from a single source. In JS annotations become decorators which, being just chunks of imperative code attached to "targets" can do whatever they like (although at the expense of static analyzability). It would be undesirable to impose this constraint on JS API as it is capable of expressing collection composition in decorators.
  • We want to allow users to define their own extensions of Angular 2 annotations and add their own common directives. This means we would have to add more machinery to the transformer and to the reflection-mode code to support that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants