Angular 6: обновить маршрут из частичного компонента - PullRequest
0 голосов
/ 20 декабря 2018

У меня есть приложение курсов на английском и французском языках.Я хочу сделать заголовок для выпадающего списка языков в другом компоненте, который появится на всех страницах программы.Когда пользователь меняет язык в заголовке, я хочу, чтобы это происходило:

  1. Интерфейс вызывает бэкэнд и меняет предпочтения языка для пользователя, поэтому в следующий раз, когда он войдет, он увидит курсына языке, который он выбрал
  2. , что независимо от того, где, маршрут изменится на страницу со списком курсов и загрузит курсы с обновленными настройками.

Пока это только работаесли я обновлю страницу и не сразу.Когда я писал языковую кнопку в каждом компоненте, он работал.

РЕДАКТИРОВАТЬ: он работает на всех страницах, кроме списка курсов, возможно, потому что мы уже внутри этого компонента, поэтому мы нене вызывать функцию getCoursesByLanguage (когда мы находимся на других компонентах, я использую router.navigate к компоненту списка курсов, который запускает getCoursesByLanguage).Как заставить это работать на странице списка курсов?

это соответствующий код:

app.component.html

<div class='container-fluid' id='main'>
      <lg-header></lg-header>
      <router-outlet></router-outlet>
</div>

header.component.html

<div style="float:right; padding-right:30px">
  <button id="button-logout" mat-button (click)="toggleLanguage()">
    <img width="27" height="17" style="margin-right: 10px;" src="./assets/images/{{flag}}_Flag.png"/>
    <span>{{languageName}}</span>
  </button>
</div>

header.component.ts

import { Component, OnInit, Pipe, PipeTransform } from '@angular/core';
import { ActivatedRoute, Router, Routes } from '@angular/router';
import { LocalStorage } from '@ngx-pwa/local-storage';

import { IUser, IUserCourses } from '../users/user';
import { UserProgressService } from '../users/user-progress.service';

@Component({
  selector: 'lg-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.sass']
})
export class HeaderComponent implements OnInit {
  // variables for laungage
  language: number;
  languageName: string;
  flag: string;

  constructor(private route: ActivatedRoute,
              private router: Router,
              private userProgressService: UserProgressService) {
                userProgressService.connectUser();
                this.getUpdatedLanguageAndFlag();
              }

  // get from service updated data from backend and localStorage
  getUpdatedLanguageAndFlag() {
    this.language = this.userProgressService.getLanguage();
    this.flag = this.userProgressService.getFlag();
    this.languageName = this.userProgressService.getLanguageName();
  }

  ngOnInit() { }

  // change laungage
  toggleLanguage(){
    this.userProgressService.changeAppLanguage();
    this.getUpdatedLanguageAndFlag();
    if (this.router.url == '/courses') {
      // I need to trigger here getCourseListByLanguage in course-list from here
    }
    else
      this.router.navigate(["/courses"]);
  }
}

user-progress.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, groupBy } from 'rxjs/operators';
import { LocalStorage } from '@ngx-pwa/local-storage';

import { UserService } from './user.service';
import { IUser, IUserCourses } from './user';

@Injectable({
  providedIn: 'root'
})

export class UserProgressService {
      private user: IUser;
      private errorMessage: string;
      private language: number;
      private flag: string;
      private languageName: string;

      constructor(private userService: UserService) { }

      // get user from local store
      connectUser() {
        this.user = JSON.parse(localStorage.getItem('user'));
        this.language =+ localStorage.getItem('language');
        this.flag = localStorage.getItem('flag');
        this.languageName = localStorage.getItem('languageName');
      }

      getUserName() {
        return this.user.name;
      }

      getLanguage() {
        return this.language;
      }

      getLanguageName() {
        return this.languageName;
      }

      getFlag() {
        return this.flag;
      }

      // determine flag and language name depends on language value
      setFlagLanName() {
        if (this.language == 0) {
          this.flag = "UK";
          this.languageName = "English";
        }

        else {
          this.flag = "FR";
          this.languageName = "French";
        }

        localStorage.setItem('languageName', this.languageName);
        localStorage.setItem('flag', this.flag);
      }

      // manage toggle action
      changeAppLanguage() {
        if ( this.language == 0 )
            this.language = 1;

        else
            this.language = 0;

        this.setFlagLanName();
        this.updateBackendLanguage(this.language);
      }

      // update language in backend
      updateBackendLanguage(lan: number) {
        this.userService.updateLanguage(this.user.id, lan).subscribe(
          () => { this.getUserFromBackend(); },
          error  => this.errorMessage = <any>error
        );
      }

      // update user after changes - call the backend again for GET
      getUserFromBackend() {
        this.userService.getUser(this.user.id).subscribe(
          user => {
            this.user = user;
            this.language = user.language;
            localStorage.setItem('user', JSON.stringify(this.user));
            localStorage.setItem('language', this.user.language.toString());
          },
          error  => this.errorMessage = <any>error
        );
      }
  }

user.service.ts

import { environment } from '../../environments/environment';
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams } from '@angular/common/http';
import { BehaviorSubject } from 'rxjs';
import { Observable, throwError } from 'rxjs';
import { catchError, groupBy, tap } from 'rxjs/operators';

import { IUser, IUserCourses } from './user';

const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};

@Injectable()
export class UserService {
  private url = environment.educationBE_url+'/users';

  constructor(private http: HttpClient) {  }

  // Get Single user by id. will 404 if id not found
  getUser(id: number): Observable<IUser> {
    const detailUrl = `${this.url}/${id}` + '.json';

    return this.http.get<IUser>(detailUrl)
        .pipe(catchError(this.handleError));
  }

  // update user preference for language view
  updateLanguage(user_id: number, language: number) {
    const userUrl = `${this.url}/${user_id}` + '.json';
    let body = JSON.stringify({language: language});

    return this.http.patch(userUrl, body, httpOptions)
      .pipe(
        tap(_ => console.log(`updated user ${user_id} with this entry: ${language}`)),
        catchError(this.handleError)
      );
  }

  // // Handle Any Kind of Errors
  private handleError(error: HttpErrorResponse) {

    // A client-side or network error occured. Handle it accordingly.
    if (error.error instanceof ErrorEvent) {
      console.error(`Error: ${error.error.message}`);
    }

    // The backend returned an unsuccessful response code.
    // The response body may contain clues as to what went wrong.
    else {
      console.error(`Error Code: ${error.status}\nMessage: ${error.message}`);
    }

    // return an Observable with a user-facing error error message
    return throwError(
      'Something bad happend; please try again later.');
  }
}

course-list.ts

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router, Routes } from '@angular/router';

import { ICourse } from '../course';
import { CourseService } from '../course.service';
import { UserProgressService } from '../../users/user-progress.service';

@Component({
  selector: 'lg-course-list',
  templateUrl: './course-list.component.html',
  styleUrls: ['./course-list.component.sass']
})

export class CourseListComponent implements OnInit {
  courses: ICourse[] = [];
  errorMessage: string;

  // variables for laungage
  language: number;
  languageName: string;
  flag: string;
  
  // the courses we display in html file
  coursesByLanguage: ICourse[] = [];

  constructor(private courseService: CourseService,
        private userProgressService: UserProgressService,
        private route: ActivatedRoute,
        private router: Router) {
          userProgressService.connectUser();
          this.getUpdatedLanguageAndFlag();
        }

  ngOnInit() {
    this.getCourseList();
  }

  getUpdatedLanguageAndFlag() {
    this.language = this.userProgressService.getLanguage();
    this.flag = this.userProgressService.getFlag();
    this.languageName = this.userProgressService.getLanguageName();
  }

  // Get list of courses from service
  getCourseList() {
    this.courseService.getCourses()
      .subscribe(
        courses => {
          this.courses = courses;
          this.getCourseListByLanguage();
        },
        errorMessage => this.errorMessage = <any>Error
      );
  }

  // get from courses only the relavent ones
  getCourseListByLanguage() {
    this.getUpdatedLanguageAndFlag();
    this.coursesByLanguage = this.courses.filter( course => course.language == this.language);
  }
  
  
  // change laungage
  toggleLanguage(){
    this.userProgressService.changeAppLanguage();
    this.getCourseListByLanguage();
  }

}

Ответы [ 2 ]

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

Вы получаете все курсы здесь:

  ngOnInit() {
    this.getCourseList();
  }

И когда вы получаете их, вы сразу фильтруете их:

  // Get list of courses from service
  getCourseList() {
    this.courseService.getCourses()
      .subscribe(
        courses => {
          this.courses = courses;
          this.getCourseListByLanguage();
        },
        errorMessage => this.errorMessage = <any>Error
      );
  }

  getCourseListByLanguage() {
    this.getUpdatedLanguageAndFlag();
    this.coursesByLanguage = this.courses.filter( course => course.language == this.language);
  }

Так что теперь courses фильтруется толькодля определенного языка: английский1, английский2, английский3 и т. д.

Затем вы позже позвоните toggleLanguage и повторно отфильтруете курсы.

  toggleLanguage(){
    this.userProgressService.changeAppLanguage();
    this.getCourseListByLanguage();
  }

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

Один из способов решить эту проблему - сохранить список всех курсов отдельно от отфильтрованных курсов.

Так что-то вроде этого:

  courses: ICourse[] = [];
  allCourses: ICourse[] = [];

Затем установите его здесь:

  getCourseList() {
    this.courseService.getCourses()
      .subscribe(
        courses => {
          this.allCourses = courses;    // Set all courses here
          this.getCourseListByLanguage();
        },
        errorMessage => this.errorMessage = <any>Error
      );
  }

Затем выполните фильтрацию в переменную courses.

  getCourseListByLanguage() {
    this.getUpdatedLanguageAndFlag();
    this.coursesByLanguage = this.allCourses.filter( course => course.language == this.language);
  }

Теперь вывсегда фильтруют полный список всех курсов только по тем для конкретного языка в свойстве courses.

Это работает?

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

Похоже, что проблема с синхронизацией.

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

Попробуйте переместить подпискуздесь (псевдокод):

  toggleLanguage(){
    this.userProgressService.changeAppLanguage().subscribe(x => {
      this.getUpdatedLanguageAndFlag();
      this.router.navigate(["/courses"]);
    };
  }

Поместив следующие две функции в подписку, вы убедитесь, что они не будут выполнены до тех пор, пока язык не будет фактически изменен.

ПРИМЕЧАНИЕ. Для этогочтобы работать правильно, вам нужен ваш метод .changeAppLanguage(), чтобы вернуть наблюдаемое.Таким образом, этот код также необходимо изменить (псевдокод):

  changeAppLanguage(): Observable<any> {
    if ( this.language == 0 )
        this.language = 1;

    else
        this.language = 0;

    this.setFlagLanName();
    return this.userService.updateLanguage(this.user.id, this.language);
  }

БОЛЬШОЕ ПРИМЕЧАНИЕ. Ни один из этого кода не был проверен синтаксисом, так как не было предоставленного стекаблица с кодом.

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

Это поможет гарантировать, что ваши процессы происходят в правильной последовательности.

...