如何基于用户单击父组件中的对象,从共享服务中显示子组件中的数据?

时间:2019-11-28 07:26:21

标签: angular

首先,我将从概述开始,以便更轻松地理解我的问题。

我有一个projects页面,其中列出了多个使用Spring Boot RESTcontroller和数据库从后端获取的project对象。每个project可以有多个module对象。

这是Project.ts

import { Module } from './module';

export class Project {

    projectId: number;
    name: string;
    description: string;
    module: Module[];

}

和Module.ts

import { Project } from './project';
import { TestCase } from './test-case';

export class Module {

    moduleId: number;
    project: Project;
    name: string;
    createdBy: string;
    referenceDocument: string;
    testCase: TestCase[];

}

所以,我希望实体结构清晰。

现在,用户可以在项目页面上单击表格行。该点击将获取project对象,并将其发送到另一个组件(searchModules组件),该组件旨在搜索与被点击的modules相关的project。用户单击项目后,URL应更改为searchModules HTML模板,在其中将显示与该项目相关的所有模块。

以下是组件及其HTML模板:

search-modules.component.ts

import { Component, OnInit, Input } from '@angular/core';
import { Module } from '../module';
import { Project } from '../project';
import { ProjectService } from '../project.service';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-search-modules',
  templateUrl: './search-modules.component.html',
  styleUrls: ['./search-modules.component.css']
})
export class SearchModulesComponent implements OnInit {

  projectModules: Module[];
  errorMessage: any;
  public project: Project;

  constructor(private projectService: ProjectService, private http: HttpClient) { }

  ngOnInit() {
  }

  receiveAndSetClickedProject(project: Project) {
    console.log("In Search Component Module: ", this.project);
    this.project = project;
  }

  findModulesForProject(): void {
    console.log("Finding modules for Project: ", this.project, " in SearchModules...")
    this.projectService.findModulesByProject(this.project).subscribe(responseData => {
      this.projectModules = responseData;
      console.log(this.projectModules);
      if (this.projectModules.length == 0)
        alert("This project doesn't have a module yet.")
    }, error => 
    {
      this.errorMessage = error;
      window.alert(this.errorMessage);
    })
  }

}

search-modules.component.html

<div>
  <br />
  <div class="card" (click)="findModulesForProject()">
    <div class="card-header">
      <h4 *ngFor="let module of projectModules"> Modules in Project: {{module.project.name}}</h4>
      <div class="col-md-7 float-right"></div>
      <a [routerLink]="['/modulesForm']" routerLinkActive="router-link-active" class="btn btn-outline-primary"
        style="float: right;"> + New Module</a>
    </div>
    <div class="card-body">
      <table class="table table-bordered table-hover">
        <thead class="thead-dark">
          <tr>
            <th scope="col">Module ID</th>
            <th scope="col">Module Name</th>
            <th scope="col">Project</th>
            <th scope="col">Reference Document</th>
            <th scope="col">Created By</th>
          </tr>
        </thead>
        <tbody>
          <tr *ngFor="let module of projectModules">
            <td>{{module.moduleId}}</td>
            <td>{{module.name}}</td>
            <td>{{module.project.name}}</td>
            <td>{{module.referenceDocument}}</td>
            <td>{{module.createdBy}}</td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>

  <br />
  <br />
</div>
<app-project (sendProject)="receiveAndSetClickedProject($event)"></app-project>

project.component.ts

<div>
  <br />
  <div class="card" (click)="findModulesForProject()">
    <div class="card-header">
      <h4 *ngFor="let module of projectModules"> Modules in Project: {{module.project.name}}</h4>
      <div class="col-md-7 float-right"></div>
      <a [routerLink]="['/modulesForm']" routerLinkActive="router-link-active" class="btn btn-outline-primary"
        style="float: right;"> + New Module</a>
    </div>
    <div class="card-body">
      <table class="table table-bordered table-hover">
        <thead class="thead-dark">
          <tr>
            <th scope="col">Module ID</th>
            <th scope="col">Module Name</th>
            <th scope="col">Project</th>
            <th scope="col">Reference Document</th>
            <th scope="col">Created By</th>
          </tr>
        </thead>
        <tbody>
          <tr *ngFor="let module of projectModules">
            <td>{{module.moduleId}}</td>
            <td>{{module.name}}</td>
            <td>{{module.project.name}}</td>
            <td>{{module.referenceDocument}}</td>
            <td>{{module.createdBy}}</td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>

  <br />
  <br />
