Osakr
Osakr

Reputation: 1066

Identify component declared variables using the componentRef instance in Angular

I was wondering if there is a way to identify undefined variables inside Angular components via the componentRef

Let me explain a bit. Assume we have a component called contentComponent with some defined variables like this:

export class ContentComponent {

  public public_var_without_content: any;
  public public_var_with_empty_string: string = "";
  public public_var_with_content: any = { foo: 'bar', baz: ['foo', 'bar', 'baz']};
  
  private private_var: any;
  private private_var_with_content: string = "foobar";

  constructor() { }
}

Basically we have several public and private variables defined, some of them have content and other are undefined.

Now we create dynamically ( or just get the instance of the component ) via the componentFactory API (https://angular.io/api/core/ComponentFactory) like this:

import { ContentComponent } from '.. the route .. ';

... Code

// This is where the component will be rendered in the template
const viewContainerRef = this.componentLoader.viewContainerRef;
viewContainerRef.clear();

var componentRef = viewContainerRef.createComponent<any>(ContentComponent);
const componentInstance = componentRef.instance;

... More code

For now we have generated the component and rendered in the html template, so we are able to see the generated template inside a different component.

However, what if you want to play a little bit with the component, and we want to check if some variable is present in the component, as we saw in the first block of code, we have five declared variables, what if I do a console.log(componentInstance)?

I only get the variables which have any kind of content, even the private ones ( that is just another question :D ) as you can see here: enter image description here

So we can see 3 of the 5 variables, I cannot see the undefined variables, and my question is:

Can we get a mapping or something of the defined variables of the component?

EDIT:

When I assign the input values for the component after creating it I want to be able to identify which ones really exists and which ones doesn't exists. So I have a code like this:

// this.component is an object with a reference to the imported 
// component itself, to all the params i want to bind (inputs) 
// and all the functions I want to trigger when some event is 
// triggered ( ouput eventEmitters )
for ( let param of Object.keys(this.component.params) ) {
     if (!componentRef.instance[param]) 
          // Here all the variables that are undefined will pass this condition and I only want to pass those that are not defined in the created component
          // throw new Error(`The input parameter '${param} does not exist in the component`);
     componentRef.instance[param] = this.component.params[param];
}

So basically this is it, I have components ( child-components ) that are loaded by a dynamic component loader which is contained in another component ( component-container ). In base an array of components in the component-container i want to bind all child-component Input variables to the data which is in the component-container array.

And also, if someone knows why the private variables are visible from outside the component would be nice, but I think that's a whole new question.

Edit: Documentation about ComponentRef API: https://angular.io/api/core/ComponentRef

Upvotes: 1

Views: 1224

Answers (1)

ardentia
ardentia

Reputation: 724

The reason why you don't see properties that don't have assigned values is because Chrome doesn't display them in the dev tools (I guess it's some sort of an optimization). You usually can see them if you expand the object prototype ([[Prototype]]).

Secondly, the reason why you can inspect class properties marked as private is because you're viewing the transpiled TypeScript code. The access modifier rules will be applied in two cases:

  • when your IDE/task runner lints your code
  • when you build your code

Both will result in errors if you try to modify private class properties but this is always before runtime.

Since TypeScript transpiles to plain JavaScript after you build your application, the end result is just a JavaScript object which by default has no "concealed" properties.

Other than that, once you create your component instance, you can assign values to its public properties and/or call its public methods and Angular will reflect those changes.

Edit: The way you iterate over the instantiated properties will not work, because you do not have a differentiation between @Input properties and ordinary class members. You already know the type of the component you are creating (i.e, you are creating a new instance of the ChildComponent class). You already know which properties are @Inputs in that child component - you only need to assign values to them after checking whether they have values.

Let's take this example child class with some @Input props:

@Component({
  //omitted
})
export class ExampleChildComponent {
  @Input() input1;
  @Input() input2;
  @Input() input3 = 'value';
  //rest of the code emitted
}

In your parent component where you instantiate ExampleChildComponent:

if (!this.component.input1) {
  this.component.input1 = 'some value';
}

The above check needs to be repeated for each of the inputs of ExampleChildComponent.

If you need a dynamic list of all @Input properties of ExampleChildComponent, you can create one, as shown here: https://stackoverflow.com/a/38655925/17963017 and then you can access it through the parent and assign values accordingly.

Upvotes: 2

Related Questions