Inputs not included when using directive composition API in a directive

This issue has been created since 2022-11-17.

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

core

Is this a regression?

No

Description

I have a simple standalone directive like:

@Directive({
  standalone: true,
  selector: '[appBgColor]'
})
export class BgColorDirective {
  @HostBinding('style.display') display = 'inline-block';
  @Input() @HostBinding('style.background-color') bgColor = 'yellow';
}

And another directive compose the directive above using compisition API

@Directive({
  selector: '[appMyDirective]',
  standalone: true,
  hostDirectives: [
    {
      directive: BgColorDirective,
      inputs: ['bgColor']
    }
  ]
})
export class AnotherDirective { }

Because I have include bgColor of BgColorDirective, so I ecpect bgColor should be also the input of AnotherDirective.

But when I using AnotherDirective in the hostDirectives of component, it's not working when I want to include bgColor again.

TS:

@Component({
  selector: 'app-my-comp',
  templateUrl: './my-comp.component.html',
  styleUrls: ['./my-comp.component.scss'],
  hostDirectives: [
    {
      directive: AnotherDirective,
      inputs: ['bgColor']
    }
  ]
})
export class MyCompComponent { }

HTML using MyComComponent:

<app-my-comp bgColor="green"></app-my-comp>

After compile I got following error:

image

If I just compose BgColorDirective, it works well.

A workaround is add @Input by myself

@Directive({
  selector: '[appMyDirective]',
  standalone: true,
  hostDirectives: [
    {
      directive: BgColorDirective,
      inputs: ['bgColor']
    }
  ]
})
export class AnotherDirective {
  // let bgColor input works
  @Input() bgColor = '';
}

Please provide a link to a minimal reproduction of the bug

https://github.com/wellwind/ng-inputs-not-working-in-directive-composition-api-demo

Please provide the exception or error you saw

No response

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

     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/
    

Angular CLI: 15.0.0
Node: 16.17.0
Package Manager: npm 8.15.0
OS: darwin arm64

Angular: 15.0.0
... animations, cli, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, router

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1500.0
@angular-devkit/build-angular   15.0.0
@angular-devkit/core            15.0.0
@angular-devkit/schematics      15.0.0
@schematics/angular             15.0.0
rxjs                            7.5.7
typescript                      4.8.4

Anything else?

No response

crisbeto wrote this answer on 2022-11-17

This is working as expected. The bgColor input doesn't actually exist on AnotherDirective, it is sent to the instance of BgColorDirective that is applied together with AnotherDirective. This is something that I was considering whether to support when I was implementing the new API, but I decided against it because the input doesn't really belong to the host, it belongs to the host directive which MyCompComponent doesn't have access to.

wellwind wrote this answer on 2022-11-17

It's confusing, when a host component compose a directive and it's inputs, these inputs will become the component's input. But when a host directive compose another directive and inputs, the inputs of another directive are not belongs the host directives.

Which means I can use directive composition API to extend a component input, but I shouldn't extend a directive's input?

If it is expected, in my case when I add a @Input() bgColor to AnotherDirective, it should not effect the bgColor of BgColorDirective, right?

If I do want to change the bgColor of BgColorDirective, I think the better way is inject BgColorDirective in AnotherDirective, then change it.

@Directive({
  selector: '[appMyDirective]',
  standalone: true,
  hostDirectives: [BgColorDirective]
})
export class AnotherDirective {
  @Input() set bgColor(value: string) {
    this.bgColorDirective.bgColor = this.bgColor;
  }

  get bgColor(): string {
    return this.bgColorDirective.bgColor;
  }

  constructor(private bgColorDirective: BgColorDirective) { }
}

But for now, I just add the @Input() bgColor to AnotherDirective, then the bgColor of BgColorDirective works well.

When I read the document, I expect the directive composition API are same in component and directive, but seems not.

If finally the inputs of composed inputs not belongs the host directive, I recommend the document should be more clearly, and maybe include sample code if I really want to expose the inputs.

crisbeto wrote this answer on 2022-11-17

Good point about clarifying it in the documentation. I just want to point out that there's no difference between applying host directives to a component or to a directive. Your example doesn't work, because the component is reaching for an input of a host directive that belongs to another host directive. That won't work, because the inputs array only refers to the inputs declared on the class in the directive field.

wellwind wrote this answer on 2022-11-17

Now I understand my code doesn't works because bgColor is not the @Input of AnotherDirective because of my misunderstanding from document.

But when I just add a @Input() bgColor = '' to AnotherDirective, I should belongs AnotherDirective, not belongs BgColorColor, but the style still changed.

@Directive({
  selector: '[appMyDirective]',
  standalone: true,
  hostDirectives: [
    {
      directive: BgColorDirective,
      inputs: ['bgColor']
    }
]
})
export class AnotherDirective {
  // bgColor belongs AnotherDirective, 
  // but still affect to the BgColorDirective.bgColor, why?
  @Input() bgColor = '';
}

Is it also the expected behavior? I have just update the repo.

wellwind wrote this answer on 2022-11-23

I found that I can just remove the @Input() bgColor in AnotherDirective, when I compose AnotherDirective into MyCompComponent, I can use bgColor directly without pass bgColor into inputs: []

The complete working code is:

