#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 (несколько раз), и я думаю, что это дало мне хорошую отправную точку. Когда я начинаю новый проект, я сначала записываю все входные данные, выходные данные и то, как они смешиваются. Это дает мне график, который легко реализовать. С этого момента все намного проще, и добавление нового поведения — это просто вопрос подключения его в нужное место.