Как правильно связать компонент для обновления при обновлении значения? - PullRequest
0 голосов
/ 19 декабря 2018

У меня есть простое приложение, целью которого является получение текущей погоды для местоположения.Это местоположение имеет значение по умолчанию, но у одного из компонентов есть кнопки, которые должны изменить местоположение, вызвать API, а затем повторно отобразить данные.

Итак, у меня есть два компонента: Location и WeatherDisplay.У меня также есть Служба, которая выполняет тяжелую работу (в основном вызывая API).

Моя проблема в том, что WeatherDisplay не меняется, когда пользователь нажимает на новое местоположение.Я новичок в Angular, поэтому дайте мне знать, если я пропущу что-то в этой «презентации».

Я пробовал несколько разных вещей, и я думаю, что я сузил это до чего-то в APIОбслуживание.

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { MessageService } from './message.service';
import { BehaviorSubject } from 'rxjs';
import { WeatherStruct } from './weather';

const API_URL = 'http://api.openweathermap.org/data/2.5/weather';
// my key for the OpenWeatherAPI
const API_KEY = '3f25...'; // deleted most of what my key is just 'cause
// default location
const LOCATION_ID = '4480285'; // Morrisville, NC
// get the value from the message

@Injectable({
    providedIn: 'root'
})
export class APIService {
    private locationSource: BehaviorSubject<any> = new BehaviorSubject(null);
    currentLocation = this.locationSource.asObservable();

    constructor(
        private httpClient: HttpClient,
        private msgService: MessageService
    ) {}

    getWeather(loc_id = '4480285'){
        // this.msgService.add('api.service.ts '+loc_id);
      console.log('api.service getWeather('+loc_id+')');
      const api_string = `${API_URL}?id=${loc_id}&APPID=${API_KEY}&units=imperial`;
      console.log(api_string);
      return this.httpClient.get(api_string);
  }

    changeLocation(locid: string) {
        console.log('api.service changeLocation('+locid+')');
        this.locationSource.next(locid);
        // need to tell weather-display to re-fetch the data
        this.getWeather(locid);
    }
}

в шаблоне компонента местоположения у меня есть:

<button (click)="newLocation('5391811')">San Diego, CA</button>

компонент отображения погоды:

import { Component, OnInit } from '@angular/core';
import { MessageService } from '../message.service';
import { WeatherStruct } from '../weather';
import { APIService } from '../api.service';

@Component({
    selector: 'app-weather-display',
    templateUrl: './weather-display.component.html',
    providers: [APIService],
    styleUrls: ['./weather-display.component.css']
})

export class WeatherDisplayComponent implements OnInit {
    weather: WeatherStruct;
    private today: number = Date.now();
    loc_id: string;

    constructor(
        private apiService: APIService,
        private msgService: MessageService
    ) {}

    ngOnInit() {
        this.apiService.currentLocation
            .subscribe(location => this.loc_id = location);
        this.fetchWeather();
        console.log('CCC');
    }

    fetchWeather(loc_id = '4480285') {
        console.log('weather-display.component showWeather(' + loc_id + ')');
        this.apiService.getWeather(loc_id).subscribe((data: WeatherStruct) => {
            this.weather = data;
            this.weather['today'] = this.today;
        });
    }
}

и в location.component.ts:

export class LocationComponent implements OnInit {
    location: string;

    @Output() talk: EventEmitter<string> = new EventEmitter<string>();

    constructor(private apiService: APIService,
        private msgService: MessageService) {}

    ngOnInit() {
        this.apiService.currentLocation.subscribe(
            location => (this.location = location)
        );
    }

    newLocation(newLoc: string) {
        console.log('location.component newLocation('+newLoc+')');
        // use an event emiitter 
        this.talk.emit(newLoc);
        // call a function in the service
        this.apiService.changeLocation(newLoc);
    }
}

и, наконец, в app.component.ts

export class AppComponent implements OnInit {

    location: string;

    constructor(private apiService: APIService) {}

    title = 'The Weather';

    ngOnInit() {
        this.apiService.currentLocation.subscribe(
            location => (this.location = location)
        );
    }
    goGetWeather(newLoc){
        console.log("app.component.goGetWeather("+newLoc+")");
    }
}

Когда я запускаю свое приложение и пытаюсь нажать на кнопку, я вижу, что код выполняется так, как я ожидаю:

location.component newLocation () вызывается, что вызывает app.component.goGetWeather [который сообщает мне, что работает источник событий];и api.service changeLocation , что приводит к api.service getWeather , который создает правильно отформатированную строку API для вызова

http://api.openweathermap.org/data/2.5/weather?id=2643743&APPID=3f25...&units=imperial

, но ничего не изменяется в компоненте DisplayWeather.

Я знаю, что это что-то простое, но я просто не могу понять, в чем дело.

Ответы [ 2 ]

0 голосов
/ 19 декабря 2018

Если я правильно понял вашу проблему, данные не передаются должным образом.Вы можете исправить это, используя больше RxJS.Посмотрите, как бы я это реализовал.

// Dont use any inside your source code, use an type alias while you are working dirty instead. 
type Location = any;
type Weather = any;


// The name is not really correct anymore, it is more a WeatherLocService.. 
@Injectable()
export class APIService {

  defaultLocation = {
    // .. or handle the cases where currentLocation emits null anyhow. 
  };

  // Holds the current location
  private currentLocation$ = new BehaviorSubject<Location | null>(null);

  // Holds the weather at the current location
  weatherAtLocation$: Observable<Weather>;

  constructor(private httpClient: HttpClient) {

    // Whenever location changes, call getWeather with the new location and return the result 
    this.weatherAtLocation$ = this.currentLocation$.pipe(  
      mergeMap(location => this.getWeather(location))
    )
  }

  getWeather(location: Location = this.defaultLocation): Observable<Weather> {
      return this.httpClient.get("https://www.example.com");
  }

  updateLocation(location: Location) {
    this.currentLocation$.next(location);
  }

}

https://stackblitz.com/edit/angular-umqxpv?file=src%2Fapp%2Fapi.service.ts

0 голосов
/ 19 декабря 2018

это может быть достигнуто с помощью общего сервиса.Обратитесь к ссылке о том, как реализовать общий сервис: http://www.angulartutorial.net/2017/09/angular-share-data-between-components.html

Вот как будет выглядеть код:

Service.ts

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class SharedService {
    // Subject (emitter) for User Name Change
    CityNameChange: Subject<string> = new Subject<string>();

    // SetUserChange() - This method sets the local variable and emits the change
    SetCityChange(param: string) {
        this.CityNameChange.next(param);
    }
}

в вашем первомкомпонент по нажатию кнопки, при которой происходит смена города, вызывается service.setCityChange ()

this._sharedService.SetCItyChange(newCityName);

Во втором компоненте в ngOnInit подпишитесь на событие изменения города и получите данные о погоде на основе нового города:

this._sharedService.UserNameChange
  .subscribe((value) => {
    // Get the weather information
  });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...