@Directive({
  standalone: true,
  selector: '[appBgColor]'
})
export class BgColorDirective {
  @HostBinding('style.display') display = 'inline-block';
  @Input() @HostBinding('style.background-color') bgColor = 'yellow';
}

// compose BgColorDirective into AnotherDirective
@Directive({
  selector: '[appAnotherDirective]',
  standalone: true,
  hostDirectives: [BgColorDirective]
})
export class AnotherDirective { }

// compose AnotherDirective into MyCompComponent
@Component({
  selector: 'app-my-comp',
  // no need to pass inputs:[]
  // BgColorDirective.bgColor still works
  hostDirectives: [AnotherDirective],
  ...
})
export class MyCompComponent { }

// usage of MyCompComponent
@Compontnt({
  template: `<app-my-comp bgColor="blue"></app-my-comp>`,
  ...
})
export class AppComponent

I think this is the design of directive composition API?

More Details About Repo
Owner Name angular
Repo Name angular
Full Name angular/angular
Language TypeScript
Created Date 2014-09-18
Updated Date 2022-12-01
Star Count 85220
Watcher Count 3042
Fork Count 22607
Issue Count 1316

YOU MAY BE INTERESTED

Issue Title Created Date Comment Count Updated Date
[WordPress] PHP version not updated after creating new PHP 7 instance 3 2022-11-01 2022-11-23
Problem with SocketAI 4 2021-11-21 2022-10-04
Typo in mask argument of extract? 1 2022-04-22 2022-11-15
Argument "val.preload" documented but not known 1 2022-03-24 2022-09-08
config.compile_sdk_version = 31 时有没有试过功能正常吗 1 2022-03-17 2022-11-16
New or removed repositories (added repos 1, removed repos: 0) 0 2021-07-22 2022-11-12
Cannot initialize project (Failed to find Apple developer teams) 6 2021-02-28 2022-11-17
Read / Write shared workbooks 0 2022-09-14 2022-11-12
[Question] Vertex array object not supported on this platform 6 2021-08-24 2022-11-15
Sending message , reopen other tab - not confortable for user 5 2021-09-08 2022-10-31
Add option to specify operating system in MockFileSystem 4 2021-12-13 2022-11-13
[PORTER]k8s.gcr.io/kube-apiserver:v1.23.7 2 2022-08-27 2022-09-10
Crash ValueError: string too long 5 2021-03-10 2022-09-23
Server froze, all players time out, but no crash 1 2022-05-31 2022-11-16
Failing to rebuild qt libs after inital build 4 2021-05-25 2022-11-22
Insufficient error handling in save() 1 2018-10-18 2022-12-01
Profile S3.GetObject 1 2021-03-01 2022-11-22
Integrate Intercom on Mobile 3 2021-11-16 2022-09-19
How to force metadata saving in case when there is no changes in model 3 2014-12-19 2022-09-30
request_store gem might now work as expected in sidekiq environments 1 2021-07-15 2022-11-10
Update Reproducible Builds docs with pip-tools 0 2022-03-25 2022-11-11
High CPU usage 5 2022-01-18 2022-01-13
Zap Android doesn't connect - SSL handshake aborted 11 2021-06-07 2022-07-27
Static IP address for clients 3 2022-03-11 2022-11-20
付费应用无法查看详情 1 2021-06-16 2021-12-28
Deleting top-level form makes cursor jump to end of file 5 2022-07-14 2022-10-07
Add `KEY_SEED_PATH=` to replace `KEY_PATH` 5 2021-09-23 2022-11-29
Deleting namespace stuck at "Terminating" state 1 2022-02-25 2022-11-04
globalGroup documentation 6 2022-02-08 2022-11-09
How to modify headers 1 2022-09-27 2022-11-05
Unread counter exceeds navigation entry 1 2022-08-30 2022-11-10
www.acuvue.ru 1 2021-11-21 2021-12-29
Apply diff should be minimal 0 2021-02-03 2022-07-26
Issues with brew upgrade "$FORMULA" 13 2021-09-10 2022-11-19
Online editor & preview 3 2021-05-12 2022-11-12
[QUESTION] How to Stop Master when viewer click on stop viewer ? 1 2022-09-27 2022-11-17
.env scanner http scenario 1 2022-04-21 2022-10-11
Stuck on Starting Metals server 2 2022-07-30 2022-11-10
[Feature Request] Recently split tunneled apps at top of list 2 2022-06-06 2022-11-22
【论文复现营】报错No module named 'paddle.fluid' 2 2021-05-19 2022-11-12
FlexLayoutDemos: "Photo Wrapping" sample broken 2 2022-08-03 2022-11-24
multiple images (Picker) not handled 4 2022-06-27 2022-11-07
Add option to cleanup task/job executions from task-list 1 2021-02-26 2022-11-30
father从1升级到4,打包后使用svg作为jsx报错 1 2022-09-07 2022-11-22
dumi +antd 执行father build后,antd样式并没有进行打包 4 2022-10-21 2022-11-22
fix(transformer): same name md previewer conflict 3 2021-08-30 2022-02-02
Switch gcmode to archive after sync from Snap 2 2021-12-15 2022-10-02
Look at me 0 2021-12-15 2022-01-10
Android, for overlayScreen, the statusBar does not mean the actual statusBar area. 0 2022-06-16 2022-11-19
zIndex not working correctly in RN 0.69.0-rc.1 3 2022-05-18 2022-11-20