d2-04-search-delete-page

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Day 2 · Step 4 — Search page with capability-based features

Day 2 · Step 4 — 基于能力特性的搜索页面

Note: This skill adapts based on capabilities selected in d2-03-main-menu:
  • If
    search
    is selected: shows list with Edit button
  • If
    delete
    is also selected: adds Delete button with inline confirmation popup
  • If only
    search
    : no Delete button
up-app/src/app/pages/<entity>-search/<entity>-search.component.ts
(standalone component):
  • Use
    signal<Entity[]>([])
    to store the items list; on data fetch, call
    .set()
    to update.
  • On init, call
    <entity>Service.getAll()
    and
    this.items.set(data)
    to update signal.
  • If
    create
    capability enabled: Header bar with an Add button →
    router.navigate(['/<entity>/new'])
    .
  • Add Back button that navigates to home/menu →
    router.navigate(['/'])
    .
  • Per row (use
    @for
    with
    track item.id
    ):
    • Edit button →
      router.navigate(['/<entity>', item.id, 'edit'])
      (always included)
    • Delete button (only if
      delete
      capability selected) → show confirmation popup; if confirmed, call
      <entity>Service.delete(id)
      and update signal with
      .set()
      on success.
Routes: Already defined in d2-03-main-menu (complete app.routes.ts). This skill uses those routes for navigation.
Component TypeScript:
typescript
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { CommonModule } from '@angular/common';
import { signal } from '@angular/core';
import { <Entity>Service, <Entity> } from '../../services/<entity>.service';

@Component({
  selector: 'app-<entity>-search',
  standalone: true,
  imports: [CommonModule],
  templateUrl: './<entity>-search.component.html',
  styleUrls: ['./<entity>-search.component.css']
})
export class <Entity>SearchComponent implements OnInit {
  items = signal<<Entity>[]>([]);
  
  // Capability flags (set based on selections from d2-03)
  hasCreateCapability = true;   // Set to false if 'create' not selected
  hasDeleteCapability = true;   // Set to false if 'delete' not selected

  constructor(
    private router: Router,
    private <entity>Service: <Entity>Service
  ) {}

  ngOnInit() {
    this.<entity>Service.getAll().subscribe(data => {
      this.items.set(data);
    });
  }

  onBack() {
    this.router.navigate(['/']);  // Navigate back to menu/home
  }

  onAdd() {
    this.router.navigate([`/<entity>/new`]);
  }

  onEdit(id: number | undefined) {
    if (id) {
      this.router.navigate(['/<entity>', id, 'edit']);
    }
  }

  onDelete(item: <Entity>) {
    if (confirm(`Delete "<property name from item>"?`)) {
      if (item.id) {
        this.<entity>Service.delete(item.id).subscribe(() => {
          const current = this.items();
          this.items.set(current.filter(i => i.id !== item.id));
        });
      }
    }
  }
}
Template (with flexible columns, Back button, and capability-based Delete):
html
<div class="min-h-screen bg-gray-50">
  <div class="max-w-4xl mx-auto p-6">
    <!-- Header -->
    <div class="flex justify-between items-center mb-6">
      <div class="flex items-center gap-4">
        <button
          (click)="onBack()"
          class="bg-gray-500 hover:bg-gray-600 text-white font-semibold px-4 py-2 rounded-lg transition"
        >
          ← Back
        </button>
        <h1 class="text-4xl font-bold text-gray-900"><Entity> List</h1>
      </div>
      @if (hasCreateCapability) {
        <button
          (click)="onAdd()"
          class="bg-green-500 hover:bg-green-600 text-white font-semibold px-6 py-3 rounded-lg transition shadow-md"
        >
          + Add New
        </button>
      }
    </div>

    <!-- Table -->
    <div class="bg-white rounded-lg shadow-md overflow-hidden">
      <div class="overflow-x-auto">
        <table class="min-w-full">
          <thead>
            <tr class="bg-gray-100 border-b border-gray-200">
              <!-- Replace with your entity's columns, e.g.: -->
              <th class="px-6 py-3 text-left font-semibold text-gray-700">Code</th>
              <th class="px-6 py-3 text-left font-semibold text-gray-700">Name</th>
              <th class="px-6 py-3 text-left font-semibold text-gray-700">Address</th>
              <th class="px-6 py-3 text-left font-semibold text-gray-700">Phone</th>
              <th class="px-6 py-3 text-center font-semibold text-gray-700">Actions</th>
            </tr>
          </thead>
          <tbody>
            @for (item of items(); track item.id) {
              <tr class="border-b border-gray-100 hover:bg-gray-50 transition">
                <!-- Replace with your entity's properties, e.g.: -->
                <td class="px-6 py-3 text-gray-900 font-medium">{{ item.empCode }}</td>
                <td class="px-6 py-3 text-gray-900">{{ item.empName }}</td>
                <td class="px-6 py-3 text-gray-700">{{ item.address || "-" }}</td>
                <td class="px-6 py-3 text-gray-700">{{ item.phoneNo || "-" }}</td>
                <td class="px-6 py-3 text-center">
                  <button
                    (click)="onEdit(item.id)"
                    class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg mr-2 transition text-sm font-semibold"
                  >
                    Edit
                  </button>
                  @if (hasDeleteCapability) {
                    <button
                      (click)="onDelete(item)"
                      class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg transition text-sm font-semibold"
                    >
                      Delete
                    </button>
                  }
                </td>
              </tr>
            }
          </tbody>
        </table>
      </div>

      @if (items().length === 0) {
        <div class="text-center py-8 text-gray-600">
          <p class="text-lg">No <entity> found. 
            @if (hasCreateCapability) {
              <a href="javascript:" (click)="onAdd()" class="text-blue-500 hover:underline">Create one</a>.
            } @else {
              Contact your administrator to create records.
            }
          </p>
        </div>
      }
    </div>
  </div>
