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

Alternative way for depreciation of ComponentFactory in order to get componentFactory inputs #44926

Closed
ofirrifo opened this issue Feb 1, 2022 · 11 comments
Assignees
Labels
area: core Issues related to the framework runtime core: dynamic view creation
Milestone

Comments

@ofirrifo
Copy link

ofirrifo commented Feb 1, 2022

Which @angular/* package(s) are the source of the bug?

core

Is this a regression?

Yes

Description

Since Angular 13 the ComponentFactory is deprecated.

Since it depreciated I need an alternative way to get the inputs property that I used to get from the instance of ComponentFactory this.componentFactory.inputs

Here is example of code where I'm using the inputs

@Directive({
  selector: '[dynamicComponentDirective]',
})
export class SingDynamicComponentDirective implements OnInit {
  @Input() dynamicComponentClass!: Type<any>;
  @Input() inputs: any;

  componentFactory: ComponentFactory<any> | undefined = undefined;
  component!: ComponentRef<any> | null;

  constructor(private resolver: ComponentFactoryResolver, private container: ViewContainerRef) {}

  ngOnInit(): void {
    this.container.clear();
    this.componentFactory = void 0;
    this.component = null;
    this.componentFactory = this.resolver.resolveComponentFactory<any>(this.dynamicComponentClass);
    this.component = this.container.createComponent(this.componentFactory);

    this.updateComponentInputs();
  }

  /**
   * update component inputs
   */
  updateComponentInputs(): void {
    const validInputs = this.inputs && isObject(this.inputs) && Object.keys(this.inputs).length;
    if (this.component && validInputs && this.componentFactory && this.componentFactory.inputs) {
      this.componentFactory.inputs.forEach((input: any) => {
        if (this.component && this.inputs.hasOwnProperty(input.propName)) {
          this.component.instance[input.propName] = this.inputs[input.propName];
        }
      });
    }
  }
}

Now that ComponentFactory is deprecated what is the alternative to get the inputs property?

Please provide a link to a minimal reproduction of the bug

above there is a code with the example of my problem

Please provide the environment you discovered this bug in (run ng version)

Angular CLI: 13.2.0
Node: 16.10.0
Package Manager: npm 7.6.0
OS: darwin x64

Angular: 13.2.0
... animations, cdk, cli, common, compiler, compiler-cli, core
... forms, material, platform-browser, platform-browser-dynamic
... router

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1302.0
@angular-devkit/build-angular   13.2.0
@angular-devkit/core            13.2.0
@angular-devkit/schematics      13.2.0
@schematics/angular             13.2.0
ng-packagr                      13.2.0
rxjs                            7.5.2
typescript                      4.5.5

Anything else?

No response

@AndrewKushnir AndrewKushnir self-assigned this Feb 1, 2022
@AndrewKushnir AndrewKushnir added area: core Issues related to the framework runtime core: dynamic view creation labels Feb 1, 2022
@ngbot ngbot bot modified the milestone: needsTriage Feb 1, 2022
@JGrancha
Copy link

JGrancha commented Mar 1, 2022

I am going to comment on a similar example in which I have tried to find an alternative solution but have not found it. I have defined this variable that exports some components that I need to create with the @angular/elements library

export const customElementComponents = [
TextBoxCustomElementComponent,
  CheckBoxCustomElementComponent,
  NumberBoxCustomElementComponent,
  DateBoxCustomElementComponent,
  TextAreaCustomElementComponent,
  WebcamCustomElementComponent,
  LabelCustomElementComponent,
  ColorBoxCustomElementComponent,
  PdfCustomElementComponent,
  PdfFakeCustomElementComponent,
  ImageDrawerCustomElementComponent,
  ImageDrawerFakeCustomElementComponent,
  SelectBoxFixedCustomElementComponent,
  SwitchBoxCustomElementComponent,
  RadioGroupCustomElementComponent,
];

And then in the app.module I have the following to define the Web Components. For that I need to get the selector and I get it with the ComponentFactoryResolver:

import { HTTP_INTERCEPTORS, HttpClient, HttpClientModule } from '@angular/common/http';
import { ComponentFactoryResolver, Injector, NgModule, Type } from '@angular/core';
import { createCustomElement } from '@angular/elements';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterModule } from '@angular/router';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { SharedModule } from '@shared';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { ErrorInterceptor, JwtInterceptor } from './core/interceptors';
import { customElementComponents } from './shared/components/custom-elements';

export function createTranslateLoader(http: HttpClient) {
  return new TranslateHttpLoader(http, './assets/i18n/', '.json')
}

function createCustomElements(injector: Injector, factory: ComponentFactoryResolver){
  customElementComponents.forEach((cmp: Type<any>) => {
    const element = createCustomElement(cmp, { injector: injector })
    const { selector } = factory.resolveComponentFactory(cmp);
    customElements.define(selector, element);
  })
}

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    RouterModule,
    AppRoutingModule,
    HttpClientModule,
    TranslateModule.forRoot({
      loader: {
        provide: TranslateLoader,
        useFactory: createTranslateLoader,
        deps: [HttpClient]
      }
    }),
    SharedModule
  ],
  providers: [
    { provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },
    { provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true }
  ],
  bootstrap: [AppComponent]
})
export class AppModule{
  constructor(private injector: Injector, private factory: ComponentFactoryResolver) {
    createCustomElements(injector, factory);    
  }
}

