Angular.js: Able to use ES6 classes for Angular 1.x components

Created on 4 Oct 2017  路  7Comments  路  Source: angular/angular.js

I'm submitting a ...

  • [ ] bug report
  • [ x] feature request
  • [ ] other (Please do not submit support requests here (see above))

Current behavior:
We can use a class for a service or controller. Would be nice to be able to use a class for component as well. This would probably help people migrate to Angular 2 as well as the syntax (even though it's Typescript in Angular 2) could be similar.

Expected / new behavior:
The feature would make Angular 1.x seem more object oriented. For me I use C# for my back-end so it's easier if the front-end looks similar with classes.

Anything else:
I'm not 100% sure it would play out but something from a users point of view like:

class TestComponent {
    constructor() {
  }
}
app.component("testComponent", TestComponent);

Example of using classes for the other things:

https://jsfiddle.net/gtrwzsn1/299/

All 7 comments

You can already use classes for components, all you need to do is:

app.component("testComponent", new TestComponent());

If you really want to benefit classes, you probably want to use classes for your component's controllers as well:

class TestComponentController {
  constructor($timeout) {}
  $onInit() {
  }
}

and your component would like like:

class TestComponent {
  bindings = {
        value: '<'
    };
  controller = TestComponentController;
  template: 'test-component: {{$ctrl.value}}';
}

Note: the benefit of using classes for Component's is very little (a lot of people use a simple Object Literal for Components and use a class for their Component's controller as that's the place where most of the stuff happens anyway), but it is possible.

I think you can go one step further and use a class type directly, as long as the properties are statics on the class:

class TestComponent {
  static bindings = {
    value: '<'
  };
  static controller = TestComponentController;
  static template = 'test-component: {{$ctrl.value}}
}

and so on.

Also you could consider @Hotell's ngMetadata project.

OK, those are nice alternatives to just using 1 normal non static class. Ideally it would be just like you could use services or controllers but I understand why it's not. Consistency is just always nice.

For the record, there is nothing stopping you from using the controller class itself for the static properties:

class MyTestComponent {
  static template = 'Hello, world!';
  static controller = MyTestComponent;

  $onInit() {
    console.log('onInit');
  }
}

.component('myTest', MyTestComponent)

The difference between directives and services/controllers is that directives require more statically available metadata. Services and controllers only require the identifier (e.g. the name), which is provided as the first argument.

But for directives, we need other stuff, such as the restrict, transclude, template, priority values. Some of them might not apply to components, but some do, so you need to provide that metadata along with the component's controller constructor. (Note that the same is true in Angular, where you provide them via the @Component(...) decorator.)

#

So, you can either use a 3rd-party library (such as ngMetadata) to emulate the @Component() decorator, or provide the metadata in a static object (along with the controller constructor).

I tend to think of...

{
  template: '...',
  bindings: { ... },
  controller: class MyTestComponent { ... }
}

...as the AngularJS equivalent of Angular's...

@Component({
  ...
  template: '...',
  inputs: [ ... ],
  ouputs: [ ... ],
})
class MyTestComponent { ... }

#

On top of that, you can use static fields on the controller constructor itself to provide the metadata (as proposed above), which is as close to service/controller registering as you can get.

I like to take it a step further and also provide the selector on the controller :grin::

class MyTestComponent {
  static selector = 'myTest';
  static template = 'Hello, world!';
  static controller = TestComponent;

  $onInit() {
    console.log('onInit');
  }
}

.component(MyTestComponent.selector, MyTestComponent)

Using the controller class itself (your first and last example) is brilliant! It makes more sense to me now that those properties are static since those are values that each component instance share, and it puts them all into one class which is great! Thank you for those examples!

Wait, it doesn't seem possible to define static class variables that way in ES6. So that doesn't work for me.

Looks like I have to make them get selectors instead:

    static get selector() { return 'app'; }
    static get templateUrl() { return 'app.html'; }
    static get bindings() { return { name: '<' }; }
    static get controllerAs() { return 'vm'; }
    static get controller() { return ['NamesService', AppComp]; }

A complete example in case anyone else is interested:

class AppComp {
    // setup component properties
    static get templateUrl() { return 'app.html'; }
    static get controllerAs() { return 'vm'; }
    static get controller() { return ['NamesService', AppComp]; }
    static get bindings() {
        return {
            name: '<'
        };
    }

    // component code below
    constructor(namesService) {
        console.log("AppComp ctor");

        // setup component instance variables
        this.selectedUser = null;
        this.users = null;
        this.users = namesService.getNames();
    }

    SelectName(user) {
        this.selectedUser = user;
    }

    $onInit(){
        console.log('onInit');
    }
}
appModule.component("app", AppComp);

My examples were in TypeScript (which does support static properties). If you are using ES6, then you need to either define them as static getters (as you already found out) or attach them to the class itself:

class MyTestComponent {
   ...
}
MyTestComponent.selector = '...';
MyTestComponent.template = '...';
MyTestComponent.controller = MyTestComponent;
Was this page helpful?
0 / 5 - 0 ratings