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

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?

117137 次浏览

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).

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]
})

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.

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.

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.