At the internal core level, I can't comment because I don't control it, but it would still be a good idea to leave the new way of creating components and this way of working with components. At the moment it is only deprecated but I read that in version 16 it was going to be removed...

@winterer
Copy link

Same problem here. Our entire application framework relies on dynamic views that are created on behalf of a (JSON) view model that can be modified by the user at runtime. This model also contains values for input properties that are set on the view component after creation. Currently, we use ComponentFactory.inputs for two reasons:

  1. to limit the setting of values to real input properties (those decorated with @Input). (Other properties of the model are ignored.)
  2. (less important) to be able to use the template name of an input property (instead of the javascript property name) in the json model.

@DanielIcc
Copy link

I have the same problem i need the selector any alternative?

@sabribv
Copy link

sabribv commented Jun 2, 2022

Same problem here. I'm creating dynamic dashboards where all of their components are created dynamically depending on the dashboard instance type. I need to get the components' inputs and outputs to validate that I'm setting them all correctly and to bind their values.

@ofirrifo
Copy link
Author

@alxhub , @AndrewKushnir is this block me from upgrade to angular 14 ?

@AndrewKushnir
Copy link
Contributor

is this block me from upgrade to angular 14 ?

@ofirrifo it should not block you from upgrading to Angular v14, the ComponentFactory symbol is still present there. We plan to perform further research on the APIs needed to support the described use-cases and we'll provide an update once we have additional information.

gund added a commit to gund/ng-dynamic-component that referenced this issue Jun 15, 2022
…ed code

As there is currently no API available to replace it
See angular/angular#44926
gund added a commit to gund/ng-dynamic-component that referenced this issue Jun 15, 2022
…ed code

As there is currently no API available to replace it
See angular/angular#44926
gund added a commit to gund/ng-dynamic-component that referenced this issue Jun 15, 2022
…ed code

As there is currently no API available to replace it
See angular/angular#44926
AndrewKushnir added a commit to AndrewKushnir/angular that referenced this issue Jul 2, 2022
This commit introduces a new function that allows creating a object which exposes a number of getters to retrieve information about a given component.

Closes angular#44926.
@demike
Copy link

demike commented Jul 4, 2022

@pkozlowski-opensource since one of your latest features (#46641) it is possible to set an input value (including marking for change ...).
Wouldn't it be possible to implement some methods on ComponentRef like
public hasInput(name: string): boolean
and / or public getInputs(): string[].

I think this could/would solve this issue.

And as a bonus one would not have to rely on try / catch if the input is not available.
It would be possible to check the availability upfront

@pkozlowski-opensource
Copy link
Member

@demike ComponentMirror API from #46685 gets us almost there but I agree that a boolean check on the ComponentRef would be probably more convenient. @AndrewKushnir WDYT?

@AndrewKushnir
Copy link
Contributor

@pkozlowski-opensource I agree, the ComponentMirror API should provide an ability to check if an input is present. Adding an extra hasInput function to the ComponentRef feels redundant in this case and would add some non-tree-shakable code.

@demike
Copy link

demike commented Jul 7, 2022

On ComponentRef the non-tree-shakable code would be like:

  hasInput(name: string) { 
    return this._tNode.inputs?.[name] !== undefined;
  }

AndrewKushnir added a commit to AndrewKushnir/angular that referenced this issue Jul 8, 2022
This commit introduces a new function that allows creating a object which exposes a number of getters to retrieve information about a given component.

Closes angular#44926.
AndrewKushnir added a commit to AndrewKushnir/angular that referenced this issue Jul 8, 2022
This commit introduces a new function that allows creating a object which exposes a number of getters to retrieve information about a given component.

Closes angular#44926.
AndrewKushnir added a commit to AndrewKushnir/angular that referenced this issue Jul 10, 2022
This commit introduces a new function that allows creating a object which exposes a number of getters to retrieve information about a given component.

Closes angular#44926.
AndrewKushnir added a commit to AndrewKushnir/angular that referenced this issue Jul 11, 2022
This commit introduces a new function that allows creating a object which exposes a number of getters to retrieve information about a given component.

Closes angular#44926.
AndrewKushnir added a commit to AndrewKushnir/angular that referenced this issue Jul 12, 2022
This commit introduces a new function that allows creating a object which exposes a number of getters to retrieve information about a given component.

Closes angular#44926.
AndrewKushnir added a commit to AndrewKushnir/angular that referenced this issue Jul 13, 2022
This commit introduces a new function that allows creating a object which exposes a number of getters to retrieve information about a given component.

Closes angular#44926.
AndrewKushnir added a commit to AndrewKushnir/angular that referenced this issue Jul 14, 2022
This commit introduces a new function that allows creating a object which exposes a number of getters to retrieve information about a given component.

Closes angular#44926.
AndrewKushnir added a commit to AndrewKushnir/angular that referenced this issue Jul 15, 2022
This commit introduces a new function that allows creating a object which exposes a number of getters to retrieve information about a given component.

Closes angular#44926.

PR Close angular#46685
@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 Aug 14, 2022
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 core: dynamic view creation
Projects
None yet
Development

No branches or pull requests

9 participants