У меня есть компонент с подробностями курса, который содержит данные (названный курс) из моего внутреннего приложения, и я хочу передать эти данные другому компоненту (курс-игра), не связанному с этим компонентом. Я хочу отображать те же данные, которые я получил от моего бэкэнда, в этих двух компонентах. Это соответствующие файлы:
модуль маршрутизации приложения:
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { CourseListComponent } from './courses/course-list/course-list.component';
import { CourseDetailComponent } from './courses/course-detail/course-detail.component';
import { CoursePlayComponent } from './courses/course-play/course-play.component';
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
const appRoutes: Routes = [
{ path: '', redirectTo: '/courses', pathMatch: 'full' },
{ path: 'courses', component: CourseListComponent, pathMatch: 'full' },
{ path: 'courses/:id', component: CourseDetailComponent, pathMatch: 'full'},
{ path: 'courses/:id/:id', component: CoursePlayComponent, pathMatch: 'full' },
{ path: '**', component: PageNotFoundComponent, pathMatch: 'full' }];
@NgModule({
imports: [RouterModule.forRoot(appRoutes)],
exports: [RouterModule]
})
export class AppRoutingModule { }курсы / курс (интерфейс)
export interface ICourse {
course_id: number;
title: string;
autor: string;
segments: ISegment[];
}
export interface ISegment {
segment_id: number;
unit_id: number;
unit_title: string;
name: string;
type: string;
data: string;
}курсы / курс. сервис:
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable, throwError } from 'rxjs';
import { catchError, groupBy } from 'rxjs/operators';
import { ICourse } from './course';
// Inject Data from Rails app to Angular app
@Injectable()
export class CourseService{
constructor(private http: HttpClient) { }
private url = 'http://localhost:3000/courses';
private courseUrl = 'http://localhost:3000/courses.json';
// 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('An error occured:', error.error.message);
}
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong.
else {
console.error(
'Backend returned code ${error.status}, ' +
'body was ${error.error}');
}
// return an Observable with a user-facing error error message
return throwError(
'Something bad happend; please try again later.');
}
// Get All Courses from Rails API App
getCourses(): Observable<ICourse[]> {
const coursesUrl = `${this.url}` + '.json';
return this.http.get<ICourse[]>(coursesUrl)
.pipe(catchError(this.handleError));
}
// Get Single Course by id. will 404 if id not found
getCourse(id: number): Observable<ICourse> {
const detailUrl = `${this.url}/${id}` + '.json';
return this.http.get<ICourse>(detailUrl)
.pipe(catchError(this.handleError));
}
}курсы / детали курса / детали курса.ts:
import { Component, OnInit, Pipe, PipeTransform } from '@angular/core';
import { ActivatedRoute, Router, Routes } from '@angular/router';
import { ICourse } from '../course';
import { CourseService } from '../course.service';
@Component({
selector: 'lg-course-detail',
templateUrl: './course-detail.component.html',
styleUrls: ['./course-detail.component.sass']
})
export class CourseDetailComponent implements OnInit {
course: ICourse;
errorMessage: string;
constructor(private courseService: CourseService,
private route: ActivatedRoute,
private router: Router) {
}
ngOnInit() {
const id = +this.route.snapshot.paramMap.get('id');
this.getCourse(id);
}
// Get course detail by id
getCourse(id: number) {
this.courseService.getCourse(id).subscribe(
course => this.course = course,
error => this.errorMessage = <any>error);
}
onBack(): void {
this.router.navigate(['/courses']);
}
}курсы / course-play / course-play.ts:
import { Component, OnInit} from '@angular/core';
import { ActivatedRoute, Router, Routes, NavigationEnd } from '@angular/router';
import { MatSidenavModule } from '@angular/material/sidenav';
import { ICourse } from '../course';
import { CourseService } from '../course.service';
@Component({
selector: 'lg-course-play-course-play',
templateUrl: './course-play.component.html',
styleUrls: ['./course-play.component.sass']
})
export class CoursePlayComponent implements OnInit {
courseId: number;
errorMessage: string;
private sub: any;
constructor(private courseService: CourseService,
private route: ActivatedRoute,
private router: Router) {
}
ngOnInit() {
}
onBack(): void {
this.router.navigate(['/courses/:id']);
}
}используйте наблюдаемый и подписывайтесь на него в каждом компоненте через службу.
Я пытаюсь, но это не позволяет мне сохранить изменения
Паван Шукла как? Я получаю курс от функции getCourse, и это наблюдаемое, которое я хочу передать компоненту курсовой игры.
вы можете определить getcourses в методе в course-play.ts и вызвать ngOninit, если вы не хотите вызывать метод из других источников
извините, это course-play.ts, вы уже используете общий сервис. дайте мне знать, работает ли это
Если я понимаю, что вы написали, я это сделал. Я подробно описываю метод getCourse, вызываю его в ngOninit и подписываюсь на полученные данные. конечно, содержит данные, и я хочу, чтобы эта же переменная передавалась в игру, но я не знаю, как





