Вызов, не загружающийся в поле зрения в Angular2 API

#angular #typescript #rxjs

#angular #typescript #rxjs

Вопрос:

Здесь новый веб-разработчик (поэтому я заранее приношу извинения за несколько неаккуратный код). Меня попросили создать прототип страницы инвентаризации в Angular2.

Как вы увидите в приведенном ниже коде. Начиная со страницы EXAMPLEapi.service, мы выполняем вызов нашего базового серверного API (‘https://api.EXAMPLE.com/api/v1/inventory ?’), который затем передается в ngOnInit в EXAMPLEapi.component для загрузки нашего первоначального инвентаря на странице.

Отсюда вы должны иметь возможность выбирать различные значения фильтрации (минимальный / максимальный год, пробег, цвета и т.д.). Эти значения передаются из формы (EXAMPLEapi.component.html ) в функцию refreshInv() (EXAMPLEapi.component), которая преобразует значения в параметры, используя URLSearchParams, прежде чем, наконец, быть переданным обратно в вызов API.

API построен таким образом, что простое добавление ‘min_price =’ в конце базового URL должно соответствующим образом отразиться на вызове инвентаризации.

Проблема в том, что я успешно получаю сетевой вызов с успешным статусом 200, когда onSubmit завершается передачей значений фильтрации. К сожалению, ни один из инвентаря не загружается обратно на страницу. Это просто пустое.

EXAMPLEAPI.service.ts

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


@Injectable()
export class EXAMPLEAPIService {
constructor(public http: Http){}

baseUrl = "https://api.EXAMPLE.com/api/v1/inventory?";


getVehicles(params) {
    return this.http.get(this.baseUrl, params)
        .map(res => res.json());
}

click(): Observable<Data[]> {
    console.log(value);
}

log(x) {
    console.log(x);
}
  

EXAMPLE.component.ts

 import {Component, OnInit} from 'angular2/core';
import {Http, RequestOptions, URLSearchParams, HTTP_PROVIDERS}       from 'angular2/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import {EXAMPLEService} from './EXAMPLEapi.service';
import {FormsModule} from 'angular2/forms';


@Component({
selector: 'example-container',
styles: [`
        * {
            display: inline-block;
            max-width: 500px;
            vertical-align: top;
        }
        .invbar {
            border: 1px solid black;
            padding: 5px;
            background-color: #6A7372;
            border-radius: 15px;
        }
        .hotel_container{
            border: 1px solid black;
            background-color: #6A7372;
            max-height: 410px;
        }
        #colorsBox{
            display: block;
        }
        input[type="checkbox"]{
            margin-left: 5px;
        }
        label{
            margin-top: 5px;
            color: #FF3B37;
        }
     `],
templateUrl: 'app/exampleapi.component.html',
providers: [HTTP_PROVIDERS, EXAMPLEAPIService]
})

export class EXAMPLEAPIComponent implements OnInit {
constructor(public EXAMPLEAPIService: EXAMPLEAPIService){


    this.minPrice = {
        value: ''
    };
    this.maxPrice = {
        value: ''
    };
    this.minYear = {
        value: '2013'
    };
    this.maxYear = {
        value: ''
    };

}

// On Page-Load this loads the API Call
ngOnInit() {
    this.EXAMPLEService.getVehicles()
        .subscribe(data => {
            this.vehicles = data.data;
            console.log(data.data[0].name);
        }
}

// Takes the Filter Parameters and passes them back into the API Call
refreshInv() {
    let params = new URLSearchParams();
    params.set('min_price', this.minPrice.value);
    params.set('max_price', this.maxPrice.value);
    params.set('min_year', this.minYear.value);
    params.set('max_year', this.maxYear.value);
    params.set('ext_color', this.colorsChecked);

    let options = new RequestOptions({
        search: params
    });
    console.log(options);
    return this.EXAMPLEService.getVehicles(options)
        .subscribe(data => this.vehicles = data.data);
}

// Keeps the Filter Queries Actively Refreshing on each interaction
onSubmit(value: any): Observable<Data[]> {
    console.log(value);
    return this.refreshInv()
};

// Dynamic Checkbox Generation for Color Filters
colors = ['Red', 'Blue', 'Yellow', 'Green', 'White', 'Black'];
colorsMap = {
    Red: false,
    Blue: false,
    Yellow: false,
};
colorsChecked = [];

initColorsMap() {
    for (var x=0; x<this.colors.length; x  ) {
        this.colorsMap[this.colors[x]] = true;
    }
}

updateCheckedColors(color, event) {
    this.colorsMap[color] = event.target.checked;
}

colorUpdate() {
    for(var x in this.colorsMap) {
        if(this.colorsMap[x]) {
            this.colorsChecked.push[x];
        }
    }
    this.colors = this.colorsChecked;
    this.colorsChecked = [];
}

// Dynamic Checkbox Generation for Lift Filters
lifts = ['Lifted', 'Unlifted'];
liftsMap = {
    Lifted: false,
    Unlifted: false,
};
liftsChecked = [];

initLiftsMap() {
    for (var x=0; x<this.lifts.length; x  ) {
        this.liftsMap[this.maps[x]] = true;
    }
}

updateCheckedMaps(map, event) {
    this.liftsMap[lift] = event.target.checked;
}

liftUpdate() {
    for(var x in this.liftMap) {
        if(this.liftMap[x]) {
            this.liftsChecked.push[x];
        }
    }
    this.lifts = this.liftsChecked;
    this.liftsChecked = [];
}

// Dynamic Checkbox Generation for Transmission Filters
trans = ['Manual', 'Automatic'];
transMap = {
    Manual: false,
    Automatic: false,
};
transChecked = [];

initTransMap() {
    for (var x=0; x<this.trans.length; x  ) {
        this.transMap[this.trans[x]] = true;
    }
}

updateCheckedmap(map, event) {
    this.transMap[map] = event.target.checked;
}

transUpdate() {
    for(var x in this.transMap) {
        if(this.transMap[x]) {
            this.transChecked.push[x];
        }
    }
    this.trans = this.transChecked;
    this.transChecked = [];
}

// Dynamic Checkbox Generation for Interior Filters
ints = ['Cloth', 'Leather', 'Vinyl'];
intsMap = {
    Cloth: false,
    Leather: false,
    Vinyl: false,
};
intsChecked = [];

initIntMap() {
    for (var x=0; x<this.ints.length; x  ) {
        this.intsMap[this.ints[x]] = true;
    }
}

updateCheckedmap(map, event) {
    this.intsMap[map] = event.target.checked;
}

intsUpdate() {
    for(var x in this.intsMap) {
        if(this.intsMap[x]) {
            this.intsChecked.push[x];
        }
    }
    this.ints = this.intsChecked;
    this.intsChecked = [];
}


}
  

Комментарии:

1. вы также можете захотеть обновить свой angular2 до @angular. angular2 устарел. angular.io/docs/ts/latest/guide/npm-packages.html

Ответ №1:

Прежде всего, если вам нужен пример того, как настроить angular2 plnkr (используя unpkg), посмотрите здесь.

Что касается вашего кода, ваш refreshInv возвращает подписку ( Observable.subscribe() returns a subscription), от которой нет никакой пользы, кроме как отказаться от подписки.

Я хотел бы предложить другой подход, который хорошо сработал для меня и упростит ваш код. На самом деле, каков ваш поток данных: ‘query’ -> ‘request’ -> ‘display’. В rxjs это легко кодируется как:

 let items$ = jsonParams$
  .map(urlParamsMapper)
  .switchMap(urlParams => http.get(baseUrl, urlParams)
  .map(response => response.json().data);
  

Where urlParamsMapper получает запрос на основе json (как вы указали) и преобразует его в URLSearchParams .

и jsonParams$ является наблюдаемым (в нашем случае субъектом), который вы можете обновить с помощью соответствующих деталей запроса:

 let jsonParams$: Subject<any> = new BehaviorSubject({
  min_price: '',
  max_price: '',
  ...
  ext_color: ''
});
  

и каждый раз, когда вы обновляете свои параметры, вы создаете свой новый json (у вас есть логика в вашем коде), а затем выполняете:

 jsonParams$.next(currentParams);
  

Обратите внимание, что в rxjs 4 вам следует использовать onNext вместо next .

Это запустит всю цепочку для вас.

Последний шаг привязки его к вашему шаблону будет чем-то вроде:

 <my-item-renderer *ngFor="let item; of items$ | async;" [item]="item"></my-item>
  

Обратите внимание async на то, что позволяет вам привязываться к наблюдателю.

Теперь, поскольку вы, вероятно, захотите сохранить архитектуру своего сервиса, то есть сохранить http-вызов в сервисе, вы можете захотеть разбить его следующим образом:

в службе:

 createItemsLoader(jsonParams$: Observable<any>): Observable<Array<any>> {
  return jsonParams$.map(urlParamsMapper)
    .switchMap(urlParams => http.get(baseUrl, urlParams))
    .map(response => response.json().data);
}
  

а затем в вашем компоненте:

 let jsonParams$: Subject<any> = new BehaviorSubject({min_price: '' ...});
let items$ = ItemsService.createItemsLoader(jsonParams$);

refreshInv() {
  .... let jsonParams: any = construct json params ....
  this.jsonParams$.next(jsonParams);
}
  

И в этом прелесть реактивного программирования: если вы правильно построили свой конвейер, все, что вам нужно сделать, это отправить новые данные по нужному каналу, и тот, кто слушает на другом конце, получит их. Приведенный выше код все еще находится в процедурном «мышлении».

Комментарии:

1. Увидев ваш ответ, мы решили вернуться и применить очень похожий подход к вашему сообщению! Код стал намного чище и связнее. Большое вам спасибо!

2. Рад, что смог помочь. Реактивный подход отличается, и для адаптации требуется время. Старые привычки умирают с трудом, и я вижу, как люди пытаются заставить его вести себя как старая модель программирования. Несколько месяцев назад я прочитал это: gist.github.com/staltz/868e7e9bc2a7b8c1f754 (несколько раз), и я думаю, что это дало мне хорошую отправную точку. Когда я начинаю новый проект, я сначала записываю все входные данные, выходные данные и то, как они смешиваются. Это дает мне график, который легко реализовать. С этого момента все намного проще, и добавление нового поведения — это просто вопрос подключения его в нужное место.