0

As title implies i'm looking for a way to bind an object with multiple properties to component @Inputs without having to explicitly write all of them

Let's say I have this object

let params = {
    delta: 0.2,
    theta: 2.3,
    sigma: 'foo',
}

Instead of having to bind all of them individually like this

<my-component
    [delta]="params.delta"
    [theta]="params.theta"
    [sigma]="params.sigma"/>

I would like bind all of them at once.

<my-component [someDirectiveIDontKnow]="params"/>

How can i do this?

Found a link to a previously asked question but couldn't get that to work properly.

Edit: I'm not asking how to bind @Inputs. Imagine that the component I'm rendering has 40 @Inputs and I'm NOT allowed to rewrite it to just accept one @Input that could contain all the params.

So writing a template that uses this component gets really ugly and big.

....
<my-component
  [param1]="params.param1"
  [param2]="params.param2"
  [param3]="params.param3"
  [param4]="params.param4"
  [param5]="params.param5"
  [param6]="params.param6"
  [param7]="params.param7"
  [param8]="params.param8"
  [param9]="params.param9"
  [param10]="params.param10"
  [param11]="params.param11"
  [param12]="params.param12"
  [param13]="params.param13"
  [param14]="params.param14"
  ... and so on ....
/>
....
9
  • 1
    your component <my-component> should have @Input params in its component.ts. Then you bind with brackets <my-component [params]="myParamObject">. Commented Jun 3, 2019 at 14:37
  • I don't know if that's possible, but I certainly don't like it. I suspect it won't be possible because Angular validates input properties - IE if you add [thing]="true" for a component that doesn't have a thing input property, Angular throws an error. Commented Jun 3, 2019 at 14:40
  • 1
    @Vlad274 it's just passing an object as an input. Of course it's possible. As for the OP, RTFM ! Commented Jun 3, 2019 at 14:40
  • angular.io/guide/template-syntax#input-and-output-properties Commented Jun 3, 2019 at 14:49
  • 1
    To the folks who are missing the point: the question isn't "can I pass an object?" the question is "can I have an object expand/flatten it's properties?" Commented Jun 3, 2019 at 14:52

2 Answers 2

1

In my opinion, It would be best to define them all in a model

You would start with the following model

params.model.ts

import {SomeOtherModel} from './some-other.model'

export interface ParamsModel {
    paramName1: string;
    paramName2?: string;
    paramName3?: number;
    paramName4: SomeOtherModel;
}

Then in your component, you can force your input to take a specific model argument

my.component.ts

import {ParamsModel} from './params.model';

@Component({..})
class MyComponent {
  @Input() params: ParamsModel;
}

app.component.html

<my-component params="paramsModel"></my-component>

app.component.ts

import {ParamsModel} from './params.model';

@Component({..})
class AppComponent implements OnInit {
    paramsModel: ParamsModel;


    ngOnInit(): void {
        this.paramsModel = <ParamsModel>{someValue: someValue};
    }
}

this way you have full code completion.

do note though! Angular does not deepwatch the contents, so changing the contents inside the Params object, will still have the same object ID in javascript, causing angular to not see the changes.

There are a few work-around for this

1: Bind every param (this is exactly what you do not want)

2: When changing contents of the model, destroy the instance and create a new instance everytime, you could do this by adding a constructor in the model and convert it olike this code

export class ParamsModel {
    paramName1: string;
    paramName2?: string;
    paramName3?: number;
    paramName4: SomeOtherModel;

    constructor(config?: ParamsModel) {
        Object.assign(this, config);
    }
}

// first init
this.paramsModel = new ParamsModel(<ParamsModel>{someValue: someValue});

// updated init
this.paramsModel = new ParamsModel(this.paramsModel);
this.paramsModel.changedValue = changedValue; // (could also use an extend function on original params model)

3: Create an observer with events and trigger update events on the other side

4: use ngDoCheck to perform your own check if the contents changed

Sign up to request clarification or add additional context in comments.

1 Comment

Excellent answer! The second half of this answer is super important and everyone should read it twice. One minor point: "you can force your input to take a specific model argument" - technically not true, this is just syntactic sugar for Typescript and at runtime it can be anything without throwing a specific error (although the logic using it will probably break). This has caused me an annoying amount of pain, so I mention it as a heads up to others
0

There is no generic directive to pass input properties in Angular. However, Angular supports binding any valid JavaScript type be it objects, arrays or primitives.

In the template

<my-component [params]="params"/>

In the class you have to use the @Input decorator to mark an object as an input. You can access it's value in any of the lifecycle hooks, some shown below. Note that params will not be set inside the constructor as view binding is performed after the class is instantiated.

class MyComponent {
  @Input()
  params: any

  constructor() { }   // <-- params not set

  ngOnChanges() { } // <-- anytime params changes

  ngOnInit() { } // <-- once when the component is mounted

}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.