angular使用IndexedDb实现增删改查sql

news/2025/2/27 8:30:44

说明:我听说前端有一款数据库,叫IndexedDb数据库,可以存储超大的文件和数据,大约有250M,有了这个,就可以在浏览器里面,存储超大的数据,
事实上IndexedDb存储的数据,存在浏览器文件夹的c盘,缓存浏览器的文件夹里面,只要你存了数据,用户关闭浏览器,再次打开,数据依然还在,可以加载,用来做缓存,非常方便
因此,我就计划用angular框架,使用indexdb技术,先写一个简单的增删改查的小demo,用来存储用户信息,弄了一上午,总算可以用了,下面是详细步骤和效果图

效果图:
在这里插入图片描述

step0:用npm安装IndexedDb

npm install ngx-indexed-db 

step1:IndexedDb安装成功后,在 package.json可以查看安装的IndexedDb版本19.3.3

 "@angular/animations": "^19.1.0",
    "@angular/cdk": "^19.1.5",
    "@angular/common": "^19.1.0",
    "@angular/compiler": "^19.1.0",
    "@angular/core": "^19.1.0",
    "@angular/forms": "^19.1.0",
    "@angular/material": "^19.1.5",
    "@angular/platform-browser": "^19.1.0",
    "@angular/platform-browser-dynamic": "^19.1.0",
    "@angular/router": "^19.1.0",
    "ngx-indexed-db": "^19.3.3",
    "rxjs": "~7.8.0",
    "tslib": "^2.3.0",
    "zone.js": "~0.15.0"

step2:接下来需要在angular项目中,导入IndexedDb
新建一个文件,文件名和具体路径是:C:\Users\Administrator\WebstormProjects\untitled4\src\app\db.config.ts

import { DBConfig } from 'ngx-indexed-db';

export const DB_CONFIG: DBConfig = {
  name: 'Angular19DB',
  version: 2,
  objectStoresMeta: [{
    store: 'tasks',
    storeConfig: {
      keyPath: 'id',
      autoIncrement: true
    },
    storeSchema: [
      { name: 'title', keypath: 'title', options: { unique: false } },
      { name: 'status', keypath: 'status', options: { unique: false } }
    ]
  }]
};

step3:然后就是注册,
文件名和具体路径是:C:\Users\Administrator\WebstormProjects\untitled4\src\app\app.config.ts

import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './app.routes';
import { provideIndexedDb } from 'ngx-indexed-db';
import { DB_CONFIG } from './db.config';

import { provideAnimations } from '@angular/platform-browser/animations';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';



export const appConfig: ApplicationConfig = {
  providers: [provideIndexedDb(DB_CONFIG),provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes),provideAnimations(), provideAnimationsAsync()]
};

step4:路由这些,不用我多说了吧,新建一个模块,添加到路由里面,然后就直接开整,

 ng generate component student

step5:C:\Users\Administrator\WebstormProjects\untitled4\src\app\student\student.component.ts

import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
import { DatabaseService } from './data.service';
import { FormsModule } from '@angular/forms';
import { NgForOf } from '@angular/common';
import { Task } from './task.model';// 从统一定义文件导入

@Component({
  selector: 'app-task',
  imports: [FormsModule, NgForOf],
  templateUrl: './student.component.html',
  styleUrl: './student.component.css'
})

export class StudentComponent  implements OnInit {
  tasks: Task[] = [];
  newTask: Omit<Task, 'id'> = {
    title: '',
    status: 'pending'
  };

  constructor(
    private db: DatabaseService,
    private cdr: ChangeDetectorRef
  ) {}

  async ngOnInit() {
    await this.loadTasks();
  }

  async loadTasks() {
    // 添加类型断言确保数据一致性
    this.tasks = await this.db.getAllTasks() as Task[];
    this.cdr.detectChanges();
  }

  async addTask() {
    if (this.newTask.title.trim()) {
      // 类型安全传递
      await this.db.addTask(this.newTask);
      this.newTask = { title: '', status: 'pending' };
      await this.loadTasks();
    }
  }

  async updateTask(task: Task) {
    // 添加空值检查
    if (task.id === undefined) return;

    await this.db.updateTask(task);
    await this.loadTasks();
  }

  async deleteTask(id: number) {
    await this.db.deleteTask(id);
    await this.loadTasks();
  }
}

step6:C:\Users\Administrator\WebstormProjects\untitled4\src\app\student\task.model.ts

