Quill: Blots rendering depending on quill instance options

Created on 5 Dec 2016  Â·  7Comments  Â·  Source: quilljs/quill

Currently there is no other proper way to render Quill Delta on front-end for display only except using Quill instance in read-only mode. All other methods (third-party libs or own parser) are not suitable for this cause require all formats/blots double registering and also must copy existing Quill functional - rendering Delta with registered Blots and passed settings (it's disscussed here #993 ).
There is some use-cases when complex Blots should be rendered different in edit and display mode, e.x. medium-like images/vieos that has descriptions field

<!-- {{ smth }} is just marks for values places -->
<div class="article__figure" contenteditable="false">
    <!-- wrapper with image or video here -->
    <div class="article__image">
        <img src="{{ src }}" />
    </div>

    <!-- and different ways to edit caption - via contenteditable div or textarea -->
    <div class="article__caption" contenteditable="true">
        {{ caption }}
    </div>
    <!-- or -->
    <div class="article__caption">
        <!-- view mode only, created in static create() by default -->
        {{ caption }}
        <!-- edit mode only, replaces plain text in constructor() -->
        <textarea rows="1">{{ caption }}</textarea>
    </div>
</div>

In this case we need to display caption in editable mode (contenteditable="true" or <textarea>) while editing and as plain text in display mode.
Currently there is no proper way to render same Blot depending on Quill instance options (edit/read-only mode). I'm using ugly-hack for this - in Blot constructor() check that closest Quill instance editor DOM node has .ql-disabled class and re-render part of markup that is awful.

Most helpful comment

This is really pretty easy to implement yourself.

class MyEmbedBlot extends SomeBaseBlot {
    /**
     * Get the attached quill instance.
     *
     * This will _NOT_ work before attach() is called.
     */
    private get quill() {
        if (!this.scroll || !this.scroll.domNode.parentNode) {
            return null;
        }

        return Quill.find(this.scroll.domNode.parentNode!);
    }
}

The quill instance is then available with this.quill. You just have to be aware of where in the lifecycle you call the method. For example calling this in the constructor, before attach() is called will not work because you don't have a scroll or parent blot yet.

All 7 comments

Please don't include stuff like {{ caption }} that is not related to Quill or JavaScript in code examples. It makes it harder to understand.
A working codepen example is much better.

You can render the Blot the same for both normal and readonly modes and hide/show parts of it based on the .ql-disabled class.

Currently facing the same dilemma, how to add options to Blots/access instance options. 🙂

I totally agree. I had to roll my own renderer...

The common suggestion of using JSDOM is too slow for my use-case (my custom renderer, which uses mustache templates, is more than 100x faster), and the 3rd-party renderers are all either incomplete or broken in some way.

I really hope offline (non-DOM-based) rendering is included in Quill 2.0.

Ok found a very interesting solution that was suitable to my problem:

DopeCustomBlot.getQuillInstance = function(domNode) {
  let scrollBlot = Quill.find(domNode);

  // Navigate up to Scroll blot
  while(scrollBlot.parent) {
    scrollBlot = scrollBlot.parent;
  }

  return Quill.find(scrollBlot.domNode.parentNode);
}

DopeCustomBlot.create = function (value) {
  const node = super.create(value);

  console.log('Look ma, Quill instance options:', DopeCustomBlot.getQuillInstance(node).options);

  return node;
}

A similar approach could be replicated on other methods.

And since I've delved a bit more into the internals, it's clear the Blots/Parchment need to be instance agnostic – mainly for re-usability concerns IMO and the fact that Parchment is made to be Quill-independent as well – so although I would admit the interface/API having a method such this one (getQuillInstance), that would make Parchment bound to Quill.

Anyway, leaving the shout.

This reinforces my instinct that the new Quill({ toolbar : "#toolbar" }) constructor should only create an editor-factory, not a full-blown editor instance... For more details, take a look at my responses to this issue:

https://github.com/quilljs/quill/issues/633#issuecomment-327034759

But expanding upon that example, instead of registering blot classes to the top-level Quill module namespace, they should be passed as a schema when attaching to a particular editor instance. For example:

let articleSchema = [ SomeArticleBlot, SomeOtherArticleBlot ];
let commentSchema = [ SomeCommentBlot, SomeOtherCommentBlot ];

let editorFactory = new Quill({ toolbar : "#toolbar" });

editorFactory.attach({
  container : '#article-abc',
  schema : articleSchema
});

editorFactory.attach({
  container : '#comment-123',
  schema : commentSchema
});

This is really pretty easy to implement yourself.

class MyEmbedBlot extends SomeBaseBlot {
    /**
     * Get the attached quill instance.
     *
     * This will _NOT_ work before attach() is called.
     */
    private get quill() {
        if (!this.scroll || !this.scroll.domNode.parentNode) {
            return null;
        }

        return Quill.find(this.scroll.domNode.parentNode!);
    }
}

The quill instance is then available with this.quill. You just have to be aware of where in the lifecycle you call the method. For example calling this in the constructor, before attach() is called will not work because you don't have a scroll or parent blot yet.

@artaommahe Hey, if you implemented figure with editable caption can you share the snippet. I am trying to implement one myself. I want the caption to be formattable. I have implemented the functionality and the html generated is also correct. But I have screwed up with my delta. So, when I save and reload it in the editor, it does not render correctly. I have raised an issue at #2437. I have been stuck here for more than a week now. I would really appreciate if you could help me out.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Yves-K picture Yves-K  Â·  3Comments

GildedHonour picture GildedHonour  Â·  3Comments

CHR15- picture CHR15-  Â·  3Comments

sferoze picture sferoze  Â·  3Comments

rsdrsd picture rsdrsd  Â·  3Comments