d2-04-search-delete-page
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDay 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 is selected: shows list with Edit button
search - If is also selected: adds Delete button with inline confirmation popup
delete - If only : no Delete button
search
up-app/src/app/pages/<entity>-search/<entity>-search.component.ts- Use to store the items list; on data fetch, call
signal<Entity[]>([])to update..set() - On init, call and
<entity>Service.getAll()to update signal.this.items.set(data) - If capability enabled: Header bar with an Add button →
create.router.navigate(['/<entity>/new']) - Add Back button that navigates to home/menu → .
router.navigate(['/']) - Per row (use with
@for):track item.id- Edit button → (always included)
router.navigate(['/<entity>', item.id, 'edit']) - Delete button (only if capability selected) → show confirmation popup; if confirmed, call
deleteand update signal with<entity>Service.delete(id)on success..set()
- Edit button →
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,addresswith your entity's actual property namesphoneNo - Add/remove table header and table data
<th>columns to match your entity structure<td> - Use to display dashes for empty/null optional fields
|| "-"
Capability Flags:
- Set and
hasCreateCapabilitybased on selections from d2-03hasDeleteCapability - If , the Add and Delete buttons won't appear
false - 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的选择设置和
hasCreateCapabilityhasDeleteCapability - 若设为,新增和删除按钮将不会显示
false - 根据你的CRUD需求动态适配按钮