</div>
Column Customization:
  • Replace
    empCode
    ,
    empName
    ,
    address
    ,
    phoneNo
    with your entity's actual property names
  • Add/remove table header
    <th>
    and table data
    <td>
    columns to match your entity structure
  • Use
    || "-"
    to display dashes for empty/null optional fields
Capability Flags:
  • Set
    hasCreateCapability
    and
    hasDeleteCapability
    based on selections from d2-03
  • If
    false
    , the Add and Delete buttons won't appear
  • Adapt buttons dynamically to your CRUD requirements
注意: 本技能会根据d2-03-main-menu中选择的能力进行适配:
  • 若选择
    search
    :显示带编辑按钮的列表
  • 若同时选择
    delete
    :添加带内嵌确认弹窗的删除按钮
  • 若仅选择
    search
    :不显示删除按钮
up-app/src/app/pages/<entity>-search/<entity>-search.component.ts
(独立组件):
  • 使用
    signal<Entity[]>([])
    存储条目列表;获取数据时,调用
    .set()
    更新。
  • 初始化时,调用
    <entity>Service.getAll()
    并通过
    this.items.set(data)
    更新信号。
  • 若启用
    create
    能力:头部栏添加新增按钮 → 触发
    router.navigate(['/<entity>/new'])
  • 添加返回按钮,导航至首页/菜单 →
    router.navigate(['/'])
  • 每行(使用
    @for
    并配合
    track item.id
    ):
    • 编辑按钮 → 触发
      router.navigate(['/<entity>', item.id, 'edit'])
      (始终显示)
    • 删除按钮(仅当选择
      delete
      能力时显示)→ 显示确认弹窗;确认后,调用
      <entity>Service.delete(id)
      ,成功后通过
      .set()
      更新信号。
路由: 已在d2-03-main-menu中定义(完整的app.routes.ts)。本技能使用这些路由进行导航。
组件TypeScript代码:
typescript
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { CommonModule } from '@angular/common';
import { signal } from '@angular/core';
import { <Entity>Service, <Entity> } from '../../services/<entity>.service';

@Component({
  selector: 'app-<entity>-search',
  standalone: true,
  imports: [CommonModule],
  templateUrl: './<entity>-search.component.html',
  styleUrls: ['./<entity>-search.component.css']
})
export class <Entity>SearchComponent implements OnInit {
  items = signal<<Entity>[]>([]);
  
  // 能力标识(根据d2-03的选择设置)
  hasCreateCapability = true;   // 若未选择'create'则设为false
  hasDeleteCapability = true;   // 若未选择'delete'则设为false

  constructor(
    private router: Router,
    private <entity>Service: <Entity>Service
  ) {}