// src/app/models/task.model.ts
export interface Task {
  id?: number;  // 将id改为可选属性
  title: string;
  status: 'pending' | 'completed';
}

step7:C:\Users\Administrator\WebstormProjects\untitled4\src\app\student\data.service.ts

import { Injectable } from '@angular/core';
import { Task } from './task.model';

@Injectable({ providedIn: 'root' })
export class DatabaseService {
  private db!: IDBDatabase;
  private readonly dbName = 'AngularDB';
  private readonly storeName = 'tasks';

  // 添加数据库准备状态追踪
  private dbReady: Promise<void>;

  constructor() {
    this.dbReady = this.initializeDB();
  }

  private async initializeDB(): Promise<void> {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(this.dbName, 1);

      request.onupgradeneeded = (event) => {
        this.db = (event.target as IDBOpenDBRequest).result;
        if (!this.db.objectStoreNames.contains(this.storeName)) {
          const store = this.db.createObjectStore(this.storeName, {
            keyPath: 'id',
            autoIncrement: true
          });
          store.createIndex('title', 'title', { unique: false });
        }
      };

      request.onsuccess = (event) => {
        this.db = (event.target as IDBOpenDBRequest).result;
        resolve();
      };

      request.onerror = (event) => {
        reject((event.target as IDBRequest).error);
      };
    });
  }

  // 所有方法添加等待初始化
  async getAllTasks(): Promise<Task[]> {
    await this.dbReady;
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction(this.storeName, 'readonly');
      const store = transaction.objectStore(this.storeName);
      const request = store.getAll();

      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  async addTask(task: Omit<Task, 'id'>): Promise<number> {
    await this.dbReady;
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction(this.storeName, 'readwrite');
      const store = transaction.objectStore(this.storeName);
      const request = store.add(task);

      request.onsuccess = () => resolve(request.result as number);
      request.onerror = () => reject(request.error);
    });
  }

  async updateTask(task: Task): Promise<void> {
    await this.dbReady;
    if (task.id === undefined) {
      throw new Error('Cannot update task without ID');
    }

    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction(this.storeName, 'readwrite');
      const store = transaction.objectStore(this.storeName);
      const request = store.put(task);

      request.onsuccess = () => resolve();
      request.onerror = () => reject(request.error);
    });
  }

  async deleteTask(id: number): Promise<void> {
    await this.dbReady;
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction(this.storeName, 'readwrite');
      const store = transaction.objectStore(this.storeName);
      const request = store.delete(id);

      request.onsuccess = () => resolve();
      request.onerror = () => reject(request.error);
    });
  }
}

step8:C:\Users\Administrator\WebstormProjects\untitled4\src\app\student\student.component.html

<div class="task-manager">
  <div class="task-header">
    <input
      [(ngModel)]="newTask.title"
      placeholder="Enter task title"
      class="task-input"
      (keyup.enter)="addTask()"
    >
    <button
      class="primary-btn"
      (click)="addTask()"
    >
      + Add Task
    </button>
  </div>

  <div class="task-list">
    <div *ngFor="let task of tasks" class="task-item">
      <input
        [(ngModel)]="task.title"
        class="task-input"
      >
      <select
        [(ngModel)]="task.status"
        (change)="updateTask(task)"
        class="status-select"
      >
        <option value="pending">⏳ Pending</option>
        <option value="completed">✅ Completed</option>
      </select>
      <button
        class="danger-btn"
        (click)="deleteTask(task.id!)"
      >
        🗑 Delete
      </button>
    </div>
  </div>
</div>

step9:C:\Users\Administrator\WebstormProjects\untitled4\src\app\student\student.component.css

/* 现代配色方案 */
.task-manager {
  max-width: 800px;
  margin: 2rem auto;
  padding: 2rem;
  background: white;
  border-radius: 12px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.05);
}

.task-header {
  display: flex;
  gap: 1rem;
  margin-bottom: 2rem;
}

.task-input {
  flex: 1;
  padding: 12px;
  border: 2px solid #e9ecef;
  border-radius: 8px;
  font-size: 16px;
  transition: border-color 0.3s ease;
}
.task-input:focus {
  outline: none;
  border-color: #4a90e2;
  box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.1);
}

