2

I have a model like this:

export class Person{
name: string;
}

Which is used in my component as follow:

export class TestComponent {
  @Input() person : Person;
  constructor() {
  }
}

I want to set person name from html as below:

<app-test [person.name]="'Jack'"</app-test>

When I do so, angular gets angry that : "Can't bind to 'person.name' since it isn't a known property of 'app-test'."

well, he is right somehow, person is property of my component, not person.name

On the other hand, we don't get any error in following sample:

<span [style.color]="'red'">This span is red.</span>

Am I moving against rules??

Or is style property internally different? How?

Thanks

Efe
  • 800
  • 10
  • 32

4 Answers4

2

You can't bind to person.name, because the Input() decorator is bound to the person in the child component (test), not any specific property like person.name

You have to send an object instead to fix this issue.

See this example.

Akhil
  • 1,403
  • 7
  • 19
1

You're correct [ngStyle] is different and [style] is different, You need

<app-test [person]="{name:'Jack'}"</app-test>

Other options are

<app-test [person]="person"</app-test>

And in your main.app

this.person.name='Jack'

Remember that, if you use an object in a input any change of a property of the object in parent or in child change the "properties" of the object

Eliseo
  • 50,109
  • 4
  • 29
  • 67
1

Its because the Input property you are try to pass to the Test Component is "person.name", which isn't defined on your Test component. Your Test component expects something named "person". Secondly you are passing a string and not a Person type you defined on the Test Component. Change the Test component to accept a string or pass the Person and reference the name property in the Test component. And make sure the names match.

Here is two ways you can think about passing the person's name to the other component

<app-test [name]="person.name" [person]="person"></app-test>

Then in the Test Component:

 export class TestComponent  {
  @Input() name: string;
  @Input() person : Person;
}

Notice how the names and types match? My advice would be dont pass more than you need. If you just need the Person name passed, just have your component accept a string. No need to pass the whole object.

TestComponent Template:

<h1>Person: {{person.name}}</h1>
<h1>Name: {{name}}</h1>

I am going to add a link to the Angular Documentation as I think its a pretty important part of Angular you might want to review. The Heroes tutorial they have is really good.

https://angular.io/api/core/Input

devBrandon
  • 131
  • 4
  • you are right. But suppose that my test component has got 10 other children components, which have their own properties with default values. For example two input components having properties like: required, placeholder, type(password) , autocomplete and etc ....If I define properties like input1placeholder, input2placeholder and so on in my test component, it will get really messy. The reason I insist on sth like [style.color] is tidiness, less code and avoid setting defaults. suppose that i'm writing a component framework for myself and want less limits and less code. – Efe Feb 02 '20 at 15:43
  • Well [name]="person.name" is what you are trying to do. Child components would just pass the string. They no longer need the reference since the parent component has it. Another option I would look into is Observable Data Services. Its a pattern you can use in Angular. I dont recommend using input properties if you plan to pass objects or values down a long component tree like 10 children. Using DI, you can simply use a service to grab and update the values. Its much cleaner. https://blog.angular-university.io/how-to-build-angular2-apps-using-rxjs-observable-data-services-pitfalls-to-avoid/ – devBrandon Feb 02 '20 at 16:05
0

That is because style is directive with multiple selectors and inputs.

You can achieve this by having some directive that would do the job. I guess you could make something similar to this: (Not tested)

import { Directive, ViewContainerRef, ChangeDetectorRef, OnInit } from '@angular/core';

const selector = `
  [name],
  [name.foo],
  [name.bar]
`;

export const inputs = [
  'name',
  'name.foo',
  'name.bar'
];



@Directive({
  selector,
  inputs
})
export class HelloDirective implements OnInit {
  constructor(
    private viewContainerRef: ViewContainerRef
  ) {
  }

  ngOnInit(): void {
    const getSetKeys = Object.keys(this).filter(item => inputs.indexOf(item) > -1);
    const getValues = getSetKeys.map(item => this[item]);
    const component = this.viewContainerRef['_view'].component;
    getSetKeys.forEach((item, index) => {
      const splitKey = item.split('.');
      component[splitKey[0]][splitKey[1]] = getValues[index];
    })
  }
}

In this case, you could use your directive as <some-component [name.foo]="'Some foo'" [name.bar]="'Some bar'"></some-component> and this would inject to your "name" object in component.

I would not recommend this way but it is possible to implement it.

Nikola Stekovic
  • 635
  • 3
  • 13