http.get() не работает как Promise и Observable

У меня есть два простых сервиса. Оба должны возвращать результат REST API.

Первоначально я начал с Обещания. Я не мог понять, почему toPromise() не был найден (та же проблема, что и здесь).

Поэтому я попытался переключиться на Observable. Проблема аналогичная: map() не найден.

Позвольте мне дать вам много кода...

// package.json
{
  "name": "mvc-angular-2",
  "version": "0.0.1",
  "license": "ISC",
  "scripts": {
    "start": "tsc && concurrently \"npm run tsc:w\" ",
    "tsc": "tsc",
    "tsc:w": "tsc -w",
    "typings": "typings"
  },
  "dependencies": {
    "@angular/common": "2.0.0-rc.4",
    "@angular/compiler": "2.0.0-rc.4",
    "@angular/core": "2.0.0-rc.4",
    "@angular/forms": "0.2.0",
    "@angular/http": "2.0.0-rc.4",
    "@angular/platform-browser": "2.0.0-rc.4",
    "@angular/platform-browser-dynamic": "2.0.0-rc.4",
    "@angular/router": "3.0.0-beta.1",
    "@angular/router-deprecated": "2.0.0-rc.2",
    "@angular/upgrade": "2.0.0-rc.4",
    "bootstrap": "^3.3.6",
    "core-js": "^2.4.0",
    "primeng": "^1.0.0-beta.9",
    "primeui": "^4.1.12",
    "reflect-metadata": "^0.1.3",
    "rxjs": "5.0.0-beta.6",
    "systemjs": "0.19.27",
    "zone.js": "^0.6.12"
  },
  "devDependencies": {
    "gulp": "^3.9.1",
    "q": "^1.4.1",
    "rimraf": "^2.5.3",
    "typescript": "^1.8.10",
    "typings": "^1.0.4"
  }
}

// typings.json
{
  "globalDependencies": {
    "core-js": "registry:dt/core-js#0.0.0+20160602141332",
    "jasmine": "registry:dt/jasmine#2.2.0+20160621224255",
    "node": "registry:dt/node#6.0.0+20160621231320"
  }
}

// tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": false,
    "outDir": "../wwwroot/app/"
  },
  "compileOnSave": true,
  "exclude": [
    "node_modules",
    "wwwroot"
  ]
}

// systemjs.config.js
/**
 * System configuration for Angular 2 samples
 * Adjust as necessary for your application needs.
 */
(function (global) {
    // map tells the System loader where to look for things
    var map = {
        'app': 'app', // 'dist',
        '@angular': 'lib/@angular',
        'rxjs': 'lib/rxjs',
        'primeng': 'lib/primeng'
    };
    // packages tells the System loader how to load when no filename and/or no extension
    var packages = {
        'app': { main: 'main.js', defaultExtension: 'js' },
        'rxjs': { defaultExtension: 'js' },
        'primeng': { defaultExtension: 'js' }
    };
    var ngPackageNames = [
      'common',
      'compiler',
      'core',
      'forms',
      'http',
      'platform-browser',
      'platform-browser-dynamic',
      'router',
      'router-deprecated',
      'upgrade',
    ];
    // Individual files (~300 requests):
    function packIndex(pkgName) {
        packages['@angular/' + pkgName] = { main: 'index.js', defaultExtension: 'js' };
    }
    // Bundled (~40 requests):
    function packUmd(pkgName) {
        packages['@angular/' + pkgName] = { main: '/bundles/' + pkgName + '.umd.js', defaultExtension: 'js' };
    }
    // Most environments should use UMD; some (Karma) need the individual index files
    var setPackageConfig = System.packageWithIndex ? packIndex : packUmd;
    // Add package entries for angular packages
    ngPackageNames.forEach(setPackageConfig);
    var config = {
        map: map,
        packages: packages
    };
    System.config(config);
})(this);

// main.ts
/// <reference path="../typings/globals/core-js/index.d.ts" />

import { bootstrap }      from '@angular/platform-browser-dynamic';
import { HTTP_PROVIDERS } from '@angular/http';

import { AppComponent }         from './components/app.component';
import { appRouterProviders }   from './app.routes';

bootstrap(AppComponent, [
    appRouterProviders,
    HTTP_PROVIDERS,
]);

// app.component.ts
import { Component } from '@angular/core';
import { ROUTER_DIRECTIVES } from '@angular/router';

import './rxjs-operators';

import { PrivilegeService } from '../services/privilege.service';
import { UserService }  from '../services/user.service';

@Component({
    selector: 'my-app',
    templateUrl: "app/app.component.html",
    styleUrls: ["app/app.component.css"],
    directives: [ROUTER_DIRECTIVES],
    providers: [
        PrivilegeService,
        UserService
    ]
})
export class AppComponent {
    title = 'NG App 3.0 Future beta 0.001';
}

