Angular2 Can't bind to DIRECTIVE since it isn't a known property of element

107,375

Solution 1

When wrapping a property in brackets [] you're trying to bind to it. So you have to declare it as an @Input.

import { Directive, Input } from '@angular/core';

@Directive({
 selector: '[appContenteditableModel]'
})
export class ContenteditableModelDirective {

  @Input()
  appContenteditableModel: string;

  constructor() { }

}

The important part is, that the member (appContenteditableModel) needs to be named as the property on the DOM node (and, in this case, the directive selector).

Solution 2

If you're using a shared module to define the directive make sure it is both declared and exported by the module it's defined in.

// this is the SHARED module, where you're defining directives to use elsewhere
@NgModule({
  imports: [
    CommonModule
  ],
  declarations: [NgIfEmptyDirective, SmartImageDirective],
  exports: [NgIfEmptyDirective, SmartImageDirective]
})

Solution 3

I was facing the same issue with a directive declared in a shared module. I'm using this directive to disable a form control.

import { Directive, Input } from '@angular/core';
import { NgControl } from '@angular/forms';

@Directive({
  selector: '[appDisableControl]'
})
export class DisableControlDirective {

  constructor(private ngControl: NgControl) { }

  @Input('disableControl') set disableControl( condition: boolean) {
    const action = condition ? 'disable' : 'enable';
    this.ngControl.control[action]();
  }

}

To work it properly, declare and export the directive in shared module (or any module you are using).

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { DisableControlDirective } from './directives/disable-control/disable-control.directive';

@NgModule({
  declarations: [
    DisableControlDirective
  ],
  imports: [
    CommonModule
  ],
  exports: [DisableControlDirective],
  providers: [],
  bootstrap: []
})
export class SharedModule { }

Now we can use this directive in any module where we are importing SharedModule.

Now to disable a reactive form's control, we can use it like this:

<input type="text" class="form-control" name="userName" formControlName="userName" appDisableControl [disableControl]="disable" />

Mistake I was doing it, I was using only selector (appDisableControl) and passing the disable param to this. but to pass an input param, we have to use it like above.

Solution 4

For me the fix was moving the directive references from root app.module.ts (the lines for import, declarations, and/or exports) to the more specific module src/subapp/subapp.module.ts my component belonged to.

Solution 5

In sum, because your directive looks like an anchor directive, remove the brackets and it would work.

Actually, I have not found the corresponding sections related to when the brackets should be removed or not, where only one mention I've found is located at the section on dynamic components:

Apply that to <ng-template> without the square brackets

, which is however not perfectly covered in the Attribute Directives document.

Individually, I agree with you and was thinking that [appContenteditableModel] should be equal to appContenteditableModel and angular template parser might work around whether there is @input() data binding or not automatically, as well. But they seem exactly not processed equally under the hood, even in current Angular Version of 7.

Share:
107,375

Related videos on Youtube

Tomas Javurek
Author by

Tomas Javurek

Updated on July 08, 2022

Comments

  • Tomas Javurek
    Tomas Javurek almost 2 years

    I generated new @Directive by Angular CLI, it was imported it to my app.module.ts

    import { ContenteditableModelDirective } from './directives/contenteditable-model.directive';
    
    import { ChatWindowComponent } from './chat-window/chat-window.component';
    
    @NgModule({
      declarations: [
        AppComponent,
        ContenteditableModelDirective,
        ChatWindowComponent,
        ...
      ],
      imports: [
        ...
      ],
      ...
    })
    

    and I try to use in my component (ChatWindowComponent)

    <p [appContenteditableModel] >
        Write message
    </p>
    

    even if within directive is only Angular CLI generated code:

     import { Directive } from '@angular/core';
    
     @Directive({
       selector: '[appContenteditableModel]'
     })
     export class ContenteditableModelDirective {
    
     constructor() { }
    
     }
    

    I got the error:

    zone.js:388 Unhandled Promise rejection: Template parse errors: Can't bind to 'appContenteditableModel' since it isn't a known property of 'p'.

    I tried almost every possible changes, following this angular docs everything should work but it does not.

    Any help?

    • Tomas Javurek
      Tomas Javurek over 7 years
      The result I need is [(appContenteditableModel)]="draftMessage.text" at the end...
    • Sanket
      Sanket over 7 years
      Then try like this <p [appContenteditableModel]="draftMessage.text"></p>
    • Tomas Javurek
      Tomas Javurek over 7 years
      It works without brackets appContenteditableModel="draftMessage.text" and also (appContenteditableMode)l="draftMessage.text" resolve the promise rejection but it also seems to do not pass the variable
  • Tomas Javurek
    Tomas Javurek over 7 years
    I have input @Input ('appContenteditableModel') model : any; and also output @Output ('appContenteditableModel') update : EventEmitter<any> = new EventEmitter(); in my directive. It seems that the model works well but the emitter called by this.update.emit(value) does not change the value in parent component. What I do wrong? [(appContenteditableModel)]="draftMessage.text"
  • Tomas Javurek
    Tomas Javurek over 7 years
    Actually I try to "simulate" [(ngModel)] outside of <input> element
  • naeramarth7
    naeramarth7 over 7 years
    @Output is for emitting events only. If you want to keep the value in sync with the parent's, you may consider adding the @HostBinding annotation.
  • Tomas Javurek
    Tomas Javurek over 7 years
    If I undersand well @HostBinding will help to keep the value in sync within the html element, am I right? This element I need to be edited by user contenteditable="true" so that input I need to keep in sync with the variable in the same component.
  • Ohad Sadan
    Ohad Sadan over 5 years
    and what if they are not in the same module?
  • aruno
    aruno over 5 years
    @OhadSadan I'm not sure exactly what you mean. This is an example of when you don't have them in the same module, and I'm just saying make sure to declare AND export directives if you're creating them in a shared module (which you must then then import them into a different module).
  • aruno
    aruno over 5 years
    In your 'main' module you only need to import the 'directives module' and then all your components can see them.
  • Sami
    Sami almost 4 years
    This is a minute detail but often missed. Thank you !
  • Luizgrs
    Luizgrs about 3 years
    if using strict mode and your directive requires no value, you need to do appContenteditableModel = undefined;
  • Kurtis Jungersen
    Kurtis Jungersen about 3 years
    Ah thank you. In case someone else runs into it, my specific issue was that the directive selector (e.g. selector: 'fooDirective') didn't match the @Input() property name (e.g. @Input() barDirective). I renamed the selector, but didn't think about the property too.
  • noririco
    noririco almost 3 years
    WTF ??????? any explanation for that ?? and it worked after 2 hours !
  • 27px
    27px over 2 years
    String did not work for me as I was using no value for the attribute, used any and it worked, I know i have to give a value for attribute but, that was my case.
  • Experimenter
    Experimenter over 2 years
    "The important part is, that the member (appContenteditableModel) needs to be named as the property on the DOM node (and, in this case, the directive selector)" - holy cow, didn't find it in the docs. Thanks!