在 Angular 中路由后,对象属性未定义

问题描述 投票:0回答:1

这里是 Angular 的新手。我知道以前有人问过类似的问题,我在这里读了太多关于 AJAX 和异步编程的答案,但无法解决问题。我想在单击“详细信息”按钮时查看产品的详细信息,路由完成其工作,但除了静态 html 之外,页面上看不到任何值。所以这里是:

这是我的详细信息服务,用于获取产品的具体详细信息

@Injectable()
export class DetailsService {

  constructor(private http:HttpClient) { }
  path = "http://localhost:3000/products";

  getProductById(productId: any): Observable<Product> {
    let newPath = this.path
    
    if (productId) {
      newPath += "?id=" + productId;
    }
    console.log(newPath);
    return this.http.get<Product>(newPath).pipe(
      tap(data => console.log(JSON.stringify(data) )),
      catchError(this.handleError)
    );
  }
  //handle error func here

这是详细信息组件

export class DetailsComponent implements OnInit {
  title = 'Product Details'
  productId: any;
  product: Product;

  constructor(private detailsService: DetailsService,
    private activatedRoute: ActivatedRoute,
    private alertifyService: AlertifyService,
  ) {}

  ngOnInit() {

    this.activatedRoute.params.subscribe(params => {
      this.productId = params["id"];
      console.log(this.productId);

      this.detailsService.getProductById(this.productId).subscribe(data => {
        this.product = data;
        console.log(this.product);
        console.log('38')
      });
      console.log(this.productId + " " + typeof this.productId);
    });

}

一切正常,没有任何错误或其他问题,但属性未显示在details.component.html上;

<div *ngIf="product">
    <img class="listing-photo" [src]="product.imageUrl"
      alt="Exterior photo of {{product.name}}"/>
    <section class="listing-description">
      <h2 class="listing-heading">{{product.name}}</h2>
      <p class="listing-location">{{product.description}}, {{product.price}}</p>
    </section>
    <section class="listing-features">
      <h2 class="section-heading">About this housing location</h2>
      <ul>
        <li>Units available: {{product.price}}</li>
        <li>Does this location have wifi: {{product.price}}</li>
        <li>Does this location have laundry: {{product.price}}</li>
      </ul>
    </section>
</div>

调用是从product.component.html进行的

//snippet with ngFor etc
<div class="card-footer">
        <a (click)="addToCart(product)" class="btn btn-primary text-white">Add to cart</a>
        <a [routerLink]="['/products',product.id]" class="btn btn-secondary ml-3 ">Details</a>
      </div>
//the rest

我尝试了一百种方法,如果有两百种的话,我现在迷失了。我了解异步编程的本质,但无法让它按预期工作。

angular angularjs asynchronous routes angular-routing
1个回答
0
投票

我看到路由未接收的问题 由于异步调用,视图发生变化, 所以这里有一个例子。

我使用了官方文档jsonplaceholder作为Api。

这里还有 stackblitz 示例:demo

main.ts

只是样板:

import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';
import { TodoDetailComponent, TodoListComponent } from './todos.service';
import { provideHttpClient } from '@angular/common/http';
import {
  RouterModule,
  Routes,
  provideRouter,
  RouterLink,
  RouterOutlet,
  RouterLinkActive,
} from '@angular/router';
import { CommonModule } from '@angular/common';
import {} from '@angular/router';
@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <h1>Example!</h1>
    <app-todo-list></app-todo-list>
    <router-outlet></router-outlet>
  `,
  imports: [
    CommonModule,
    RouterLink,
    RouterOutlet,
    RouterLinkActive,
    TodoListComponent,
  ],
})
export class App {
  name = 'Angular';
}

const appRoutes: Routes = [
  { path: 'todo/:id', component: TodoDetailComponent },
];

bootstrapApplication(App, {
  providers: [provideRouter(appRoutes), provideHttpClient()],
});

服务、组件

import { AsyncPipe, CommonModule } from '@angular/common';
import { HttpClient } from '@angular/common/http';
import { Component, Injectable, OnInit, inject } from '@angular/core';
import {
  ActivatedRoute,
  RouterLink,
  RouterLinkActive,
  RouterOutlet,
} from '@angular/router';
import { Observable, switchMap } from 'rxjs';

// Interface to manage object
export interface Todo {
  userId: number;
  id: number;
  title: string;
  complete: boolean;
}

// Service to handle data
@Injectable()
export class TodosService {
  private BASE_URL: string = 'https://jsonplaceholder.typicode.com/todos';
  constructor(private http: HttpClient) {}
  getTodos() {
    return this.http.get(`${this.BASE_URL}`);
  }
  getTodoById(id: string): Observable<Todo> {
    return this.http.get<Todo>(`${this.BASE_URL}/${id}`);
  }
}

//Full list of todo items
@Component({
  standalone: true,
  selector: 'app-todo-list',
  template: `
  <ul>
    @for(todo of todos; track todo.id){
      <li>
        <a [routerLink]="['todo',todo.id]" routerLinkActive="active">
          {{todo.title}}
        </a>
     </li>
    } @empty {
      <li>No items.</li>
    }
  </ul>
  `,
  providers: [TodosService],
  imports: [RouterLink, RouterLinkActive],
})
export class TodoListComponent implements OnInit {
  private _todos: Todo[] = [];
  private todoService: TodosService = inject(TodosService);
  ngOnInit() {
    this.todoService.getTodos().subscribe({
      next: (response: any) => {
        this.todos = response.slice(0, 4);
      },
      error: (error: any) => {
        console.error(error);
      },
    });
  }
  get todos() {
    return this._todos;
  }
  set todos(todos: Todo[]) {
    this._todos = todos;
  }
}

/**
 * Detail of todo item passed by route param.
 * @see https://angular.dev/guide/routing/common-router-tasks#accessing-query-parameters-and-fragments
 */
@Component({
  standalone: true,
  selector: 'app-todo-detail',
  template: `
    <a [routerLink]="['/']">Back</a>
    <h2>Todo detail</h2>
    @if( todo$ | async; as todoItem) {
      {{todoItem.id}}
      {{todoItem.title}}
      {{todoItem.complete}}
    }
  `,
  imports: [
    CommonModule,
    RouterLink,
    RouterOutlet,
    RouterLinkActive,
    AsyncPipe,
  ],
  providers: [TodosService],
})
export class TodoDetailComponent {
  todo$!: Observable<Todo>;

  constructor(
    private route: ActivatedRoute,
    private todoService: TodosService
  ) {}

  ngOnInit() {
    this.todo$ = this.route.paramMap.pipe(
      switchMap((params) => {
        let todoId = params.get('id');
        return this.todoService.getTodoById('' + todoId);
      })
    );
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.