// users.components.ts
import { Component, OnInit } from '@angular/core';
import { Router }            from '@angular/router';

import { DataTable, Column } from '../../node_modules/primeng/primeng';

import { User }              from '../models/user';
import { UserService }       from '../services/user.service';

@Component({
    selector: 'my-users',
    templateUrl: 'app/users.component.html',
    providers: [UserService],
    directives: [DataTable, Column]
})
export class UsersComponent implements OnInit {
    users: User[];

    constructor(
        private router: Router,
        private userService: UserService) { }

    getUsers() {
        this.userService
            .getUsers()
            .then(users => this.users = users)
            .catch(error => console.log(error));
    }

    ngOnInit() {
        this.getUsers();
    }

    gotoUserDetails(event) {
        let link = ['/user', event.data.id];
        this.router.navigate(link);
    }
}

// user.service.ts (Promise)
import { Injectable }    from '@angular/core';
import { Headers, Http } from '@angular/http';
import '../rxjs-operators';

import { User } from '../models/user';

@Injectable()
export class UserService {
    private usersUrl = 'http://localhost:25153/users';

    constructor(private http: Http) { }

    getUsers(): Promise<User[]> {
        return this.http.get(this.usersUrl)
            // .toPromise() cannot be found
            .toPromise()
            .then(response => response.json())
            .catch(this.handleError);
    }

    getUser(id: string) {
        return this.getUsers()
            .then(users => users.find(user => user.id === id));
    }

    private handleError(error: any) {
        console.error('An error occurred', error);
        return Promise.reject(error.message || error);
    }
}

// privilege.service.ts (Observable)
import { Injectable }    from '@angular/core';
import { Headers, Http, Response } from '@angular/http';
import { Observable }     from 'rxjs/Observable';
import '../rxjs-operators';
import { Privilege } from '../models/privilege';

@Injectable()
export class PrivilegeService {
    private privilegesUrl = 'http://localhost:25153/privileges';

    constructor(private http: Http) { }

    getPrivileges(): Observable<Privilege[]> {
        return this.http.get(this.privilegesUrl)
            // .map() cannot be found, only .forEach(), .lift() and .subscribe() seem to be available
            .map(this.extractData)
            .catch(this.handleError);
    }

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

    getPrivilege(id: string) {
        return null;
        //return this.getPrivileges()
        //    .then(privileges => privileges.find(privilege => privilege.id === id));
    }

    private handleError(error: any) {
        console.error('An error occurred', error);
        return Promise.reject(error.message || error);
    }
}

Последний, но тем не менее важный:

  • npm -v: 3.10.3
  • node -v: v6.3.0
  • Visual Studio 2015, обновление 3

Я уже удалил папку node_modules и пересобрал. Немного не изменился.

Кроме того, я пытался создать его вне Visual Studio, но я просто понятия не имею, как это сделать. Когда я ввожу что-то вроде npm run tsc --rootDir .\app, я получаю следующий вывод:

> [email protected] tsc C:\Users\Carsten Franke\Documents\Visual Studio 2015\Projects\MVCAngular2\src\Client
> tsc ".\app"

error TS6053: File 'app.ts' not found.

npm ERR! Windows_NT 6.3.9600
npm ERR! argv "C:\\Program Files\\nodejs\\node.exe" "C:\\Program Files\\nodejs\\node_modules\\npm\\bin\\npm-cli.js" "run" "tsc" "--rootDir" ".\\app"
npm ERR! node v6.3.0
npm ERR! npm  v3.10.3
npm ERR! code ELIFECYCLE
npm ERR! [email protected] tsc: `tsc ".\app"`
npm ERR! Exit status 2
npm ERR!
npm ERR! Failed at the [email protected] tsc script 'tsc ".\app"'.
npm ERR! Make sure you have the latest version of node.js and npm installed.
npm ERR! If you do, this is most likely a problem with the mvc-angular-2 package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR!     tsc ".\app"
npm ERR! You can get information on how to open an issue for this project with:
npm ERR!     npm bugs mvc-angular-2
npm ERR! Or if that isn't available, you can get their info via:
npm ERR!     npm owner ls mvc-angular-2
npm ERR! There is likely additional logging output above.

npm ERR! Please include the following file with any support request:
npm ERR!     C:\Users\Carsten Franke\Documents\Visual Studio 2015\Projects\MVCAngular2\src\Client\npm-debug.log

Он совершенно прав, файла app.ts нет. Вот почему я сказал ему транспилировать папку. Кстати, это структура файлов и папок (за исключением файлов node_modules, файлов .map и некоторых специфичных для Visual Studio материалов):

D:\projects\MVCAngular2\src\Client\
...\app\app.routes.ts
...\app\components
...\app\components\app.component.html
...\app\components\app.component.ts
...\app\components\user-detail.component.html
...\app\components\user-detail.component.ts
...\app\components\users.component.html
...\app\components\users.component.ts
...\app\main.ts
...\app\models
...\app\models\privilege-group.ts
...\app\models\privilege.ts
...\app\models\user.ts
...\app\rxjs-operators.ts
...\app\services
...\app\services\privilege.service.ts
...\app\services\user.service.ts
...\app\tsconfig.json
...\gulpfile.js
...\node_modules\[14.536 node_modules files]
...\package.json
...\Program.cs
...\Startup.cs
...\typings
...\typings.json
...\typings\globals
...\typings\globals\core-js
...\typings\globals\core-js\index.d.ts
...\typings\globals\core-js\typings.json
...\typings\globals\jasmine
...\typings\globals\jasmine\index.d.ts
...\typings\globals\jasmine\typings.json
...\typings\globals\node
...\typings\globals\node\index.d.ts
...\typings\globals\node\typings.json
...\typings\index.d.ts
...\web.config
...\wwwroot
...\wwwroot\app
...\wwwroot\app\app.routes.js
...\wwwroot\app\components
...\wwwroot\app\components\app.component.js
...\wwwroot\app\components\user-detail.component.js
...\wwwroot\app\components\users.component.js
...\wwwroot\app\main.js
...\wwwroot\app\models
...\wwwroot\app\models\privilege-group.js
...\wwwroot\app\models\privilege.js
...\wwwroot\app\models\user.js
...\wwwroot\app\rxjs-operators.js
...\wwwroot\app\services
...\wwwroot\app\services\privilege.service.js
...\wwwroot\app\services\user.service.js
...\wwwroot\images\...
...\wwwroot\index.html
...\wwwroot\lib\[copy of node_modules]
...\wwwroot\scripts\...
...\wwwroot\styles\...
...\wwwroot\systemjs.config.js

Я нашел этот хороший plnkr, который использует точно такой же версии, как я, и работает как шарм. Единственная разница, которую я вижу, заключается в том, что скрипты транспилируются в браузере.

Есть идеи, что не так?

С уважением, Карстен


person Carsten Franke    schedule 14.07.2016    source источник
comment
У вас есть в файле system.config.js поле http в переменной ngPackageNames?   -  person Yevgen    schedule 14.07.2016
comment
Я добавил содержимое файла systemjs.config.js. Но при текущей настройке (файл лежит в wwwroot) сомневаюсь, что это как-то связано с моей проблемой.   -  person Carsten Franke    schedule 14.07.2016


Ответы (2)


Похоже, у вас неправильный импорт. При импорте вы импортировали объект Observalbe, но не импортировали операторов. Попробуйте изменить в своих служебных файлах:

import '../rxjs-operators';

to

import 'rxjs/add/operator/map';
import 'rxjs/add/operator/toPromise';
import 'rxjs/add/operator/catch';

или в более общем виде:

import 'rxjs/Rx';
person Yevgen    schedule 14.07.2016
comment
Если бы это было НАСТОЛЬКО просто, я бы не создавал тему. ;) Это мало что меняет. - person Carsten Franke; 14.07.2016
comment
Убедитесь, что у вас действительно есть папка rxjs в вашей lib. - person Yevgen; 14.07.2016
comment
В нем около 1349 файлов. - person Carsten Franke; 14.07.2016
comment
Я бы переместил ваши библиотеки обратно в node_modules из lib и перешел на этот «rxjs»: «node_modules/rxjs» или получил структуру проекта по умолчанию. Ваша система неправильно отображает папку rxjs. - person Yevgen; 14.07.2016
comment
Я заменил список файлов и папок с (к сожалению) неполного снимка экрана на выдержку из консоли. Как видите, у меня есть две копии node_modules (да, это требует доработки). Все другие модули, которые я использую, не показывают этой проблемы, поэтому я думаю, что проблема не в пути. - person Carsten Franke; 15.07.2016

Проблема в том, что tsc не может скомпилировать папку. Просто. Для компиляции требуется файл.

Если вы хотите скомпилировать все - вы должны использовать один из сборщиков, например gulp - https://www.npmjs.com/package/gulp-typescript или лучше — webpack, я могу порекомендовать этот стартер — https://github.com/AngularClass/angular2-webpack-starter

Я использовал его в качестве основы для одного из проектов моей компании, который сейчас находится в производстве.

Или, в качестве альтернативы, умные IDE, такие как WebStorm или что-то еще, могут компилировать «на лету» все файлы ts в js.

person Oleg Barinov    schedule 14.07.2016
comment
Но если я правильно понимаю, быстрый запуск Angular 2 использует tsc -w для переноса всех (?) файлов и отслеживает их изменения. Вот почему я ожидал, что это сработает. - person Carsten Franke; 14.07.2016