Angular17版本集成Quill富文本编辑器

news2024/10/7 6:43:25

Angular17版本集成Quill富文本编辑器

前言:网上找了好多富文本资源,对应Angular17版本的且兼容的太少了,且找到不到对应的版本
自己就去网上找个兼容的免费的富文本组件

1.兼容Angular17版本的quill包

    "@types/quill": "^1.3.10",
    "ngx-quill": "^24.0.0",
    "quill": "^1.3.7",
    "quill-delta": "^5.1.0",
    "quill-image-resize-module": "^3.0.0",
    "rxjs": "^7.5.7",

其中 “rxjs”:需要和 “ngx-quill”: “^24.0.0”,兼容 所以是 ^7.5.7版本

quill-image-resize-module": “^3.0.0”,是图片操作 对图片放大放小

2.引入配置文件

angular.json中需要引入css/js   不在这里引入的话组件会报错
            "scripts": [
              "node_modules/quill/dist/quill.min.js",
              "node_modules/quill-image-resize-module/image-resize.min.js"
            ]
            "styles": [
              "node_modules/driver.js/dist/driver.css",
              "node_modules/quill/dist/quill.snow.css"
            ]

要找到位置添加

封装组件

我是在原有的组件上封装的组件,因为涉及到图片上传,和绑定表单数据所以自己就封装个独立组件方便使用

在这里插入图片描述

其中html

<div id="editor"></div>

样式:less

#editor {
  min-height: 400px;
  max-height: 800px;
  overflow: auto;
}

ts

import { Component, forwardRef, Input, OnInit } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { HttpClient } from '@angular/common/http';
import Quill from 'quill';
import ImageResize from 'quill-image-resize-module';
// 注册ImageResize模块
Quill.register('modules/imageResize', ImageResize);
@Component({
  selector: 'app-qu-editor',
  standalone: true,
  templateUrl: './qu-editor.component.html',
  styleUrls: ['./qu-editor.component.less'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => QuEditorComponent),
      multi: true
    }
  ]
})
export class QuEditorComponent implements OnInit, ControlValueAccessor{
  @Input() uploadDir: string = "pmc/image/project"; // 默认的上传目录

  quillEditor: any;
  editorContent: string = '';

  constructor(private http: HttpClient) {}

  ngOnInit() {
    this.initEditor();
  }

  initEditor() {
    this.quillEditor = new Quill('#editor', {
      theme: 'snow',
      modules: {
        toolbar: [
          [{ 'header': [1, 2, 3, 4, 5, 6, false] }],
          ['bold', 'italic', 'underline', 'strike'],
          [{ 'list': 'ordered'}, { 'list': 'bullet' }],
          [{ 'indent': '-1'}, { 'indent': '+1' }],
          [{ 'size': ['small', false, 'large', 'huge'] }],
          [{ 'color': [] }, { 'background': [] }],
          [{ 'align': [] }],
          ['link', 'image', 'video', 'code-block'],
          ['clean']
        ],
        imageResize: {} // 添加imageResize模块
      },
    });

    this.quillEditor.on('text-change', () => {
      this.onChange(this.quillEditor.root.innerHTML);
    });

    this.quillEditor.getModule('toolbar').addHandler('image', this.imageHandler.bind(this));
  }

  imageHandler() {
    const input = document.createElement('input');
    input.setAttribute('type', 'file');
    input.setAttribute('accept', 'image/*');
    input.click();

    input.onchange = () => {
      const file = input.files[0];
      if (file) {
        const formData = new FormData();
        formData.append('file', file);
        formData.append('dir', this.uploadDir);

        this.http.post<any>('/pmc/upload', formData).subscribe(
            res => {
              if (res.success) {
                const range = this.quillEditor.getSelection();
                this.quillEditor.insertEmbed(range.index, 'image', res.data);
              } else {
                console.error('Image upload failed: ' + res.msg);
              }
            },
            err => {
              console.error('Image upload failed: ' + err.message);
            }
        );
      }
    };
  }

