48

I have an angular site that contains a component inside another component. I'm using routing and lazy loading the outer component (ComponentA). The inner component (ComponentB) depends on a 3rd party directive.

Here's the proof of concept (open the console to see the error).

I'm getting an error when using the 3rd party directive inside ComponentB. This isn't an error with the directive itself but an error with how I've structured my code.

<tree-root [nodes]="nodes"></tree-root>

Can't bind to 'nodes' since it isn't a known property of 'tree-root'

I can solve this issue by importing the 3rd party module in the ComponentA but since ComponentA doesn't depend on this module, it feels wrong to do so.

The Plunker I have created is a very small portion of my app, designed to isolate the issue. It's to demonstrate a concept, not to make sense as an app. What I'm trying to achieve is to create a component that can be added to any of my pages. This component could be a widget or something, added to one or more parent pages. It is self contained.

My limited knowledge in Angular is starting showing here. Since components are supposed to allow us to do component based development breaking our application into smaller parts, I don't understand why ComponentA needs to know what dependencies ComponentB has in order to use it in its view.

I'll also demonstrate that this is only an issue because I have a 3rd party directive included in ComponentB. If I remove the directive, all works. For example if I did something like this instead:

<ul>
    <li *ngFor="let node of nodes">
        {{ node.name }}
    </li>
</ul>

the app runs fine with no errors.

What have I done wrong and what should I be doing differently? If the solution is to add an import to ComponentA, I will accept that as an answer given a good explanation is provided (such as why *ngFor works but the tree-root directive doesn't).

Michael
  • 8,362
  • 6
  • 61
  • 88
Lydon
  • 2,942
  • 4
  • 25
  • 29
  • 4
    define the property for the component – Roman C Apr 20 '17 at 15:09
  • You could add the 3rd party's module to a parent's module. And then you would have it, no? – SrAxi Apr 20 '17 at 15:10
  • Is there something that prevents you from importing third-party module in feature b module? – Estus Flask Apr 20 '17 at 15:12
  • " ComponentA doesn't depend on this module" --> it does, moduleA declares componentB. Actually, your whole code structure is veeery weird. For example why declaring ComponentB in ModuleA and ModuleB ? That does not make sense. – n00dl3 Apr 20 '17 at 15:32
  • Thanks for the comments. I've edited my post to include some more detail. – Lydon Apr 21 '17 at 10:48

5 Answers5

64

I went back to the start and realised what I missed:

In feature-b.module.ts I should have exported the component:

exports: [
    FeatureBComponent
]

It was also necessary for me to import FeatureBModule rather than FeatureBComponent.

import { FeatureBComponent } from '../feature-b/feature-b.component';

import { FeatureBModule } from '../feature-b/feature-b.module';
Lydon
  • 2,942
  • 4
  • 25
  • 29
3

I was able to get the application running by removing FeatureBModule entirely. Then the FeatureAModule is correct as it needs to then delcare FeatureBComponent.

The FeatureAModule then looks like this:

import { NgModule }       from '@angular/core';
import { CommonModule }   from '@angular/common';

import { FeatureAComponent }           from './feature-a.component';
import { FeatureARoutingModule }       from './feature-a-routing.module';
import { TreeModule }                  from 'angular-tree-component';

import { FeatureBComponent } from '../feature-b/feature-b.component';

@NgModule({
  imports: [
    CommonModule,
    FeatureARoutingModule,
    TreeModule
  ],
  declarations: [
    FeatureAComponent,
    FeatureBComponent
  ]
})
export class FeatureAModule {}

I updated the plunker here: https://plnkr.co/edit/mkGH5uG1FGsWWpWbS1Zw?p=preview

DeborahK
  • 57,520
  • 12
  • 104
  • 129
  • Thanks for the suggestion. I have updated my post to include more information and reasons as to why I'm confused that things aren't working as expected. I'm after reasons as to why parent components need to import all the modules of child components. – Lydon Apr 21 '17 at 10:55
  • There are two `import` in Angular. One that is the import statement required for ES 2015 modules. And one that is the imports array required for Angular modules. When you say `import all modules` above, which are you referring to? – DeborahK Apr 21 '17 at 19:12
  • Angular modules, as in `import { TreeModule } from 'angular-tree-component';` – Lydon Apr 22 '17 at 06:45
3

You can get this error on directives where you are binding to the attribute that attaches the directive itself, but has the corresponding Input decorated incorrectly.

@Directive({ selector: '[myDirective]' })
export class MyDirective {
    @Input('mydirectiveSpelledDifferently') data: any;
}

The input in the example has "mydirectiveSpelledDifferently" but it should be matching the selector (i.e. "myDirective").

You'll know this is the issue in your case when it works this way:

<div myDirective>

But fails this way:

<div [myDirective]="something">

The working case is correctly finding the selector you chose for your directive. The failing case is looking for the @Input() decoration and failing to because it doesn't exist as @Input('myDirective') on your directive.

Kevin Beal
  • 10,500
  • 12
  • 66
  • 92
1

I just experienced the same, and the problem was that I had the wrong case ('a' vs 'A') for the component.

In my parent component's template, I had:

<mychild-component></mychild-component>

Instead of

<myChild-component></myChild-component>
MMalke
  • 1,857
  • 1
  • 24
  • 35
0

Had a similar scenario to MMalke. By default Angular adds the prefix app- to the selector name of a component when the component is generated via the command line interface. I should have written <app-my-component><app-my-component> but I wrote <my-component><my-component> instead. FYI, check to make sure the component's selector name matches what's in your HTML.

HelloWorldPeace
  • 1,315
  • 13
  • 12