Kamran Pervaiz Kamran Pervaiz - 4 months ago 214
Javascript Question

Angular2 : Type 'Subscription' is not assignable to type

I have created a very small application to fetch countries from a json file and bind it to dropdown.


countries.json


export class Country {
id: number;
name: string;
}



factory.service.ts


import { Injectable } from '@angular/core';
import { Http, Response} from '@angular/http';
import { Observable } from 'rxjs/Observable';

import { Country } from './shared/country';

@Injectable()
export class FactoryService {
private countryUrl = "app/data/countries.json";

constructor(private http: Http) {

}

getCountry(): Observable<any> {
return this.http.get(this.countryUrl)
.map(this.extractData)
.do(data => console.log("get Countries from json: " + JSON.stringify(data)))
.catch(this.handleError);
}

private extractData(response: Response) {
let body = response.json();
return body || {};
}

private handleError(error: Response) {
console.log(error);
return Observable.throw(error.json().error || "500 internal server error");
}
}



factory-form.component.ts


import { Component, OnInit } from '@angular/core';
import { NgForm } from '@angular/forms';

import { Factory } from './factory';
import { Country } from './shared/country';
import { FactoryService } from './factory.service';

@Component({
moduleId: module.id,
selector: 'factory-form',
templateUrl: './factory-form.component.html',
styleUrls: ['./factory-form.component.css'],
providers: [FactoryService]
})
export class FactoryFormComponent implements OnInit{

private model: Factory;
countries: Country[];
factoryStatuses;
productTypes;
risks;
private errorMessage: string;
private submitted = false;

constructor(private factoryService: FactoryService) {

}

ngOnInit(): void {
this.countries = this.factoryService.getCountry()
.subscribe(countries => this.countries = countries,
error => this.errorMessage = error);
}

onSubmit(): void {
this.submitted = true;
}}



factory-form.component.html snippet


<div class="col-lg-3">
<select class="form-control" name="Country">
<option *ngFor="let country of countries" [value]="country.id">{{country.name}}</option>
</select>
</div>


I am getting runtime error as below:


Error: Typescript found the following errors:

C:/Projects/ethical_resourcing/src/Ethos.Client/tmp/broccoli_type_script_compiler-input_base_path-kviWq7F3.tmp/0/src/app/factory/factory-form.component.ts
(30, 9): Type 'Subscription' is not assignable to type 'Country[]'.

Property 'length' is missing in type 'Subscription'.

C:/Projects/ethical_resourcing/src/Ethos.Client/tmp/broccoli_type_script_compiler-input_base_path-kviWq7F3.tmp/0/src/app/factory/shared/country.ts
(2, 15): ';' expected.
at BroccoliTypeScriptCompiler._doIncrementalBuild (C:\Projects\ethical_resourcing\src\Ethos.Client\node_modules\angular-cli\lib\broccoli\broccoli-typescript.js:120:19)
at BroccoliTypeScriptCompiler.build (C:\Projects\ethical_resourcing\src\Ethos.Client\node_modules\angular-cli\lib\broccoli\broccoli-typescript.js:43:10)
at C:\Projects\ethical_resourcing\src\Ethos.Client\node_modules\angular-cli\node_modules\broccoli-caching-writer\index.js:152:21
at lib$rsvp$$internal$$tryCatch (C:\Projects\ethical_resourcing\src\Ethos.Client\node_modules\angular-cli\node_modules\rsvp\dist\rsvp.js:1036:16)
at lib$rsvp$$internal$$invokeCallback (C:\Projects\ethical_resourcing\src\Ethos.Client\node_modules\angular-cli\node_modules\rsvp\dist\rsvp.js:1048:17)
at lib$rsvp$$internal$$publish (C:\Projects\ethical_resourcing\src\Ethos.Client\node_modules\angular-cli\node_modules\rsvp\dist\rsvp.js:1019:11)
at lib$rsvp$asap$$flush (C:\Projects\ethical_resourcing\src\Ethos.Client\node_modules\angular-cli\node_modules\rsvp\dist\rsvp.js:1198:9)
at nextTickCallbackWith0Args (node.js:420:9)
at process._tickCallback (node.js:349:13)


Now If I change the Type of Observable and countries to any then i get below error


ORIGINAL EXCEPTION: Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables
such as Arrays.


Answer

Yes, the type must be an Observable and the you need to use async pipe to make ngFor happy:

*ngFor="let country of countries | async"

Or another option is to keep Country[] type but then subscribe and assign countries to array:

this.factoryService.getCountry() // note, removed this.countries = 
    .subscribe(
        countries => this.countries = countries,
        error => this.errorMessage = error
    );
Comments