Troubleshooting error in Angular 5 with QuillJS: "Parchment issue - Quill unable to

I've been working with the primeng editor and everything seems fine with the editor itself. However, I've spent the last two days struggling to extend a standard block for a custom tag. The official documentation suggests using the quilljs API for additional features.

Despite checking all APIs and GitHub issues, it appears that I'm on the right track but I keep encountering this pesky error:

ERROR Error: [Parchment] Unable to create marker blot
at new ParchmentError (scripts.bundle.js:148)
at Object.create (scripts.bundle.js:178)
at BlockBlot.insertAt (scripts.bundle.js:7323)
at Block.insertAt (scripts.bundle.js:855)
at Scroll.ContainerBlot.insertAt (scripts.bundle.js:3404)
at ScrollBlot.insertAt (scripts.bundle.js:7060)
at Scroll.insertAt (scripts.bundle.js:4252)
at Editor.insertEmbed (scripts.bundle.js:2606)
at scripts.bundle.js:1379
at Quill.modify (scripts.bundle.js:1610)

My goal is to add a custom tag with non-editable content inside. Here's a snippet of my code:

...
import {Editor} from 'primeng/editor';

import * as Quill from 'quill';
import * as Parchment from 'parchment';
const Block = Quill.import('blots/block/embed');
class BlockEmbed extends Parchment.default.Embed {}
BlockEmbed.prototype = Block.prototype;

export class Variable extends BlockEmbed {

  static blotName = 'marker';
  static tagName = 'marker';

  static create(value: any) {
    console.log(value);
    const node = (super.create(value) as any);
    node.innerHTML = '<span contenteditable=false>' + value + '</span>';
    node.setAttribute('contenteditable', false);
    return node;
  }

}

Variable.blotName = 'marker';
Variable.tagName = 'marker';

Quill.register('formats/marker', Variable);

@Component({
  selector: 'manager',
  templateUrl: './manager.component.html',
  styleUrls: ['./manager.component.css']
})

export class ManagerComponent implements OnInit, AfterViewInit {

   private quill: any;
  @ViewChild(Editor) editorComponent: Editor;

  ngOnInit() {}

 // based on primeng github issue this how we can get references to quill 
  ngAfterViewInit() {
    this.quill = this.editorComponent.quill;
  }

 variableSelected(event) {
    // grab string variable from event 
    this.quill.insertEmbed(this.cursor.index || 0, 'marker', event.value);
  }

}

According to similar topics on the quill GitHub, my code should be functioning correctly:

topic 1

topic 2

topic 3

topic 4

If anyone could assist me in pinpointing what I might be overlooking or where my issue lies, I would greatly appreciate it. Thanks in advance.

Answer №1

I managed to resolve my issue using the following method:

...
declare var Quill: any;
const BlockEmbed = Quill.import('blots/embed');

export class CustomBlock extends BlockEmbed {

  static create(value: any) {
    const node = super.create(typeof value === 'object' ? value.text : value);
    node.innerText = typeof value === 'object' ? value.text : value;
    node.setAttribute('contenteditable', false);
    return node;
  }

  static value(node) {
    return {
      style: node.getAttribute('contenteditable'),
      text: node.innerText
    };
  }

}

CustomBlock['blotName'] = 'marker';
CustomBlock['className'] = 'marker';
CustomBlock['tagName'] = 'span';

Quill.register('formats/marker', CustomBlock);

export class ManagerComponent implements OnInit, AfterViewInit {

  private quill: any;

