Связь между дочерним и родительским компонентом в Angular 8 - PullRequest
3 голосов
/ 14 октября 2019

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

Вот мой дочерний компонент ts

import { Component, OnInit, Input, ViewChild, ElementRef, Output, EventEmitter } from '@angular/core';
import { Namespace } from '../../../models/Namespace';
import { Schema } from '../../../models/Schema'
import { ApiNamespaceService } from '../../../Services/api-namespace.service'
import { Router } from '@angular/router';

@Component({
  selector: 'add-namespace',
  templateUrl: './add-namespace.component.html',
  styleUrls: ['./add-namespace.component.css']
})
export class AddNamespaceComponent implements OnInit {

newNamespace = new Namespace();
selectedSchema = new Schema();
schemas: Schema[];
@Input() namespaces: Namespace[];
@ViewChild('alert', { static: true }) alert: ElementRef;
@ViewChild("nameInput", { static: false }) nameInputRef: ElementRef;
success = false;
@Output() public namespaceCreated = new EventEmitter<Namespace[]>();

constructor(private namespaceService: ApiNamespaceService, private router: Router) {
    this.schemas = [{ Name: this.selectedSchema.Name, SchemaData: this.selectedSchema.SchemaData, Id: this.selectedSchema.Id }];
}

ngOnInit() {
    this.getSchemas();
}

getSchemas(): void {
    this.namespaceService.getAllSchemas().subscribe(schemas => this.schemas = schemas);
}

createNamespace(): void {
    if (this.nameInputRef.nativeElement.value != '') {
        this.newNamespace.SchemaId = this.selectedSchema.Id;
        this.newNamespace.Roles = [];
        this.namespaceService
            .createNamespace(this.newNamespace)
            .subscribe(nmsp => this.namespaces.push(nmsp));
        this.success = true;
    }
    this.namespaceCreated.emit(this.namespaces);
    this.router.navigateByUrl('main-view/namespaces');
}

createName(): void {
    if (this.nameInputRef.nativeElement.value === '') {
        this.newNamespace.Name = 'Null';
    }
}

closeAlert() {
    this.success = false;
}

Здесь я вызываю функцию в родительском HTML

<div *ngIf="shouldOpen" class="col-6">
    <add-namespace [namespaces]="namespaces" (namespaceCreated)="showNewNamespace($event)"></add-namespace>
</div>

А вот родительский компонент ts

import { Component, OnInit, ViewChild } from '@angular/core';
import { ApiNamespaceService } from '../../Services/api-namespace.service'

import { Namespace } from '../../models/Namespace'
import { EditNamespaceComponent } from './edit-namespace/edit-namespace.component';
import { Router } from '@angular/router';

@Component({
  selector: 'namespaces',
  templateUrl: './namespaces.component.html',
  styleUrls: ['./namespaces.component.css']
})
export class NamespacesComponent implements OnInit {

namespaces: Namespace[];
selectedNamespace: Namespace;
shouldOpen = false;
query: string;
filteredNamespaces: Namespace[];

constructor(private namespaceService: ApiNamespaceService, private router: Router) { }

ngOnInit() {
    this.getNamespaces();
}

getNamespaces(): void {
    this.namespaceService.getAllNamespaces().subscribe(namespaces => {
        this.namespaces = namespaces;
        this.filteredNamespaces = namespaces;
        this.search();
    });
}

openAddNamespace() {
    this.shouldOpen = true;
}

openNamespace(namespace: Namespace) {
    this.shouldOpen = false;
    this.selectedNamespace = namespace;
    this.router.navigateByUrl('main-view/namespaces/' + this.selectedNamespace.Id);
}

search() {
    if (!this.query || !this.query.trim()) {
        this.filteredNamespaces = this.namespaces;
        return;
    }

    this.filteredNamespaces = this.namespaces.filter(namespace => namespace.Name.toLowerCase()
        .includes(this.query.toLowerCase().trim()));
}

showNewNamespace($event) {
    this.namespaces = $event;
}    

Ответы [ 3 ]

0 голосов
/ 14 октября 2019

Вы можете использовать Subject для этого. Вот ссылка на мой блог для вашей ссылки

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

@Injectable()
export class MessageService {
    private subject = new Subject<any>();

    constructor() {}

    sendMessage(message: any) {
        this.subject.next(message);
    }

    getData() {
        return this.subject.asObservable();
    }
}

Я определил 2 метода здесь. Первый метод, использующий next () для отправки сообщения следующему подписчику. Так что в вашем компоненте вам просто нужно просто подписаться, чтобы получить данные

private subscription$: Subscription;

public ngOnInit(): void {
  this.subscription$ = this.messageervice
            .getData()
            .subscribe(data => { console.log(data); })
}

public ngOnDestroy(): void {
    this.subscription$.unsubscribe();
}
0 голосов
/ 15 октября 2019

Я попытался использовать @Output и источники событий (плюс изменил мой проект), и это сработало. Оказывается, мне не нужно было добавлять тип к источнику событий. (У меня было @Output() public namespaceCreated = new EventEmitter<Namespace[]>();). Я выложу код, если кому-то интересно. Кстати, проект был значительно изменен, тем не менее, мне все еще нужно было общаться от дочернего к родительскому компоненту.

Вот дочерний компонент ts

@Output() savedChanges = new EventEmitter(); 

onChange() {
    this.savedChanges.next(false);
}

Вот дочерний компонент html

<div class="row">
<label for="schema">Schema</label>
<select class="custom-select" [(ngModel)]="selectedNamespace.SchemaId" (ngModelChange)="onChange()" (change)="onSelectSchema($event.target.value)">
    <option *ngFor="let schema of schemas" [value]="schema.Id">
        {{ schema.Name }}
    </option>
</select>

Вот родительский компонент html

<div class="container">
<div class="row">
    <div class="col-6">

        <div *ngIf="!namespaces" class="d-flex justify-content-center">
            <div class="spinner-border" role="status"></div>
        </div>

        <div *ngIf="namespaces">
            <ngx-table style="cursor: pointer"
                       [data]="namespaces"
                       [configuration]="configuration"
                       [columns]="columns"
                       (event)="eventEmitted($event)">
            </ngx-table>
            <button class="btn btn-outline-info btn-sm float-right" (click)="selectNewNamespace()">
                <i class="fas fa-plus mr-1"></i>
                {{ 'General.Add' | translate }}
            </button>
        </div>

    </div>

    <div *ngIf="selectedNamespace" class="col-6">
        <edit-namespace [selectedNamespace]="selectedNamespace" [namespaces]="namespaces" [isNew]="isNew" (savedChanges)="emitSavedChanges($event)"></edit-namespace>
    </div>
</div>

Вот родительский компонент ts

@Output() savedChanges = new EventEmitter(); 

eventEmitted($event): void {
if ($event.event === "onClick" && this.savedChanges) { 
    this.isNew = false;
    this.selectedNamespace = $event.value.row;
    this.router.navigateByUrl('main-view/namespaces/' + this.selectedNamespace.Id);
    }
}

emitSavedChanges(savedChanges: boolean) {
    this.savedChanges.next(savedChanges);
}
0 голосов
/ 14 октября 2019

Да, есть. Вы можете создать «сервис» и иметь многоадресную наблюдаемую / субъектную тему (например, BehavioralSubject <>), выдвигающую новые значения.

Вот хорошая отправная точка (кстати, проверьте и другие методы):

Родители и дети общаются через службу

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...