简体   繁体   English

数据未从 Angular 中的组件传递

[英]Data is not getting passed from components in Angular

I am trying to pass data between components using the @Input method.我正在尝试使用@Input方法在组件之间传递数据。 For some reason I don't have any errors in console but nothing appears in the component I am trying to pass data from content to header , which is the Header component.出于某种原因,我在控制台中没有任何错误,但我试图将数据从content传递到header的组件中没有任何显示,这是 Header 组件。 I have a home-view component which is constructed of these 3 components:我有一个由以下 3 个组件构成的主视图组件:

HOME-VIEW家景

  • HEADER标题
  • CONTENT内容
  • FOOTER页脚

The data needs to get passed from the content component to the header component.数据需要从内容组件传递到标题组件。 So far I am using the @Input method, but it's not working.到目前为止,我正在使用 @Input 方法,但它不起作用。

Header.component.ts Header.component.ts

import { Component, OnInit, Input } from '@angular/core';
import { faHeadphones} from '@fortawesome/free-solid-svg-icons';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.scss']
})
export class HeaderComponent implements OnInit {

  faHeadphones = faHeadphones;

  @Input()playlist= [];

  constructor() { }

  ngOnInit() {
  }
}

header.html标题.html

<nav class="navbar navbar-dark bg-dark">
  <div class="container">
    <a class="navbar-brand" href="#">
      <fa-icon [icon]="faHeadphones"></fa-icon>
      Navbar
    </a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav"
      aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarNav">
      <ul class="navbar-nav">
        <li class="nav-item active">
          <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
        </li>
        <li class="nav-item">
          <a class="nav-link" href="#">Search</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" href="#">About</a>
        </li>
        <li class="nav-item">
            <a class="nav-link" href="#">{{playlist.length}}</a>
          </li>
      </ul>
    </div>
  </div>
</nav>

content-component.ts内容组件.ts

import { Component, OnInit } from '@angular/core';
import { ApiService } from '../../../services/api.service';
import { FormGroup, FormControl } from '@angular/forms';
import { FormBuilder } from '@angular/forms';
import { faSearch } from '@fortawesome/free-solid-svg-icons';
import { faRedo } from '@fortawesome/free-solid-svg-icons';
import { faHeadphones } from '@fortawesome/free-solid-svg-icons';
import { faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons';
import { faPlus } from '@fortawesome/free-solid-svg-icons';

@Component({
  selector: 'app-content',
  templateUrl: './content.component.html',
  styleUrls: ['./content.component.scss']
})
export class ContentComponent {

  public data = [];
  public playlist = [];
  public apiData: any;
  public results = [];
  public loading = false;
  public noData: any;
  p: number = 1;
  faSearch = faSearch;
  faRedo = faRedo;
  faHeadphones = faHeadphones;
  faExternalLinkAlt = faExternalLinkAlt;
  faPlus = faPlus;

  searchQuery: string = "";
  clickMessage = '';

  constructor(private service: ApiService) { }

  getAll() {
    this.service.getAll(this.searchQuery).subscribe((results) => {
      this.loading = true;
      console.log('Data is received - Result - ', results);
      this.data = results.results;
      this.loading = false;

      if (this.data.length <= 0) {
        this.noData = true;
      } else if (this.data.length >= 1) {
        this.noData = false;
      } else {
        this.noData = false;
      }
    })
  }

  closeAlert() {
    this.noData = false;
  }

  addSongToPlaylist(itunes) {
    this.playlist.push(itunes);
    console.log('Playlist - ', this.playlist);
}

  refresh(): void {
    window.location.reload();
  }

  Search() {
    this.service.getAll(this.searchQuery).subscribe((results) => {
      this.loading = true;
      console.log('Data is received - Result - ', results);
      this.data = results.results;
      this.loading = false;
    })
  }
  ngOnInit() {

  }
}

Home-view component.html主页视图 component.html

<app-header [playlist]="playlist"></app-header>
    <app-content ></app-content>
<app-footer></app-footer>

home-view.ts主页-view.ts

import { Component, OnInit } from '@angular/core';
import { ApiService } from '../../services/api.service';

@Component({
  selector: 'app-home-view',
  templateUrl: './home-view.component.html',
  styleUrls: ['./home-view.component.scss']
})
export class HomeViewComponent implements OnInit {

  public playlist = [];

  constructor(private service: ApiService) { }

  ngOnInit() {
  }

}

If I understood well, those three components are sharing the same array (called playlist).如果我理解得很好,这三个组件共享同一个数组(称为播放列表)。 The thing is that the playlist is being modified inside ContentComponent, and问题是播放列表正在 ContentComponent 中被修改,并且

  1. You're not notifying the parent (HomeViewComponent) about that change,您没有将该更改通知父级 (HomeViewComponent),
  2. The Content and Header components are siblings, so, there's no way they can communicate with simple binding. Content 和 Header 组件是兄弟组件,因此,它们无法通过简单绑定进行通信。

You have two options:您有两个选择:

  1. Move the playlist logic to the parent component and trigger those parent actions through @Output decorators inside the ContentComponent or将播放列表逻辑移动到父组件并通过 ContentComponent 内的 @Output 装饰器触发这些父操作或
  2. Use two way binding with the playlist inside the ContentComponent使用两种方式绑定 ContentComponent 中的播放列表

If you choose the second option you might declare an @Output property like @Output() playlistChange = new EventEmitter() and call this.playlistChange.emit(this.playlist) every time you modify the array inside the ContentComponent, also in your template you might change <app-content></app-content> with <app-content [(playlist)]="playlist"></app-content>如果您选择第二个选项,您可能会声明一个 @Output 属性,例如@Output() playlistChange = new EventEmitter()并在每次修改 ContentComponent 内部的数组时调用this.playlistChange.emit(this.playlist) ,也在您的模板中您可以将<app-content></app-content>更改为<app-content [(playlist)]="playlist"></app-content>

And don't forget to declare the input property like @Input() playlist: any[]并且不要忘记声明输入属性,如@Input() playlist: any[]

Note : In order to use the two way binding, the Output property must named as I specified, I mean, if your property is named x the output property must be named x Change注意:为了使用双向绑定,输出属性必须按照我指定的方式命名,我的意思是,如果您的属性命名为x,则输出属性必须命名为x更改

In app-content component, add event emitterapp-content组件中,添加事件发射器

...
@Output() playlistChange: EventEmitter = new EventEmitter<any>();
...

And emit playlist inside addSongToPlaylist并在addSongToPlaylist发出播放列表

...
addSongToPlaylist(itunes) {
    this.playlist.push(itunes);
    this.playlistChange.emit(this.playlist);
    console.log('Playlist - ', this.playlist);
}
...

Now, change app-content tag to below code in your home-view.html现在,将app-content标签更改为 home-view.html 中的以下代码

...
<app-content (playlistChange)="assignVal($event)"></app-content>
...

Add assignVal method in your home-view.component.在 home-view.component 中添加assignVal 方法。

    assignVal(value){
    this.playlist = value;
}

The easiest way I found was create a service and pass into the constructor of the component:我发现的最简单的方法是创建一个服务并传递给组件的constructor

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

@Injectable({
  providedIn: 'root'
})
export class PlaylistService {

  public playlist = [];

  constructor() { 

  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM