Как эффективно использовать компонент Http в службе в angular 2 бета-версии?

Я пытаюсь играть с Angular 2-бета, и я хочу работать с компонентом Http. Но здесь есть серьезная проблема:

Я читал этот и Я знаю, что в Angular 2 (в отличие от Angular 1) компонент Http не является сервисом, который возвращает Promise. Он возвращает что-то, называемое Observable. Мы знаем, что Компонент лучше не использовать Http напрямую. Эффективным способом является предоставление услуги, которая отвечает за потребление Http. Но как?! Если это после завершения запроса, верните обещание? (смотрите здесь)

Это имеет смысл вообще?!

Ответ 1

Возможно использование Angular 2 для реализации служб. Они просто соответствуют инъекционным классам, как описано ниже. В этом случае этот класс может быть введен в другие элементы, такие как компоненты.

import {Injectable} from 'angular2/core';
import {Http, Headers} from 'angular2/http';
import 'rxjs/add/operator/map';

@Injectable()
export class CompanyService {
  constructor(http:Http) {
    this.http = http;
  }
}

Вы можете вставить в него объект Http (используя его конструктор) при условии, что вы указали HTTP_PROVIDERS при загрузке основного компонента приложения:

import {bootstrap} from 'angular2/platform/browser'
import {HTTP_PROVIDERS} from 'angular2/http';
import {AppComponent} from './app.component'

bootstrap(AppComponent, [
  HTTP_PROVIDERS
]);

Эта услуга может быть затем введена в компонент, как описано ниже. Не забудьте указать его в списке providers компонента.

import { Component, View, Inject } from 'angular2/core';
import { CompanyService } from './company-service';

@Component({
  selector: 'company-list',
  providers: [ CompanyService ],
  template: `
    (...)  `
})

export class CompanyList {
  constructor(private service: CompanyService) {
    this.service = service;
  }
}

Затем вы можете реализовать метод, используя объект Http в своей службе и вернуть объект Observable, соответствующий вашему запросу:

@Injectable()
export class CompanyService {
  constructor(http:Http) {
    this.http = http;
  }

  getCompanies() {
    return this.http.get('https://angular2.apispark.net/v1/companies/')
                  .map(res => res.json());
  }
}

Компонент может затем вызвать этот метод getCompanies и подписаться на обратный вызов объекта Observable, который будет уведомляться, когда ответ должен обновить состояние компонента (так же, как вы сделали с promises в Angular1)

export class CompanyList implements OnInit {
  public companies: Company[];

  constructor(private service: CompanyService) {
    this.service = service;
  }

  ngOnInit() {
    this.service.getCompanies().subscribe(
      data => this.companies = data);
  }
}

Edit

Как foxx, предложенный в его комментарии, труба async также может использоваться для неявной подписки на наблюдаемый объект. Вот как его использовать. Сначала обновите свой компонент, чтобы поместить наблюдаемый объект в атрибут, который вы хотите отобразить:

export class CompanyList implements OnInit {
  public companies: Company[];

  constructor(private service: CompanyService) {
    this.service = service;
  }

  ngOnInit() {
    this.companies = this.service.getCompanies();
  }
}

Используйте асинхронный канал в шаблоне:

@Component({
  selector: 'company-list',
  providers: [ CompanyService ],
  template: `
    <ul>
      <li *ngFor="#company of companies | async">{{company.name}}</li>
    </ul>
  `
})
export class CompanyList implements OnInit {
  (...)
}

Эта статья в двух частях также может содержать более подробную информацию:

Надеюсь, это поможет вам, Thierry

Ответ 2

Нет необходимости преобразовывать наблюдаемый, возвращаемый методом Http get() в обещание. В большинстве случаев служба может просто вернуть наблюдаемый.

Если мы выберем с сервера массив или примитивный тип (т.е. строка, число, логическое) с сервера, мы можем упростить нашу логику контроллера, используя возвращенный наблюдаемые непосредственно в нашем шаблоне, с asyncPipe. Эта труба будет автоматически подписываться на наблюдаемую (она также работает с обещанием), и она вернет самое последнее значение, которое излучает наблюдаемое. Когда выдается новое значение, канал отмечает, что компонент должен быть проверен на изменения, поэтому представление автоматически обновится с новым значением.

Если мы извлекаем объект с сервера, , я не знаю о каком-либо способе использования asyncPipe,, мы могли бы использовать асинхронный канал в сочетании с безопасный навигатор следующим образом:

{{(objectData$ | async)?.name}}

Но это выглядит сложным, и нам нужно будет повторить это для каждого свойства объекта, которое мы хотели отобразить.

Вместо этого я предлагаю subscribe() наблюдаемому в контроллере и хранить содержащийся объект в свойстве компонента. Затем мы используем безопасный навигатор (?) Или (как упоминается в комментарии @Evan Plaice в комментарии) NgIf в шаблоне. Если мы не используем безопасный навигационный оператор или NgIf, ошибка будет вызываться, когда шаблон сначала попытается выполнить рендеринг, потому что объект еще не заполнен значением.

Обратите внимание, что служба ниже всегда возвращает наблюдаемый для каждого метода get.

service.ts

import {Injectable} from 'angular2/core';
import {Http} from 'angular2/http';
import 'rxjs/add/operator/map';  // we need to import this now

@Injectable()
export class MyService {
  constructor(private _http:Http) {}
  getArrayDataObservable() {
    return this._http.get('./data/array.json')
      .map(data => data.json());
  }
  getPrimitiveDataObservable() {
    return this._http.get('./data/primitive.txt')
      .map(data => data.text());   // note .text() here
  }
  getObjectDataObservable() {
    return this._http.get('./data/object.json')
      .map(data => data.json());
  }
}

app.ts

import {Component} from 'angular2/core';
import {MyService} from './my-service.service';
import {HTTP_PROVIDERS} from 'angular2/http';

@Component({
  selector: 'my-app',
  providers: [HTTP_PROVIDERS, MyService],
  template: `
    <div>array data using '| async':
      <div *ngFor="#item of arrayData$ | async">{{item}}</div>
    </div>
    <div>primitive data using '| async': {{primitiveData$ | async}}</div>
    <div>object data using ?.: {{objectData?.name}}</div>
    <div *ngIf="objectData">object data using NgIf: {{objectData.name}}</div>`
})
export class AppComponent {
  constructor(private _myService:MyService) { console.clear(); }
  ngOnInit() {
    this.arrayData$     = this._myService.getArrayDataObservable();
    this.primitiveData$ = this._myService.getPrimitiveDataObservable();
    this._myService.getObjectDataObservable()
      .subscribe(data => this.objectData = data);
  }
}

Примечание. Я помещаю "Observable" в имена метода службы – например, getArrayDataObervable() – просто чтобы подчеркнуть, что метод возвращает Observable. Обычно вы не ставите "Observable" в имени.

Данные/array.json

[ 1,2,3 ]

Данные/primitive.json

Greetings SO friends!

Данные/object.json

{ "name": "Mark" }

Вывод:

array data using '| async':
1
2
3
primitive data using '| async': Greetings SO friends!
object data using .?: Mark
object data using NgIf: Mark

Plunker


Один из недостатков использования протокола async заключается в том, что нет механизма для обработки ошибок сервера в компоненте. я ответил на другой вопрос, в котором объясняется, как поймать такие ошибки в компоненте, но в этом случае нам всегда нужно использовать subscribe().