  @ViewChild('stepper') stepper;
  @ViewChild(Editor) editorComponent: Editor;

...

variableSelected(event) {
    this.quill.insertEmbed(this.cursor.index || 0, 'marker', event.value, 'user');
    this.quill.update('user'); 
  }

Answer №2

Here is how I've been successfully using it:

import * as QuillNamespace from 'quill';
const Quill: any = QuillNamespace;

const BlockEmbed = Quill.import('blots/block/embed');

Answer №3

I encountered a similar issue while using ngx-quill. It seems that the root of the problem lies in the component being declared within a webpack-hidden scope, preventing us from accessing the necessary Quill instance to add extra components. After seeking assistance from KillerCodeMonkey on https://github.com/KillerCodeMonkey/ngx-quill, I found a resolution. By removing any other quill.js imports (from package.json or .angular-cli.json), the code should function correctly on angular/core 5.2.0:

import * as Quill from 'quill'; 
import Parchment from "parchment";

console.log(Quill);
const QuillBlockEmbed = (Quill as any).import('blots/block/embed');

class BlockEmbed extends Parchment.Embed {};
BlockEmbed.prototype = QuillBlockEmbed.prototype;

class MyBlot extends BlockEmbed {
    static create(value) {
        let node: Element = super.create(value) as Element;
        if (typeof value === 'object') {
            node.classList.add("my-class");
        }
        return node;
    }
...
}

MyBlot.blotName = 'boltTwo';
MyBlot.tagName = 'img';

(Quill as any).register({ 'blots/myblot':MyBlot});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Developing typeScript code that can be easily translated and optimized for various web browsers

Can TypeScript alleviate the worry of having to use code such as this (especially when considering browsers like IE that may not support indexOf)? arrValues.indexOf('Sam') > -1 Does the transpiling process in TypeScript generate JavaScript c ...

Stop the instantiation of type alias

Is there a way to restrict the creation of an instance of a type alias, such as ValidatedEmail? type ValidatedEmail = { address: string; validatedOn: Date } Let's say we have functions validateEmail and sendEmail. const validateEmail = (email): Valid ...

What is the best way to store the outcome of a promise in a variable within a TypeScript constructor?

Is it possible to store the result of a promise in a variable within the constructor using Typescript? I'm working with AdonisJS to retrieve data from the database, but the process involves using promises. How do I assign the result to a variable? T ...

Having trouble getting tailwind dark mode to work on next.js?

I have set up a custom boilerplate using next.js(10.0.5) with preact(10.5.12), typescript(4.1.3), and tailwind(2.0.2). I am attempting to incorporate a dark mode feature from Tailwind. I followed the instructions from next-themes in order to add the dark ...

How to instantiate an object in Angular 4 without any parameters

Currently, I am still getting the hang of Angular 4 Framework. I encountered a problem in creating an object within a component and initializing it as a new instance of a class. Despite importing the class into the component.ts file, I keep receiving an er ...

Issue encountered while authenticating client secret from backend for newly created Stripe subscription

There seems to be an issue with confirming the client secret sent from the express backend to the frontend, specifically in a React Native-based mobile application. The clientSecret is being sent in the same manner as described above. On the frontend Rea ...

What is the most effective way to condense these if statements?

I've been working on a project that includes some if statements in the code. I was advised to make it more concise and efficient by doing it all in one line. While my current method is functional, I need to refactor it for approval. Can you assist me ...

A single network request is made for every subscription

When making a post request using HTTPClient and subscribing to the returned observable, I am encountering strange behavior where each subscription triggers a new post request. So, if I subscribe to the observable 5 times, I end up with 5 separate post requ ...

Numerous sentries summoning an asynchronous function

In my scenario, I have two guards - Guard1 and Guard2. Guard1 is responsible for returning an Observable whereas Guard2 returns a Boolean value. For canActivate: [Guard1, Guard2] If Guard2 were to return false, would the request made by Guard1 be automat ...

Utilizing arrayUnion function in Firestore with Angular

My journey to learn Firestore has hit a roadblock. The documentation on AngularFire and FieldValue hasn't been much help. Every time I try to use FieldValue, it throws an error saying "FieldValue does not exist." const userRef = this.firestore.collect ...

Bring in a class with an identical name to a namespace

Currently, I am utilizing a third-party library that comes with a separate @types definition structured as follows: declare namespace Bar { /* ... */ } declare class Bar { /* ... */ } export = Bar; How should I go about importing the Bar class into my ...

Implementing MouseEvents in Typescript React without having to pass them down to child elements

Is it possible to use Mouse Events on a whole React Element without having to pass it to a child element? I have been passing my handleEvent function to several functional components and now I want to know if it can be done without causing a TypeScript err ...

Tips for enabling users to import from subdirectories within my NPM package

Is there a way to allow users to import from subfolders of my TypeScript NPM package? For instance, if the TypeScript code is structured like this: - lib - src - server - react Users should be able to import from the subfolders as package-name/react, ...

TS Mapped Type: Dynamically exclude specific keys based on their values

Seeking a method to create a mapped type that excludes specific keys based on the value of the mapped key. For instance: Consider an option: Options struct, where Options is a union type defined as: { type: DataType } or { type: DataType, params: DataPar ...

Combine arrays using union or intersection to generate a new array

Seeking a solution in Angular 7 for a problem involving the creation of a function that operates on two arrays of objects. The goal is to generate a third array based on the first and second arrays. The structure of the third array closely resembles the f ...

The utilization of *ngTemplateOutlet for conditional rendering is experiencing issues when used within a formGroup

Developed a reusable form input designed to be displayed within a form either as part of a parent formGroupName or independently as a regular input control. The code implementation is: child.component.html: <ng-container *ngIf="hasFormGroup; then f ...

Angular is known to raise the error ExpressionChangedAfterItHasBeenCheckedError

I am currently developing an Angular application using Angular version 7.0.4. My objective is to automatically set focus on the first input element of a modal if the list of working times contains more than one element. However, I am facing an issue where ...

The issue arises when the desired image size is not reflected correctly on the background after changing

I've been working on a basic image slideshow where the background image changes based on user selection. However, I've noticed that when I change the image for the first time, the backgroundSize: cover property seems to disappear. Even if I try c ...

Is there a way to troubleshoot the issue pertaining to using routerLink in Angular version 17.2?

Currently working on a small app, but encountering an error with routerLink in the console. I am new to angular and seeking help. ✘ [ERROR] NG8002: Can't bind to 'routerLink' since it isn't a known property of 'a'. Here is ...

How do Angular and NestJS manage to dynamically resolve injection tokens during runtime using the TypeScript type hints provided at compile time?

Frameworks such as Angular and NestJS in TypeScript utilize dependency injection by converting TypeScript type hints into injection tokens. These tokens are then used to fetch dependencies and inject them into constructors at runtime: @Injectable() // < ...