Jane Wayne Jane Wayne - 21 days ago 21
TypeScript Question

What happened to the directives property in the Component decorator for Angular 2?

I am using the Angular CLI v1.0.0-beta.19-3 to scaffold/build an Angular 2 website. I generate my component as follows

ng g component file-upload
.

I noticed that the generated component
file-upload.component.ts
looks like the following.

@Component({
selector: 'file-upload',
templateUrl: './file-upload.component.html',
styleUrls: ['./file-upload.component.css']
})
export class FileUploadComponent implements OnInit {
...
}


I need to use this component, ng2-file-upload, inside my own component. So I try to add the
directives
property on the
@Component
decorator, however, I get an error in VS Code.

@Component({
selector: 'file-upload',
templateUrl: './file-upload.component.html',
styleUrls: ['./file-upload.component.css'],
directives: [ FILE_UPLOAD_DIRECTIVES ]
})
export class FileUploadComponent implements OnInit {
...
}



[ts] Argument of type '{ selector: string; templateUrl: string; styleUrls: string[]; directives: undefined[]; }' is not assignable to parameter of type 'Component'.
Object literal may only specify known properties, and 'directives' does not exist in type 'Component'.


And my imports look like the following.

import { Component, OnInit, Input } from '@angular/core';
import { NgClass, NgStyle } from '@angular/common';
import { FileSelectDirective, FileDropDirective, FileUploadModule, FileUploader, FileUploaderOptions } from 'ng2-file-upload';


When I run the app
ng serve
, I see the following error reported in the console.


zone.js:388 Unhandled Promise rejection: Template parse errors:
Can't bind to 'uploader' since it isn't a known property of 'div'. ("
[ngClass]="{'nv-file-over': hasBaseDropZoneOver}"
(fileOver)="fileOverBase($event)"
[ERROR ->][uploader]="uploader"
class="well my-drop-zone">


Here's my dependencies in
package.json
.

{
"name": "app-www",
"version": "0.0.0",
"license": "MIT",
"angular-cli": {},
"scripts": {
"start": "ng serve",
"lint": "tslint \"src/**/*.ts\"",
"test": "ng test",
"pree2e": "webdriver-manager update",
"e2e": "protractor"
},
"private": true,
"dependencies": {
"@angular/common": "~2.1.1",
"@angular/compiler": "~2.1.1",
"@angular/core": "~2.1.1",
"@angular/forms": "~2.1.1",
"@angular/http": "~2.1.1",
"@angular/platform-browser": "~2.1.1",
"@angular/platform-browser-dynamic": "~2.1.1",
"@angular/router": "~3.1.1",
"angular2-cookie": "^1.2.5",
"bootstrap": "^3.3.7",
"core-js": "^2.4.1",
"jquery": "^3.1.1",
"lodash": "^4.16.6",
"ng2-bootstrap": "^1.1.16",
"ng2-file-upload": "^1.1.2",
"rxjs": "5.0.0-beta.12",
"ts-helpers": "^1.1.1",
"zone.js": "^0.6.25"
},
"devDependencies": {
"@types/jasmine": "^2.2.30",
"@types/lodash": "^4.14.38",
"@types/node": "^6.0.45",
"angular-cli": "1.0.0-beta.19-3",
"codelyzer": "1.0.0-beta.1",
"jasmine-core": "2.4.1",
"jasmine-spec-reporter": "2.5.0",
"karma": "1.2.0",
"karma-chrome-launcher": "^2.0.0",
"karma-cli": "^1.0.1",
"karma-jasmine": "^1.0.2",
"karma-remap-istanbul": "^0.2.1",
"protractor": "4.0.9",
"ts-node": "1.2.1",
"tslint": "3.13.0",
"typescript": "~2.0.3",
"webdriver-manager": "10.2.5"
}
}


The doc says to use this import statement.

import { FILE_UPLOAD_DIRECTIVES } from 'ng2-file-upload';


But in VS Code, it says the following.


[ts] Module '"/Users/jwayne/git/app-www/node_modules/ng2-file-upload/ng2-file-upload"' has no exported member 'FILE_UPLOAD_DIRECTIVES'.


What's even more interesting, is that I created my own login component like the following.

@Component({
selector: 'login-component',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
...
}


In order to use this in another component, I don't even have to reference it as a directive or anything like that anymore. For example, I have a default component that uses this login component. The HTML looks like the following.

<login-component></login-component>


And the code looks like the following (note no directive property for the component decorator).

@Component({
selector: 'app-default',
templateUrl: './default.component.html',
styleUrls: ['./default.component.css']
})
export class DefaultComponent implements OnInit, OnDestroy {
...
}


Although the
app.module.ts
file is modified now.

import { LoginComponent } from './login/login.component';
import { DefaultComponent } from './default/default.component';
@NgModule({
declarations: [
AppComponent,
LoginComponent,
DefaultComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
RouterModule.forRoot([
{ path: 'default', component: DefaultComponent },
{ path: 'main', component: MainComponent, canActivate: [ UserAuthGuard ] },
{ path: '', redirectTo: '/default', pathMatch: 'full' },
{ path: '**', redirectTo: '/default' }
])
],
providers: [
UserAuthGuard
],
bootstrap: [AppComponent]
})
export class AppModule { }


So, there's a lot of stuff going on, but to start off, what happened to the directives property for the component decorator?

Answer

The key to solving this problem was importing the directives in app.module.ts and then declaring them with @NgModule. Here's a snippet of app.module.ts.

import { FileSelectDirective, FileDropDirective} from 'ng2-file-upload';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent,
    FileSelectDirective, FileDropDirective
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    RouterModule.forRoot([
      { path: 'default', component: DefaultComponent },
      { path: 'main', component: MainComponent, canActivate: [ UserAuthGuard ] },
      { path: '', redirectTo: '/default', pathMatch: 'full' },
      { path: '**', redirectTo: '/default' }
    ])
  ],
  providers: [ ... ],
  bootstrap: [AppComponent]
})
export class AppModule { }

In my component, I still had to import the directives. Here's another snippet.

import { Component, OnInit, Input } from '@angular/core';
import { NgClass, NgStyle } from '@angular/common';
import { 
  FileSelectDirective,
  FileDropDirective, 
  FileUploader, 
  FileUploaderOptions 
} from 'ng2-file-upload';
import * as _ from 'lodash';
import { LoginService } from '../service/login.service';

@Component({
  selector: 'file-upload',
  templateUrl: './file-upload.component.html',
  styleUrls: ['./file-upload.component.css']
})
export class FileUploadComponent implements OnInit {
  //body omitted
}