.primary-btn {
  padding: 10px 20px;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  font-weight: 500;
  transition: all 0.3s ease;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: linear-gradient(135deg, #4a90e2 0%, #2d7ff9 100%);
  color: white;
}
.primary-btn:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  background: linear-gradient(135deg, #3d85d6 0%, #2770e0 100%);
}
.primary-btn:active {
  transform: translateY(0);
}

.danger-btn {
  padding: 10px 20px;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  font-weight: 500;
  transition: all 0.3s ease;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: linear-gradient(135deg, #ff4757 0%, #ff6b81 100%);
  color: white;
}
.danger-btn:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.danger-btn:active {
  transform: translateY(0);
}

.task-list {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.task-item {
  display: flex;
  gap: 1rem;
  align-items: center;
  padding: 1.5rem;
  background: #f8f9fa;
  border-radius: 8px;
  transition: transform 0.3s ease;
}
.task-item:hover {
  transform: translateX(5px);
}

.status-select {
  padding: 8px 12px;
  border: 2px solid #e9ecef;
  border-radius: 6px;
  background: white;
  cursor: pointer;
  transition: all 0.3s ease;
}
.status-select:focus {
  outline: none;
  border-color: #4a90e2;
}

直接点击运行,然后success

end


http://www.niftyadmin.cn/n/5869804.html

相关文章

【热力图 Heatmap】——1

🌟 解锁数据可视化的魔法钥匙 —— pyecharts实战指南 🌟 在这个数据为王的时代,每一次点击、每一次交易、每一份报告背后都隐藏着无尽的故事与洞察。但你是否曾苦恼于如何将这些冰冷的数据转化为直观、吸引人的视觉盛宴? 🔥 欢迎来到《pyecharts图形绘制大师班》 �…

【Web安全】图片验证码DOS漏洞

文章目录 免责声明一、漏洞原理二、测试步骤三、测试案例四、修复方式免责声明 在网络安全领域,技术文章应谨慎使用,遵守法律法规,严禁非法网络活动。未经授权,不得利用文中信息进行入侵,造成的任何后果,由使用者自行承担,本文作者不负责。提供的工具仅限学习使用,严禁…

前端依赖nrm镜像管理工具

npm 默认镜像 &#xff1a;https://registry.npmjs.org/ 1、安装 nrm npm install nrm --global2、查看镜像源列表 nrm ls3、测试当前环境下&#xff0c;哪个镜像源速度最快。 nrm test4、 切换镜像源 npm config get registry # 查看当前镜像源 nrm use taobao # 等价于 npm…

使用 Apache Dubbo 释放 DeepSeek R1 的全部潜力

作者&#xff1a;陈子康&#xff0c;Apache Dubbo Contributor 2025年1月20日&#xff0c;国产大模型公司深度求索&#xff08;DeepSeek&#xff09;正式发布了大语言模型 DeepSeek-R1&#xff0c;并同步开源其模型权重。通过大规模强化学习技术&#xff0c;DeepSeek-R1 显著提…

vue3 封装通用 ECharts 组件

我在项目中需要用到很多的图标&#xff0c;比如折线、饼图、柱状、关系等各种图标&#xff0c;我又比较懒&#xff0c;所以就封了一个基本的组件库&#xff0c;只需要传递options和canvasId&#xff0c;基本就可以了&#xff0c;代码如下&#xff1a; <template><d…

【人工智能顶刊合集】CCF-A/B/C类推荐所有期刊目录,中科院1区审稿极速,81天录用!

本期盘点【人工智能】领域CCF-A/B/C类中科院1-2区期刊最新影响因子、分区、审稿周期参考&#xff01; CCF-A类 Artificial Intelligence • 影响因子&#xff1a;5.1 • 期刊分区&#xff1a;JCR1区&#xff0c;中科院2区 • 年发文量&#xff1a;126 • 自引率&#xff1…

Python Cookbook-2.13 使用C++的类iostream语法

任务 C的基于 ostream 和操纵符(插入了这种特定的对象后,它会在 stream 中产生特定的效果)的 I/O方式&#xff0c;并想将此形式用在自己的Python 程序中。 解决方案 Python 允许使用对特殊方法(即名字前后带有连续两个下划线的方法)进行了重定义的类来重载原有的操作符。为了…

半导体晶圆精控:ethercat转profient网关数据提升制造精度

数据采集系统通过网关连接离子注入机&#xff0c;精细控制半导体晶圆制造过程中的关键参数。 在半导体制造中&#xff0c;晶圆制造设备的精密控制是决定产品性能的关键因素。某半导体工厂采用耐达讯Profinet转EtherCAT协议网关NY-PN-ECATM&#xff0c;将其数据采集系统与离子注…