  ngOnInit() {
    this.<entity>Service.getAll().subscribe(data => {
      this.items.set(data);
    });
  }

  onBack() {
    this.router.navigate(['/']);  // 导航回菜单/首页
  }

  onAdd() {
    this.router.navigate([`/<entity>/new`]);
  }

  onEdit(id: number | undefined) {
    if (id) {
      this.router.navigate(['/<entity>', id, 'edit']);
    }
  }

  onDelete(item: <Entity>) {
    if (confirm(`Delete "<property name from item>"?`)) {
      if (item.id) {
        this.<entity>Service.delete(item.id).subscribe(() => {
          const current = this.items();
          this.items.set(current.filter(i => i.id !== item.id));
        });
      }
    }
  }
}
模板(带灵活列、返回按钮及基于能力的删除功能):
html
<div class="min-h-screen bg-gray-50">
  <div class="max-w-4xl mx-auto p-6">
    <!-- 头部 -->
    <div class="flex justify-between items-center mb-6">
      <div class="flex items-center gap-4">
        <button
          (click)="onBack()"
          class="bg-gray-500 hover:bg-gray-600 text-white font-semibold px-4 py-2 rounded-lg transition"
        >
          ← 返回
        </button>
        <h1 class="text-4xl font-bold text-gray-900"><Entity> 列表</h1>
      </div>
      @if (hasCreateCapability) {
        <button
          (click)="onAdd()"
          class="bg-green-500 hover:bg-green-600 text-white font-semibold px-6 py-3 rounded-lg transition shadow-md"
        >
          + 新增
        </button>
      }
    </div>

    <!-- 表格 -->
    <div class="bg-white rounded-lg shadow-md overflow-hidden">
      <div class="overflow-x-auto">
        <table class="min-w-full">
          <thead>
            <tr class="bg-gray-100 border-b border-gray-200">
              <!-- 替换为你的实体列,例如: -->
              <th class="px-6 py-3 text-left font-semibold text-gray-700">编码</th>
              <th class="px-6 py-3 text-left font-semibold text-gray-700">名称</th>
              <th class="px-6 py-3 text-left font-semibold text-gray-700">地址</th>
              <th class="px-6 py-3 text-left font-semibold text-gray-700">电话</th>
              <th class="px-6 py-3 text-center font-semibold text-gray-700">操作</th>
            </tr>
          </thead>
          <tbody>
            @for (item of items(); track item.id) {
              <tr class="border-b border-gray-100 hover:bg-gray-50 transition">
                <!-- 替换为你的实体属性,例如: -->
                <td class="px-6 py-3 text-gray-900 font-medium">{{ item.empCode }}</td>
                <td class="px-6 py-3 text-gray-900">{{ item.empName }}</td>
                <td class="px-6 py-3 text-gray-700">{{ item.address || "-" }}</td>
                <td class="px-6 py-3 text-gray-700">{{ item.phoneNo || "-" }}</td>
                <td class="px-6 py-3 text-center">
                  <button
                    (click)="onEdit(item.id)"
                    class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg mr-2 transition text-sm font-semibold"
                  >
                    编辑
                  </button>
                  @if (hasDeleteCapability) {
                    <button
                      (click)="onDelete(item)"
                      class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg transition text-sm font-semibold"
                    >
                      删除
                    </button>
                  }
                </td>
              </tr>
            }
          </tbody>
        </table>
      </div>

      @if (items().length === 0) {
        <div class="text-center py-8 text-gray-600">
          <p class="text-lg">未找到<entity>            @if (hasCreateCapability) {
              <a href="javascript:" (click)="onAdd()" class="text-blue-500 hover:underline">创建一个</a>            } @else {
              联系管理员创建记录。
            }
          </p>
        </div>
      }
    </div>
  </div>
</div>
列自定义:
  • empCode
    empName
    address
    phoneNo
    替换为你的实体实际属性名
  • 添加/移除表头
    <th>
    和表数据
    <td>
    列,匹配你的实体结构
  • 使用
    || "-"
    为空/可选字段显示短横线
能力标识:
  • 根据d2-03的选择设置
    hasCreateCapability
    hasDeleteCapability
  • 若设为
    false
    ,新增和删除按钮将不会显示
  • 根据你的CRUD需求动态适配按钮