</div>
<app-project (sendProject)="receiveAndSetClickedProject($event)"></app-project>

project.component.html

import { Component, OnInit, Output, EventEmitter } from '@angular/core';
import { Project } from '../project';
import { Router } from '@angular/router';
import { ProjectService } from '../project.service';

import { HttpClient } from '@angular/common/http';
import { Module } from '../module';

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

  project: Project = new Project();
  listOfAllProjects: Project[];
  listOfModulesByProject: Module[];
  errorMessage: any;

  constructor(private router: Router, private projectService: ProjectService, private http: HttpClient) { }

  ngOnInit() {
    this.projectService.findAllProjects().subscribe(responseData => 
      {
        this.listOfAllProjects = responseData;
      }, error => 
      {
        this.errorMessage = error;
        window.alert(this.errorMessage);
      })
  }

  updateProject(): void {
    // this should call ProjectFormUpdateComponent's actual updateProject() method
  }

  deleteProject(project:Project): void {
    // Shouldn't use this.project here. In that case it will refer to the null object created in this file.
    // Without this, it refers to the object received from the HTML element.

    this.projectService.deleteProject(project).subscribe(responseData => {
      alert("Project ID: " + project.projectId + " has been deleted successfully.");
    })
  }

  @Output()
  sendProject:EventEmitter<Project> = new EventEmitter<Project>();     // creates the emitter to emit the clicked project.

  onSelectProject(project: Project){
    console.log("In Project Component: ", project);
    this.sendProject.emit(project);                          // emits the project to the search-modules component
    this.router.navigate(['/moduleSearch']);                 //redirects url to search-modules component
  }

}

这是我面临的问题:

  • (click)事件似乎落后一键。第一次单击时,URL更改为searchModules组件,但数据未定义。因此,在我设置的警报中,我收到一个[object Object]提示。 如何使点击同步到用户输入而又不落后一步?
  • searchModules HTML组件中,我必须删除显示的projects表,因为我已经在searchModules中使用了它的选择器来接收$event如何仅将模块保留在该页面上?

这是项目的StackBlitz链接:https://stackblitz.com/edit/angular-uy8mff 。它有点粗糙,因为我以前从未玩过它。

我试图使我的问题尽可能清楚,但是如果仍然不清楚,请要求进一步澄清。

非常感谢您阅读这个巨大的问题。 :)

1 个答案:

答案 0 :(得分:1)

我试图为您制作一个StackBlitz示例。希望可以使问题有所澄清。它仍然非常简化:)

StackBlitz

此外,我在这里将解决方案的主要部分添加为注释中建议的代码段。

  1. 使用额外的参数为searchModule组件添加特定的路线
RouterModule.forRoot([
    { path: 'project-list', component: ProjectListComponent },
    { path: 'module-search/:projectId', component: ModuleSearchComponent },
    { path: '**', redirectTo: 'project-list' }
])  
  1. 在您的projectList组件中,向每个项目条目添加一个routerLink,并将实际的projectId传递给路由器。
<a *ngFor="let project of projects" routerLink="/module-search/{{project.projectId}}">
    {{ project.projectName }}
</a>
  1. 在searchModule组件内部,使用ActivatedRoute对象从当前激活的路由器中获取projectId,并使用它来获取和更新模块列表。
constructor( private route: ActivatedRoute ) {
    this.id = parseInt(this.route.snapshot.paramMap.get('projectId'));
}