Без использования каких-либо других библиотек, Angular определяет несколько способов взаимодействия компонентов друг с другом. Документация
Поскольку ваши компоненты не являются родительскими / дочерними, а являются братьями и сестрами, варианты еще более ограничены.
Основываясь на показанном вами коде, я считаю, что №2 - ваш лучший вариант. Итак, вы можете добавить свойство к вашему CourseService:
public selectedCourse: ICourse;
Затем вы можете получить к нему доступ в обоих компонентах:
this.courseService.selectedCourse;
Проблемы с этим подходом заключаются в том, что затем вам нужно управлять псевдоглобальным состоянием и убедиться, что служба вводится / предоставляется только один раз (в противном случае каждый компонент получит свой собственный экземпляр службы, и вы не сможете обмениваться данными).
Как отмечено в комментарии к вопросу Павана, вам, вероятно, следует использовать Observable и подписаться на него. При использовании упомянутого выше подхода компоненты не будут получать уведомления при изменении значения, и им необходимо будет заранее проверять наличие изменений при загрузке.
как я это сделал? Я использую Observable <ICourse>, когда вызываю getCourse и получаю курс: ICourse в компоненте detail-course. Я хочу, чтобы та же переменная передавалась компоненту курсовой игры
Вы должны добавить новое свойство к CourseService, а затем, когда вы вызываете getCourse, вместо передачи результата любому компоненту, вы должны установить значение на CourseService, поскольку каждый из компонентов использует значение из CourseService, они оба получат значение. Если вы используете Observable, вы должны добавить новый Observable к CourseService и записать на нем .next с результатом getCourse. Каждый из компонентов должен затем .subscribe реагировать на изменения.
вы имеете в виду с темой? как здесь ?: stackoverflow.com/questions/46487255/…
@OfirSasson Ага! Это действительно хороший пример того, как организовать такой тип общения.
хорошо, я попробую, и если у меня будут ошибки, я отредактирую сообщение и комментарий. Спасибо!
@OfirSasson Если у вас возникнут проблемы с новым шаблоном, вам, вероятно, следует опубликовать новый вопрос, а не редактировать этот. Это поможет лучше увидеть ответ
Открыл новый вопрос с некоторыми изменениями: stackoverflow.com/questions/51425917/…
Если у вас есть идентификатор на вашем пути, попробуйте это
import { Component, OnInit} from '@angular/core';
import { ActivatedRoute, Router, Routes, NavigationEnd } from '@angular/router';
import { MatSidenavModule } from '@angular/material/sidenav';
import { ICourse } from '../course';
import { CourseService } from '../course.service';
@Component({
selector: 'lg-course-play-course-play',
templateUrl: './course-play.component.html',
styleUrls: ['./course-play.component.sass']
})
export class CoursePlayComponent implements OnInit {
courseId: number;
errorMessage: string;
private sub: any;
constructor(private courseService: CourseService,
private route: ActivatedRoute,
private router: Router) {
}
ngOnInit() {
const id = +this.route.snapshot.paramMap.get('id');
this.getCourse(id);
}
// Get course detail by id
getCourse(id: number) {
this.courseService.getCourse(id).subscribe(
course => this.course = course,
error => this.errorMessage = <any>error);
}
onBack(): void {
this.router.navigate(['/courses/:id']);
}
}
//inside service
export class LocalService {
obj: BehaviorSubject<any>;
constructor() {
this.obj = new BehaviorSubject(this.obj);
}
loadData(selectedObj: any): void {
this.obj.next(selectedObj);
}
}
//inside component1
const data = {
object_id: 7444,
name: 'Relaince'
};
this.localService.nextCount(data);
//inside component2 - subscribe to obj
this.localService.obj.subscribe(data => {
this.selectedObj = data;
});
// inside component2 html
<div class = "pl-20 color-black font-18 ml-auto">
{{selectedObj?.name}}
<span><b>{{selectedObj?.object_id}}</b></span>
</div>
Когда дело доходит до несвязанных компонентов, мы можем воспользоваться Шаблон посредника.
With the mediator pattern, communication between objects is encapsulated within a mediator object. Objects no longer communicate directly with each other, but instead communicate through the mediator.
Я видел, как термин «несвязанные компоненты» используется для описания компонентов с общим родительским элементом или без него.
Если есть общий родитель (братья и сестры), то родительский элемент может использоваться как посредник. Допустим, у нас есть следующая иерархия компонентов
-Component A
--Component B
--Component C
и мы хотим передать данные от Компонента B к Компоненту C. Способ, которым это будет работать, - это передать данные от Дочернего к Родителю (Компонент B к Компоненту A), а затем передать данные от Родителя к Дочернему (Компонент A к Компоненту B).
Итак, в компоненте B, использующем привязку вывода, с декоратором @Output мы генерируем событие (EventEmitter), а компонент A получает полезную нагрузку этого события. Затем компонент A вызывает обработчик событий, а компонент C получает данные с помощью декоратора @Input.
Иначе (если есть нет общего родителя), мы можем использовать сервис. В этом случае просто внедрите службу в компоненты и подпишитесь на ее события.
Вы можете использовать статические атрибуты (или методы) для обмена данными между несколькими компонентами. Ниже приведен пример:
компонент заголовка:
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss'],
})
export class HeaderComponent {
public static headerTitle: string = "hello world";
}
компонент нижнего колонтитула:
import { HeaderComponent } from '../header/header.component';
@Component({
selector: 'app-footer',
templateUrl: './footer.component.html',
styleUrls: ['./footer.component.scss'],
})
export class FooterComponent {
public footerText: string = HeaderComponent.headerTitle;
}
Хотя использование этого метода вместо создания службы значительно снижает накладные расходы, более сложная логика не рекомендуется. Если логика станет более сложной, вы можете столкнуться с предупреждениями о циклических зависимостях и проблемами связи. Примером этого является то, что для заголовка нужны данные из нижнего колонтитула и наоборот. В случае сложной логики используйте сервис с BehaviorSubject rxjs. Службы должны быть самодостаточными и не должны сильно зависеть от других служб или компонентов. Это исключит возможность появления всех предупреждений о циклической зависимости.
Не могли бы вы сделать рабочий фрагмент на stackblitz.com. Будет легче обнаружить ошибку и помочь