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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature Request] Automatic Inclusion of Component in Entry Components #28826

Closed
oleersoy opened this issue Feb 19, 2019 · 7 comments
Closed
Labels
area: core Issues related to the framework runtime feature Issue that requests a new feature freq3: high
Milestone

Comments

@oleersoy
Copy link

馃殌 feature request

Automatic inclusion / management of component in entry components.

Relevant Package

Angular/core angular/compiler

Description

When creating a component / host view as documented here:
https://medium.com/@ole.ersoy/creating-a-host-view-2dfc895a384b

We have to remember to include the component in the entry components. Ideally the Angular compile process would do a code scan and add this for us automatically when the code fits the pattern that requires this.

Or Angular just does away with entry components and builds in dynamic component support by default in all modules. I don't think it's going to add much weight, if any.

Describe the solution you'd like

Ideally we should not have to remember to do this step. Angular should just know how to make dynamic components.

Describe alternatives you've considered

Do the extra step, but this is not a DRY approach. For example if while refactoring someone removes the dynamic component creation from the module, they also have to remember to update the entry components array.

@oleersoy oleersoy changed the title Automatic Inclusion of Component in Entry Components [Feature Request] Automatic Inclusion of Component in Entry Components Feb 19, 2019
@pkozlowski-opensource pkozlowski-opensource added area: core Issues related to the framework runtime fixed by Ivy labels Feb 19, 2019
@pkozlowski-opensource
Copy link
Member

Yeh, it is a bit of extra coding today. We won't be able to change it in the current version of Angular but the good news is that ivy version of the compiler pipeline / runtime entryComponents will essentially become optional and deprecated in the longer run. Will keep this issue open so we can track all the work that needs to happen here (API update, doc update etc.)

@ngbot ngbot bot modified the milestone: needsTriage Feb 19, 2019
@oleersoy
Copy link
Author

oleersoy commented Feb 19, 2019

Very cool!

It seems like the reason entryComponents was added is so that Angular does not prune components it does not see in the template and also to generate component factories.

Ideally stuff like this works like this:

  1. The designer declares via normal imports what the component / service needs.
  2. If Angular suspects that the component / service / pipe / etc. can be pruned then lint it and the user can decide to refactor the code as necessary. This way the code stays DRY, and puts all the control in the hands of the designer. No surprises and no additional need to learn more. I love Angular (It's a really elegant thing), but the learning curve is really steep, and every little bit of uneeded configuration that can be shaved off helps.

@Airblader
Copy link
Contributor

Another option that also wouldn't be intrusive is to do it like with providedIn and just define it in the decorator?

@Component({ entryComponent: true })

@CarterLi
Copy link

CarterLi commented Feb 20, 2019

@Airblader We have already done it by introducing a new customized decorator.

// EntryComponentDecorator.ts
import { TypeDecorator } from '@angular/core';

import { makeDecorator } from './decoratorHelper';

const decoratorName = 'EntryComponent';

export interface EntryComponentDecorator extends DecoratorBase {
  (): TypeDecorator;
  new (): TypeDecorator;
}

export const EntryComponent = makeDecorator(decoratorName) as EntryComponentDecorator;
EntryComponent.decoratorName = decoratorName;
// decoratorHelper.ts: Various utils
import { Type } from '@angular/core';
import * as decoratorsHack from '@angular/core/esm2015/src/util/decorators';

// https://github.com/angular/angular/blob/885f1af509eb7d9ee049349a2fe5565282fbfefb/packages/core/src/util/decorators.ts#L38
export const ANNOTATIONS = decoratorsHack.ANNOTATIONS as
  typeof import ('@angular/core/src/util/decorators').ANNOTATIONS;

// makeDecorator is the "angular way" to create decorator class, but is inaccessible at user-code. Hack it out.
// https://github.com/angular/angular/blob/885f1af509eb7d9ee049349a2fe5565282fbfefb/packages/core/src/util/decorators.ts#L45
export const makeDecorator = decoratorsHack.makeDecorator as
  typeof import ('@angular/core/src/util/decorators').makeDecorator;

/**
 * Gets all decorators (annotations) decorated for specified class
 * @param clazz The type of the class ( function )
 * @param annoName Decorator (annotation) name. Returns all decorators if not specified
 */
export function getAnnotationsOfClass<TAnno>(clazz: Type<any>, annoName?: string) {
  if (ANNOTATIONS in clazz) {
    let annos = clazz[ANNOTATIONS] as TAnno[];
    if (annoName) {
      annos = annos.filter((anno: any) => anno.ngMetadataName === annoName);
    }
    return annos;
  }
  return [];
}

/**
 * Gets all classes which decorated by specified decorator (annotation)
 * @param module The module object (usually get by `import * as ModuleObject from 'module')
 * @param annoName Decorator (annotation) name
 */
export function getClassesOfAnnotations<TAnno>(module: { [key: string]: any }, annoName: string) {
  return Object.values(module)
    .filter(clazz => typeof clazz === 'function')
    .map(clazz => ({
      clazz,
      annos: getAnnotationsOfClass<TAnno>(clazz, annoName),
    }))
    .filter(({ annos }) => annos.length);
}
// DecoratorBase.d.ts
interface DecoratorBase {
  decoratorName: string;
}

Usage:

import * as components from './components';
import * as decorators from './decorators';

@NgModule({
  entryComponents: [
    ...getClassesOfAnnotations<void>(components, decorators.EntryComponent.decoratorName).map(({ clazz }) => clazz),
  ],
})
export class AppModule {}
@EntryComponent()
@Component({
  selector: 'my-component',
  template: '...',
})
export class MyComponentComponent {
}

Other decorators can be done in the same way

// https://github.com/angular/angular/blob/885f1af509eb7d9ee049349a2fe5565282fbfefb/packages/core/src/di/interface/provider.ts#L366
import { Provider as ProviderType, TypeDecorator } from '@angular/core';
import { makeDecorator } from './decoratorHelper';

const decoratorName = 'Provider';

export interface ProviderDecorator extends DecoratorBase {
  (obj?: ProviderType): TypeDecorator;
  new (obj?: ProviderType): TypeDecorator;
}

export const Provider = makeDecorator<ProviderType>(decoratorName) as ProviderDecorator;
Provider.decoratorName = decoratorName;
@NgModule({
  providers: [
    ...[
      ...getClassesOfAnnotations<Provider>(components, decorators.Provider.decoratorName),
    ].map(({ clazz, annos }) => annos[0].hasOwnProperty('provide') ? annos[0] : clazz),
  ],
})

@oleersoy
Copy link
Author

Just realized that it's not enough to declare a component in entryComponents to have it included in the module like this:

  entryComponents: [HelloComponent],
  declarations: [ AppComponent, HelloComponent ],

It has to be included in both declarations and entryComponents like this:

  entryComponents: [HelloComponent],
  declarations: [ AppComponent, HelloComponent ],

This seems redundant and is not DRY. Also cross referenced that this is the case on SO.

@alxhub alxhub added freq3: high feature Issue that requests a new feature labels Mar 26, 2019
@ngbot ngbot bot modified the milestones: needsTriage, Backlog Mar 26, 2019
@mhevery
Copy link
Contributor

mhevery commented Feb 15, 2020

https://ng-run.com/edit/bY1pxzWwKrEqPOjevXnS demonstrates that entryComponents are no longer required. Closing.

@mhevery mhevery closed this as completed Feb 15, 2020
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Mar 17, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: core Issues related to the framework runtime feature Issue that requests a new feature freq3: high
Projects
None yet
Development

No branches or pull requests

6 participants