  writeValue(value: any): void {
    this.editorContent = value;
    if (this.quillEditor) {
      this.quillEditor.root.innerHTML = this.editorContent;
    }
  }

  onChange = (value: any) => {};
  onTouched = () => {};

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }
}

使用:

在表单里面使用:

      <nz-form-item>
        <nz-form-label [nzSpan]="4" nzFor="description">产品描述</nz-form-label>
        <nz-form-control [nzSpan]="16" nzHasFeedback nzErrorTip="产品描述">
			  <app-qu-editor formControlName="description">
			  </app-qu-editor>
        </nz-form-control>
      </nz-form-item>

可以通过传值修改图片上传路径

也可以通过[ngModel]绑定值

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1797500.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[Bug]使用Transformers 微调 Whisper出现版本不兼容的bug

错误的现象 ImportError Traceback (most recent call last) <ipython-input-20-6958d7eed552> in () from transformers import Seq2SegTrainingArguments training_args Seq2SeqTrainingArguments( output_dir"./whisper-small-…

解决跨域的几种方法

解决跨域的方法主要有以下几种&#xff1a; 1.CORS&#xff08;跨域资源共享&#xff09; CORS是一种W3C规范&#xff0c;它定义了一种浏览器和服务器交互的方式来确定是否允许跨源请求。 服务器通过设置响应头Access-Control-Allow-Origin来允许或拒绝跨域请求。例如&#xf…

Mintegral解析休闲游戏如何靠创意素材吸引玩家

核心玩法简单清晰、容易让人无限上头的休闲游戏&#xff0c;玩法机制一般比较明确、简单&#xff0c;如果要在短时间内吸引玩家注意&#xff0c;除了完整展示游戏流程以外&#xff0c;开发者需要在素材中设置更多亮点性的内容&#xff0c;如吸睛的剧情、爆炸性的视听效果等元素…

【操作与配置】MySQL安装及启动

【操作与配置】MySQL安装及启动 下载MySQL 进入官网&#xff0c;选择社区版下载 在windows安装 选择不登陆下载 安装MySQL 双击官方安装包 选择“Developer Default”&#xff08;默认&#xff09;即可 Execute&#xff0c;安装完成后next TCP/IP端口等&#xff0c;默认即可…

什么是专业的倾斜摄影轻量化?

眸瑞科技是一家专业从事自研3D可视化技术底层、提供三维模型轻量化服务的高新技术公司&#xff0c;从事该行业近10年&#xff0c;有着丰富的三维模型处理及开发经验。目前已向许多企事业单位提供过工厂厂区、城市地貌、铁路桥梁、高速公路、旅游景区等倾斜摄影模型轻量化处理、…

Alibbaba RocketMQ笔记

作用场景 异步解耦: 将比较耗时且不需要即时(同步)返回结果 的操作放入消息队列; 流量削峰: 历史简介 基本使用 深入了解\原理

【递归、搜索与回溯】搜索

搜索 1.计算布尔二叉树的值2.求根节点到叶节点数字之和3. 二叉树剪枝4.验证二叉搜索树5.二叉搜索树中第K小的元素6.二叉树的所有路径 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#xff0c;我们一…

【算法】深入浅出爬山算法:原理、实现与应用

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

python-小游戏-弹球对决

python-小游戏-弹球对决 需要安装pygame 代码—game-Pong.py import pygame import random# Initialize pygame pygame.init()# Set up the screen WIDTH 600 HEIGHT 400 BALL_RADIUS 20 PAD_WIDTH 10 PAD_HEIGHT 80 WHITE (255, 255, 255) PURPLE (128, 0, 128) RED…

Springboot注意点

1.Usermapper里加param注解 2.RequestParam 和 RequestBody的区别&#xff1a; RequestParam 和 RequestBody的区别&#xff1a; RequestParam 和 RequestBody 是Spring框架中用于处理HTTP请求的两个不同的注 get请求一般用url传参数&#xff0c;所以参数名和参数的值就在ur…

Office(Microsoft 365) 体验

Office 现已更名为 Microsoft 365。 Microsoft 365 是订阅服务&#xff0c;订阅期间可享受定期的功能与安全更新&#xff0c;始终使用的是的最新版本。 Microsoft 365 包含 Word、Excel、PowerPoint 等办公常用套件&#xff0c;还包括了 OneDrive、Exchange 等协作和通信工具…

视觉SLAM十四讲:从理论到实践(Chapter11:回环检测)

前言 学习笔记&#xff0c;仅供学习&#xff0c;不做商用&#xff0c;如有侵权&#xff0c;联系我删除即可 一、主要目标 1.理解回环检测的必要性。 2.掌握基于词袋的外观式回环检测。 3.通过DBoW3的实验&#xff0c;学习词袋模型的实际用途。 二、概述 VO存在误差累积&…

SpringBoot发送邮件带附件的服务如何实现?

SpringBoot发送邮件带附件的步骤&#xff1f;怎么配置邮件服务&#xff1f; SpringBoot作为一款轻量级的Java框架&#xff0c;以其便捷的配置和强大的功能受到开发者的广泛欢迎。AokSend将详细介绍如何在SpringBoot中实现发送带附件的邮件服务。 SpringBoot发送邮件带附件&am…

【全开源】Workerman在线客服系统(ThinkPHP+FastAdmin+Workerman)

Workerman在线客服系统&#xff1a;高效沟通的新选择 基于ThinkPHPFastAdminWorkerman开发的一款实时在线客服系统&#xff0c;支持多客服(不限座席)、知识库、离线留言板、离线消息、历史会话、微信小程序接入、Uni-app接入(高级授权)、用户轨迹等功能。​ &#x1f4e2; 一…

2024年全国青少信息素养大赛图形化编程挑战赛集训第一天编程题分享

大家如果不想阅读前边的比赛内容介绍,可以直接跳过:拉到底部看集训第一天题目 (一)比赛内容: 【小学低年级组】 1、图形化编程软件的使用:熟悉图形化编程软件中舞台区、角色列表区、功能区、脚本编 -3- 辑区的功能及使用。 2、基础功能模块的使用: a.运动模块:角…

day52 123.买卖股票的最佳时机III 188.买卖股票的最佳时机IV

123.买卖股票的最佳时机III 关键在于至多买卖两次&#xff0c;这意味着可以买卖一次&#xff0c;可以买卖两次&#xff0c;也可以不买卖。 动态规划五部曲 1.确定dp数组以及下标的含义 一天一共就有五个状态&#xff0c; 没有操作 &#xff08;其实我们也可以不设置这个状态&a…

Qt 窗口居中显示

Qt 窗口居中显示 引言一、窗体的setGeometry函数二、计算屏幕中心然后move三、借助QRect计算四、补充知识点 引言 窗口居中可以提供良好的视觉效果、突出重点内容、提升用户导航和操作的便利性&#xff0c;有助于改善用户体验。 Qt一般情况下&#xff0c;其Mainwindow或弹出的…

学习笔记——IP地址网络协议——CIDR无类别域间路由

五、CIDR无类别域间路由 1、CIDR的介绍 无类域间路由(Classless Inter Domain Routing&#xff0c;CIDR)也称为&#xff1a;超网(supernetting)由RFC1817定义。CIDR突破了传统IP地址的分类边界&#xff0c;将路由表中的若干条路由汇聚为一条路由&#xff0c;减少了路由表的规…

河南市政环境卫生乙级资质:人员准备要点

河南市政环境卫生乙级资质申请在人员准备方面&#xff0c;需要满足一系列的要求和标准。以下是人员准备的要点&#xff0c;按照清晰、分点的方式进行归纳&#xff1a; 一、注册类工程师 注册二级建筑师&#xff1a;至少1名&#xff0c;负责市政环境卫生工程中的建筑设计及相关…

Vue3【七】setup的语法糖setup简写方法

Vue3【七】setup的语法糖setup简写方法 Vue3【七】setup的语法糖setup简写方法 使用script标签式写法称为setup语法糖 组件名称默认位文件名 export 的内容可以省略 案例截图 案例目录 案例代码 Person.vue <template><div class"person"><h1>我…