Giter Site home page Giter Site logo

killercodemonkey / ngx-quill Goto Github PK

View Code? Open in Web Editor NEW
1.7K 26.0 256.0 16.4 MB

Angular (>=2) components for the Quill Rich Text Editor

License: MIT License

TypeScript 98.66% JavaScript 1.34%
quill-editor ngx-quill quill quilljs rich-text-editor rich-text angular angular-x wysiwyg ng

ngx-quill's Introduction

ngx-quill Build Status

ngx-quill is an angular (>=2) module for the Quill Rich Text Editor containing all components you need.

Donate/Support

If you like my work, feel free to support it. Donations to the project are always welcomed :)

PayPal: PayPal.Me/bengtler

Compatibility to Angular Versions

Angular ngx-quill supported
v17 >= 25.0.0 (quill v2) until May, 2025
v17 24.x (quill v1) until May, 2025
v16 23.x (quill v1) until Nov, 2024

Examples

  • Advanced Demo
    • custom word count module
    • custom toolbar with custom fonts and formats, toolbar position
    • show the differences between sanitizing and not sanitizing your content if your content format is html
    • usage of different content formats
    • template-driven and reactive forms
    • code + syntax highlighting
    • formulas
    • custom key-bindings, e.g. shift + b for bold
    • dynamic styles and placeholder
    • toggle readonly
    • bubble toolbar
    • activate formats after editor initialisation, e.g. rtl direction
    • present quilljs content with the quill-view and quill-view-html component
  • Ionic Demo
  • Angular Universal

Installation

  • npm install ngx-quill
  • install @angular/core, @angular/common, @angular/forms, @angular/platform-browser, quill version ^2.0.0 and rxjs - peer dependencies of ngx-quill
  • include theme styling: bubble.css or snow.css of quilljs in your index.html (you can find them in node_modules/quill/dist), or add them in your css/scss files with @import statements, or add them external stylings in your build process.
  • Example at the beginning of your style.(s)css:
@import '~quill/dist/quill.bubble.css';
// or
@import '~quill/dist/quill.snow.css';

For standard webpack, angular-cli and tsc builds

  • import QuillModule from ngx-quill:
import { QuillModule } from 'ngx-quill'
  • add QuillModule to the imports of your NgModule:
@NgModule({
  imports: [
    ...,

    QuillModule.forRoot()
  ],
  ...
})
class YourModule { ... }
  • use <quill-editor></quill-editor> in your templates to add a default quill editor
  • do not forget to include quill + theme css in your buildprocess, module or index.html!
  • for builds with angular-cli >=6 only add quilljs to your scripts or scripts section of angular.json, if you need it as a global :)!

HINT: If you are using lazy loading modules, you have to add QuillModule.forRoot() to your imports in your root module to make sure the Config services is registered.

Global Config

It's possible to set custom default modules and Quill config options with the import of the QuillConfigModule from the ngx-quill/config. This module provides a global config, but eliminates the need to import the ngx-quill library into the vendor bundle:

import { QuillConfigModule } from 'ngx-quill/config';

@NgModule({
  imports: [
    ...,

    QuillConfigModule.forRoot({
      modules: {
        syntax: true,
        toolbar: [...]
      }
    })
  ],
  ...
})
class AppModule {}

Registering the global configuration can be also done using the standalone function if you are bootstrapping an Angular application using standalone features:

import { provideQuillConfig } from 'ngx-quill/config';

bootstrapApplication(AppComponent, {
  providers: [
    provideQuillConfig({
      modules: {
        syntax: true,
        toolbar: [...]
      }
    })
  ]
})

If you want to use the syntax module follow the Syntax Highlight Module Guide.

See Quill Configuration for a full list of config options.

The QuillModule exports the defaultModules if you want to extend them :).

Known issues

  • IME/special characters can add some unwanted new line (#1821 (comment)) - possible solution: unpatch the compositionend event from zone.js (https://angular.io/guide/zone#setting-up-zonejs)
  • formControl/model change is triggered on first rendering by quill (#1547), because validation can only be done after quill editor is initialise - possible solution: /

Custom Modules and options/formats

  • use customOptions for adding for example custom font sizes or other options/formats
  • use customModules for adding and overwriting modules, e.g. image-resize or your own modules

Suppress global register warnings

Per default when Quill.register is called and you are overwriting an already existing module, QuillJS logs a warning. If you pass customOptions or customModules ngx-quill is registering those modules/options/formats for you.

In e.g. an angular univeral project your AppModule and so QuillModule.forRoot() is executed twice (1x server side, 1x browser). QuillJS is running in a mocked env on server side, so it is intendet that every register runs twice.

To subpress those expected warnings you can turn them off by passing suppressGlobalRegisterWarning: true.

QuillEditorComponent

Hint

Ngx-quill updates the ngModel or formControl for every user change in the editor. Checkout the QuillJS Source parameter of the text-change event.

If you are using the editor reference to directly manipulate the editor content and want to update the model, pass 'user' as the source parameter to the QuillJS api methods.

Config

  • ngModel - set initial value or allow two-way databinding for template driven forms
  • formControl/formControlName - set initial value or allow two-way databinding for reactive forms
  • readOnly (true | false) if user can edit content
  • formats - array of allowed formats/groupings
  • format - model format - default: html, values: html | object | text | json, sets the model value type - html = html string, object = quill operation object, json = quill operation json, text = plain text
  • modules - configure/disable quill modules, e.g toolbar or add custom toolbar via html element default is
const modules = {
  toolbar: [
    ['bold', 'italic', 'underline', 'strike'],        // toggled buttons
    ['blockquote', 'code-block'],

    [{ 'header': 1 }, { 'header': 2 }],               // custom button values
    [{ 'list': 'ordered'}, { 'list': 'bullet' }],
    [{ 'script': 'sub'}, { 'script': 'super' }],      // superscript/subscript
    [{ 'indent': '-1'}, { 'indent': '+1' }],          // outdent/indent
    [{ 'direction': 'rtl' }],                         // text direction

    [{ 'size': ['small', false, 'large', 'huge'] }],  // custom dropdown
    [{ 'header': [1, 2, 3, 4, 5, 6, false] }],

    [{ 'color': [] }, { 'background': [] }],          // dropdown with defaults from theme
    [{ 'font': [] }],
    [{ 'align': [] }],

    ['clean'],                                         // remove formatting button

    ['link', 'image', 'video']                         // link and image, video
  ]
};
  • theme - bubble/snow, default is snow
  • sanitize - uses angulars DomSanitizer to sanitize html values - default: false, boolean (only for format="html")
  • styles - set a styles object, e.g. [styles]="{height: '250px'}"
  • placeholder - placeholder text, default is Insert text here ...
  • bounds - boundary of the editor, default document.body, pass 'self' to attach the editor element
  • maxLength - add validation for maxlength - set model state to invalid and add ng-invalid class
  • minLength - add validation for minlength - set model state to invalid and add ng-invalid class, only set invalid if editor text not empty --> if you want to check if text is required --> use the required attribute
  • trimOnValidation - trim trailing|leading newlines on validation run for required, min- and maxLength, default false
  • required - add validation as a required field - [required]="true" - default: false, boolean expected (no strings!)
  • registry - custom parchment registry to not change things globally
  • beforeRender - a function, which is executed before the Quill editor is rendered, this might be useful for lazy-loading CSS. Given the following example:
// typings.d.ts
declare module '!!raw-loader!*.css' {
  const css: string;
  export default css;
}

// my.component.ts
const quillCSS$ = defer(() =>
  import('!!raw-loader!quill/dist/quill.core.css').then((m) => {
    const style = document.createElement('style');
    style.innerHTML = m.default;
    document.head.appendChild(style);
  })
).pipe(shareReplay({ bufferSize: 1, refCount: true }));

@Component({
  template: '<quill-editor [beforeRender]="beforeRender"></quill-editor>',
})
export class MyComponent {
  beforeRender = () => firstValueFrom(quillCSS$);
}
  • use customOptions for adding for example custom font sizes - array of objects { import: string; whitelist: any[] } --> this overwrites this options globally !!!
// Example with registering custom fonts
customOptions: [{
  import: 'formats/font',
  whitelist: ['mirza', 'roboto', 'aref', 'serif', 'sansserif', 'monospace']
}]
  • use customModules for adding and overwriting modules - an array of objects { implementation: any; path: string } --> this overwrites this modules globally !!!
// The `implementation` may be a custom module constructor or an Observable that resolves to
// a custom module constructor (in case you'd want to load your custom module lazily).
// For instance, these options are applicable:
// import BlotFormatter from 'quill-blot-formatter';
customModules = [
  { path: 'modules/blotFormatter', implementation: BlotFormatter }
]
// Or:
const BlotFormatter$ = defer(() => import('quill-blot-formatter').then(m => m.default))
customModules = [
  { path: 'modules/blotFormatter', implementation: BlotFormatter$ }
]
  • checkout the demo repo about usage of customOptions and customModules Demo Repo
  • possibility to create a custom toolbar via projection slot [quill-editor-toolbar] and add content above [above-quill-editor-toolbar] and below [below-quill-editor-toolbar] the toolbar:

Try to not use much angular magic here, like (output) listeners. Use native EventListeners

<quill-editor>
  <div above-quill-editor-toolbar>
    above
  </div>
  <div quill-editor-toolbar>
    <span class="ql-formats">
      <button class="ql-bold" [title]="'Bold'"></button>
    </span>
    <span class="ql-formats">
      <select class="ql-align" [title]="'Aligment'">
        <option selected></option>
        <option value="center"></option>
        <option value="right"></option>
        <option value="justify"></option>
      </select>
      <select class="ql-align" [title]="'Aligment2'">
        <option selected></option>
        <option value="center"></option>
        <option value="right"></option>
        <option value="justify"></option>
      </select>
    </span>
  </div>
  <div below-quill-editor-toolbar>
    below
  </div>
</quill-editor>
  • customToolbarPosition - if you are working with a custom toolbar you can switch the position :). - default: top, possible values top, bottom
  • debug - set log level warn, error, log or false to deactivate logging, default: warn
  • trackChanges - check if only user (quill source user) or all content/selection changes should be trigger model update, default user. Using all is not recommended, it cause some unexpected sideeffects.
  • classes - a space separated list of CSS classes that will be added onto the editor element
  • linkPlaceholder - optional - set placeholder for the link tooltip
  • debounceTime - optional - debounces onContentChanged, onEditorChanged, ngModel and form control value changes. Improves performance (especially when working with large, >2-3 MiB Deltas), as neither editorChangeHandler, nor textChangeHandler handler runs internally.
  • defaultEmptyValue - optional - change the default value for an empty editor. Currently it is null, but you can set it e.g. to empty string

Full Quill Toolbar HTML

Outputs

  • onEditorCreated - editor instance
  • Use this output to get the editor instance and use it directly. After this output has called the component is stable and all listeners are binded
editor // Quill
  • onContentChanged - text is updated
{
  editor: editorInstance, // Quill
  html: html, // html string
  text: text, // plain text string
  content: content, // Content - operatins representation
  delta: delta, // Delta
  oldDelta: oldDelta, // Delta
  source: source // ('user', 'api', 'silent' , undefined)
}
  • onSelectionChanged - selection is updated, also triggered for onBlur and onFocus, because the selection changed
{
  editor: editorInstance, // Quill
  range: range, // Range
  oldRange: oldRange, // Range
  source: source // ('user', 'api', 'silent' , undefined)
}
  • onEditorChanged - text or selection is updated - independent of the source
{
  editor: editorInstance, // Quill
  event: 'text-change' // event type
  html: html, // html string
  text: text, // plain text string
  content: content, // Content - operatins representation
  delta: delta, // Delta
  oldDelta: oldDelta, // Delta
  source: source // ('user', 'api', 'silent' , undefined)
}

or

{
  editor: editorInstance, // Quill
  event: 'selection-change' // event type
  range: range, // Range
  oldRange: oldRange, // Range
  source: source // ('user', 'api', 'silent' , undefined)
}
  • onFocus - editor is focused
{
  editor: editorInstance, // Quill
  source: source // ('user', 'api', 'silent' , undefined)
}
  • onBlur - editor is blured
{
  editor: editorInstance, // Quill
  source: source // ('user', 'api', 'silent' , undefined)
}
  • onNativeFocus - editor is focused, based on native focus event
{
  editor: editorInstance, // Quill
  source: source // ('dom')
}
  • onNativeBlur - editor is blured, based on native blur event
{
  editor: editorInstance, // Quill
  source: source // ('dom')
}

QuillViewComponent, QuillViewHTMLComponent & How to present the editor content

In most cases a wysiwyg editor is used in backoffice to store the content to the database. On the other side this value should be used, to show the content to the enduser.

In most cases the html format is used, but it is not recommended by QuillJS, because it has the intention to be a solid, easy to maintain editor. Because of that it uses blots and object representations of the content and operation.

This content object is easy to store and to maintain, because there is no html syntax parsing necessary. So you even switching to another editor is very easy when you can work with that.

ngx-quill provides some helper components, to present quilljs content.

QuillViewComponent - Using QuillJS to render content

In general QuillJS recommends to use a QuillJS instance to present your content. Just create a quill editor without a toolbar and in readonly mode. With some simple css lines you can remove the default border around the content.

As a helper ngx-quill provides a component where you can pass many options of the quill-editor like modules, format, formats, customOptions, but renders only the content as readonly and without a toolbar. Import is the content input, where you can pass the editor content you want to present.

Config

  • content - the content to be presented
  • formats - array of allowed formats/groupings
  • format - model format - default: html, values: html | object | text | json, sets the model value type - html = html string, object = quill operation object, json = quill operation json, text = plain text
  • modules - configure/disable quill modules
  • theme - bubble/snow, default is snow
  • debug - set log level warn, error, log or false to deactivate logging, default: warn
  • use customOptions for adding for example custom font sizes --> this overwrites this options globally !!!
  • use customModules for adding and overwriting modules --> this overwrites this modules globally !!!
  • sanitize - uses angulars DomSanitizer to sanitize html values - default: false, boolean (only for format="html")

Outputs

  • onEditorCreated - editor instance
<quill-view [content]="content" format="text" theme="snow"></quill-view>

QuillViewHTMLComponent - Using angular [innerHTML]

Most of you will use the html format (even it is not recommended). To render custom html with angular you should use the [innerHTML] attribute.

But there are some pitfalls:

  1. You need to have the quill css files loaded, when using classes and not inline styling (https://quilljs.com/guides/how-to-customize-quill/#class-vs-inline)
  2. When using classes use a div-tag that has the innerHTML attribute and add the ql-editor class. Wrap your div in another div-tag with css classes ql-container and your theme, e.g. ql-snow.:
<div class="ql-container ql-snow" style="border-width: 0;">
  <div class="ql-editor" [innerHTML]="byPassedHTMLString">
  </div>
</div>
  1. Angular has html sanitation, so it will strip unkown or not trusted parts of your HTML - just mark your html as trusted (DomSanitizer)

After that your content should look like what you expected.

If you store html in your database, checkout your backend code, sometimes backends are stripping unwanted tags as well ;).

As a helper ngx-quill provides a component where you can simply pass your html string and the component does everything for you to render it:

  • add necessary css classes
  • bypass html sanitation
<quill-view-html [content]="htmlstring" theme="snow"></quill-view-html>

Config

  • content - html string to be presented
  • theme - bubble/snow, default is snow
  • sanitize - default: false, boolean (uses DomSanitizer to bypass angular html sanitation when set to false)

Security Hint

Angular templates provide some assurance against XSS in the form of client side sanitizing of all inputs https://angular.io/guide/security#xss.

Ngx-quill components provide the input paramter sanitize to sanitize html-strings passed as ngModel or formControl to the component.

It is deactivated per default to avoid stripping content or styling, which is not expected.

But it is recommended to activate this option, if you are working with html strings as model values.

ngx-quill's People

Contributors

123nenad avatar alexanderawitin avatar arturovt avatar aslamcode avatar benny739 avatar caballerog avatar cexbrayat avatar changhuixu avatar cwspear avatar danielemonaca avatar dasrick avatar deavon avatar dependabot-preview[bot] avatar dependabot[bot] avatar depfu[bot] avatar dzapdos avatar hypercubed avatar jlecordier avatar johanchouquet avatar jpike88 avatar kamilchlebek avatar kdaniel21 avatar killercodemonkey avatar lockonf avatar mattmcsparran avatar princemaple avatar swymmwys avatar whale-street avatar yexan avatar yharaskrik avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ngx-quill's Issues

Using Grammarly Breaks Quill

  1. What is Grammarly
  2. Download Chrome Extension
  3. Visit Demo Page
  4. Attempt to use Quill while Grammarly is enabled.

When Grammarly is enabled on a page that uses the ngx-quill Editor the editor is rendered basically useless, making selecting text impossible and inserting text into the middle of a sentence impossible. The editor I'm using to write this isn't having problems so I know it's possible to correct this issue.

Grammarly is used by a huge amount of people just wanted to let you know about this. So more devs can adopt Quill without worry of users not being able to use the editor.

How to add custom handler

Hi, first of all, thank you for this cool package :).

So, here is my issue: I need to add a custom handler for the images (I don't want the image to be stored in base 64)

How can I add a custom handler ?

Error 413 Payload Too Large

Hi, when I upload a image to the content, the error bellow happens:

Response {_body: "↵↵↵<meta char…s/cors/lib/index.js:185:7)↵↵↵", status: 413, ok: false, statusText: "Payload Too Large", headers: Headers, …}
headers
:
Headers {_headers: Map(12), _normalizedNames: Map(12)} ok : false
status : 413
statusText : "Payload Too Large"
type : 2
url :
"http://www.adrianabartolomucci.com.br/api/servicos/59370f9d6993d8381cd31694"
_body
:
"↵↵↵↵<title>Error</title>↵↵↵

Error: request entity too large
   at readStream (/home/adrianabartolomucci/apps_nodejs/node_modules/body-parser/node_modules/raw-body/index.js:196:17)
   at getRawBody (/home/adrianabartolomucci/apps_nodejs/node_modules/body-parser/node_modules/raw-body/index.js:106:12)
   at read (/home/adrianabartolomucci/apps_nodejs/node_modules/body-parser/lib/read.js:76:3)
   at jsonParser (/home/adrianabartolomucci/apps_nodejs/node_modules/body-parser/lib/types/json.js:127:5)
   at Layer.handle [as handle_request] (/home/adrianabartolomucci/apps_nodejs/node_modules/express/lib/router/layer.js:95:5)
   at trim_prefix (/home/adrianabartolomucci/apps_nodejs/node_modules/express/lib/router/index.js:317:13)
   at /home/adrianabartolomucci/apps_nodejs/node_modules/express/lib/router/index.js:284:7
   at Function.process_params (/home/adrianabartolomucci/apps_nodejs/node_modules/express/lib/router/index.js:335:12)
   at next (/home/adrianabartolomucci/apps_nodejs/node_modules/express/lib/router/index.js:275:10)
   at /home/adrianabartolomucci/apps_nodejs/app.js:43:5
   at Layer.handle [as handle_request] (/home/adrianabartolomucci/apps_nodejs/node_modules/express/lib/router/layer.js:95:5)
   at trim_prefix (/home/adrianabartolomucci/apps_nodejs/node_modules/express/lib/router/index.js:317:13)
   at /home/adrianabartolomucci/apps_nodejs/node_modules/express/lib/router/index.js:284:7
   at Function.process_params (/home/adrianabartolomucci/apps_nodejs/node_modules/express/lib/router/index.js:335:12)
   at next (/home/adrianabartolomucci/apps_nodejs/node_modules/express/lib/router/index.js:275:10)
   at cors (/home/adrianabartolomucci/apps_nodejs/node_modules/cors/lib/index.js:185:7)
↵↵↵"
proto
:
Body

Ability to add custom toolbar options through quill-editor-toolbar to existing toolbar

I'm trying to add additional options to the toolbar which contains syntax highlighting option. In other words, something like this does not work

<quill-editor [(ngModel)]="title"
              [maxLength]="5"
              [minLength]="3"
              [required]="true"
              [modules]="{syntax:true,toolbar: [['code-block']]}"
              (onContentChanged)="onContentChanged($event);">

  <div quill-editor-toolbar>
    <span class="ql-formats">
      <button class="btn-primary">save</button>
    </span>
  </div>

</quill-editor>

Is there a workaround for something like this?

Registration of quill-image-resize-module fails

I have been trying to register an image resize module (https://github.com/kensnyder/quill-image-resize-module) with Quill. But as soon as Quill.register('modules/imageResize', ImageResize); is called, I get the following error: TypeError: Cannot read property 'imports' of undefined

I created a plunker that shows the issue:
https://plnkr.co/edit/IrSndtpRKSXG324zArN7?p=preview

I don't really know if this is really an issue or if I'm simply mistaking something in the configuration. I have had a look at the examples provided and other issues but have not been able to resolve this problem. Hence, any help would be deeply appreciated as the ability to resize images is something quite handy to have.

Thank you very much.

Angular universal

Hi,

ngx-quill breaks universal

for example this line
var elem = document.createElement('div');

cause
ReferenceError: document is not defined

Configurable block tag (div, paragraph)

Any ideas on how to implement the solution found here in an Angular 2 project? I'm not fond of the <p><br></p> line on enter. I think the output should look the same as what's shown in the editor.

AOT Build Error with angular 4

When try AOT Build with Angular 4, I got the flowing error:

Unexpected value 'QuillModule in /Projects/angular-seed/node_modules/ngx-quill/in
dex.d.ts' imported by the module 'AppModule in /Projects/angular-seed/src/app/app
.module.ts'. Please add a @NgModule annotation.

Here is my app.modules.ts:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpModule, JsonpModule } from '@angular/http';

import { NgbModule } from '@ng-bootstrap/ng-bootstrap';

import { QuillModule } from 'ngx-quill';

import { AppComponent }   from './app.component';
import { RouteComponents, AppRoutes } from './app.routes';

@NgModule({
    declarations: [
        AppComponent,
        RouteComponents
    ],
    imports: [
        BrowserModule,
        BrowserAnimationsModule,
        CommonModule,
        FormsModule,
        ReactiveFormsModule,
        HttpModule,
        JsonpModule,
        NgbModule,
        QuillModule,
        AppRoutes
    ],
    bootstrap: [AppComponent]
})
export class AppModule {}

and my package.json

{
  "name": "angular-seed",
  "version": "1.0.0",
  "description": "angular seed app",
  "main": "index.html",
  "scripts": {
    "sass": "node-sass --recursive --source-map true --source-map-contents --output src src ",
    "sass:w": "npm run sass -- --watch",
    "build": "npm run tsc && npm run sass",
    "start": "npm run clean && npm run build && concurrently \"npm run tsc:w\" \"npm run sass:w\" \"npm run gulp:dev\"",
    "gulp:dev": "gulp dev",
    "gulp:serve": "gulp serve",
    "clean": "rm -rf dist && find src -name \\*.js -type f -delete && find src -name \\*.js.map -type f -delete && find src -name \\*.ngfactory.ts -type f -delete && find src -name \\*.css -type f -delete && find src -name \\*.css.map -type f -delete && find src -name \\*.shim.ts -type f -delete && find src -name \\*.ngsummary.json -type f -delete && find src -name \\*.ngstyle.ts -type f -delete",
    "ngc": "ngc --project tsconfig-aot.json",
    "tsc": "tsc --project tsconfig.json",
    "tsc:w": "npm run tsc -- --watch",
    "tsc-aot": "tsc -p tsconfig-aot.json",
    "rollup": "rollup -c rollup.config.js",
    "build-aot": "npm run build && npm run ngc && npm run tsc-aot && npm run rollup"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/beginor/angular-seed.git"
  },
  "keywords": ["angular", "seed", "gulp", "systemjs"],
  "author": "beginor",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/beginor/angular-seed/issues"
  },
  "homepage": "https://github.com/beginor/angular-seed#readme",
  "dependencies": {
    "@angular/animations": "^4.2.5",
    "@angular/common": "^4.2.5",
    "@angular/compiler": "^4.2.5",
    "@angular/core": "^4.2.5",
    "@angular/forms": "^4.2.5",
    "@angular/http": "^4.2.5",
    "@angular/platform-browser": "^4.2.5",
    "@angular/platform-browser-dynamic": "^4.2.5",
    "@angular/router": "^4.2.5",
    "core-js": "^2.4.1",
    "reflect-metadata": "^0.1.10",
    "rxjs": "~5.4.1",
    "systemjs": "^0.20.14",
    "zone.js": "^0.8.12",
    "bootstrap": "^4.0.0-alpha.6",
    "@ng-bootstrap/ng-bootstrap": "^1.0.0-alpha.26",
    "font-awesome": "^4.7.0",
    "web-animations-js": "^2.2.5",
    "quill": "^1.2.6",
    "ngx-quill": "^1.3.3"
  },
  "devDependencies": {
    "@angular/compiler-cli": "^4.2.5",
    "@angular/platform-server": "^4.2.5",
    "@types/core-js": "~0.9.42",
    "@types/node": "~8.0.6",
    "concurrently": "^3.5.0",
    "gulp": "^3.9.1",
    "gulp-connect": "^5.0.0",
    "http-proxy-middleware": "0.17.4",
    "node-sass": "^4.5.3",
    "rollup": "^0.43.0",
    "rollup-plugin-commonjs": "^8.0.2",
    "rollup-plugin-node-resolve": "^3.0.0",
    "rollup-plugin-uglify": "^2.0.1",
    "typescript": "~2.3.4",
    "@types/web-animations-js": "^2.2.4",
    "@types/quill":"^0.0.31"
  }
}

Is there a way to "clean" text as it is pasted in?

I do not have the Clean button enabled in the toolbar. Many users copy and paste rich HTML (From Word etC) into the textbox and then they can't remove the formatting.

Is there a way to clean it on paste? (ie. it removes all formatting and then they can reformat as they want?)

Thanks

Cursor moving to beginning of line

ngx-quilltest

As seen above, whenever I first initiate the quill editor (with a null, '', or database value), the cursor automatically moves to the front and starts placing characters in front of it.

quill version: "ngx-quill": "^1.3.3" (I've confirmed that 1.33 is installed by using the npm list --depth=0 command

HTML:
<quill-editor formControlName="answer" (onEditorCreated)="setFocus($event)"></quill-editor>

The setFocus method is the same as you have in your demo.

Any insights as to why this is happening? If you need more information, please let me know.

Integrating with Directives?

Wondered if there was anyway to integrate the editor with directives? I've been trying to use this example for auto including link tags on regex match but instead trying to auto include some directive or locator for Angular2 to use. Tried adding attributes like [custom] or adding custom tags to tie certain words to angular hooks but just constantly get "[Parchment] Cannot insert custom into block," 'custom' is whatever I try build either the attribute/blot/embed/inline/etc. Wondered if this was actually possible, or just incompatible.

Loading external fonts

Hello,

I'm having issues on loading external fonts.
Html:

<quill-editor [(ngModel)]="HTML_DATA" [style]="{'height' : '100%'}" [modules]="configEditor">

On my config object I've add:

configEditor: {} = {
    toolbar: [
      ['bold', 'italic', 'underline', 'strike'],       
      [{ 'list': 'ordered'}, { 'list': 'bullet' }],
      [{ 'header': [1, 2, 3, 4, 5, 6, false] }],
      [{ 'color': [] }, { 'background': [] }],         
      [{ 'font': [ 'Aref Ruqaa', 'Mirza', 'Roboto'] }],
      [{ 'align': [] }],
      ['clean'],                   
    ]
  };

loaded the google font on index.html
<link href="https://fonts.googleapis.com/css?family=Aref+Ruqaa|Mirza|Roboto" rel="stylesheet">

and added some css on the component:

#editor-container {
    font-family: "Aref Ruqaa";
    font-size: 18px;
    height: 375px;
  }
 
  #toolbar-container .ql-font span[data-label="Aref Ruqaa"]::before {
    font-family: "Aref Ruqaa";
  }

  #toolbar-container .ql-font span[data-label="Mirza"]::before {
    font-family: "Mirza";
  }

  #toolbar-container .ql-font span[data-label="Roboto"]::before {
    font-family: "Roboto";
  }
  
  .ql-font-mirza {
    font-family: "Mirza";
  }

  .ql-font-roboto {
    font-family: "Roboto";
  }

I'm doing anything wrong? It's possible to load fonts with this directive? The documentation is a little bit confusing. The only working example that I can see is in regular js.

https://codepen.io/quill/pen/gLBYam

Thanks.

onContentChanged - triggers when item is removed from array

I have a problem regarding the onContentChanged method.

In my project I have a array of quill editors. And a user can add a new editor instance to the array and delete one from the array. But when the user delete a editor from the array the onContentChanged method is triggered.

And that is causing some side effects in my project. Because every time onContentChanged is triggered I save the data to the database. And when I delete a editor from my array, onContentChanged will add it back ;-(

Is there a way to change the behavior of onContentChanged so it's only watching the content of the current instance of the editor?

Build Error 1.3.3

I dialed back to 1.3.1 and everything is fine, so I'm thinking there might be an issue with a later release. (fyi ... I am not an AoT user)

node error;
build 06-Jul-2017 15:55:14 ERROR in [at-loader] ./node_modules/ngx-quill/src/quill-editor.component.ts:109:7
build 06-Jul-2017 15:55:14 TS2345: Argument of type '{ modules: any; placeholder: string; readOnly: boolean; theme: string; formats: string[]; bounds:...' is not assignable to parameter of type 'QuillOptionsStatic'.
build 06-Jul-2017 15:55:14 Object literal may only specify known properties, and 'bounds' does not exist in type 'QuillOptionsStatic'.

Register new module ImageResize

I'm trying to add a new module in the editor to show image size and can resize it.

I found this module for the Quill editor but I am not able to add it in ngx-quill when it returns the editor in onEditorCreated ()

Component.js
onEditorCreated(quill) { this.editor = quill; this.editor.register('modules/ImageResize',ImageResize); console.log('quill is ready! this is current quill instance object', quill); }

`/**
 * Custom module for quilljs to allow user to resize <img> elements
 * (Works on Chrome, Edge, Safari and replaces Firefox's native resize behavior)
 * @see https://quilljs.com/blog/building-a-custom-module/
 */ 
export class ImageResize {
 
	constructor(quill, options = {}) {
		// save the quill reference and options
		this.quill = quill;
		this.options = options;

		// bind handlers to this instance
		this.handleClick = this.handleClick.bind(this);
		this.handleMousedown = this.handleMousedown.bind(this);
		this.handleMouseup = this.handleMouseup.bind(this);
		this.handleDrag = this.handleDrag.bind(this);
		this.checkImage = this.checkImage.bind(this);

		// track resize handles
		this.boxes = [];

		// disable native image resizing on firefox
		document.execCommand('enableObjectResizing', false, 'false');

		// respond to clicks inside the editor
		this.quill.root.addEventListener('click', this.handleClick, false);
	}

	handleClick(evt) {
		if (evt.target && evt.target.tagName && evt.target.tagName.toUpperCase() == 'IMG') {
			if (this.img === evt.target) {
				// we are already focused on this image
				return;
			}
			if (this.img) {
				// we were just focused on another image
				this.hide();
			}
			// clicked on an image inside the editor
			this.show(evt.target);
		}
		else if (this.img) {
			// clicked on a non image
			this.hide();
		}
	}

	show(img) {
		// keep track of this img element
		this.img = img;
		this.showResizers();
		this.showSizeDisplay();
		// position the resize handles at the corners
		const rect = this.img.getBoundingClientRect();
		this.positionBoxes(rect);
		this.positionSizeDisplay(rect);
	}

	hide() {
		this.hideResizers();
		this.hideSizeDisplay();
		this.img = undefined;
	}

	showResizers() {
		// prevent spurious text selection
		this.setUserSelect('none');
		// add 4 resize handles
		this.addBox('nwse-resize'); // top left
		this.addBox('nesw-resize'); // top right
		this.addBox('nwse-resize'); // bottom right
		this.addBox('nesw-resize'); // bottom left
		// listen for the image being deleted or moved
		document.addEventListener('keyup', this.checkImage, true);
		this.quill.root.addEventListener('input', this.checkImage, true);
	}

	hideResizers() {
		// stop listening for image deletion or movement
		document.removeEventListener('keyup', this.checkImage);
		this.quill.root.removeEventListener('input', this.checkImage);
		// reset user-select
		this.setUserSelect('');
		this.setCursor('');
		// remove boxes
		this.boxes.forEach(box => document.body.removeChild(box));
		// release memory
		this.dragBox = undefined;
		this.dragStartX = undefined;
		this.preDragWidth = undefined;
		this.boxes = [];
	}

	addBox(cursor) {
		// create div element for resize handle
		const box = document.createElement('div');
		// apply styles
		const styles = {
			position: 'absolute',
			height: '12px',
			width: '12px',
			backgroundColor: 'white',
			border: '1px solid #777',
			boxSizing: 'border-box',
			opacity: '0.80',
			cursor: cursor,
		};
		this.extend(box.style, styles, this.options.handleStyles || {});
		// listen for mousedown on each box
		box.addEventListener('mousedown', this.handleMousedown, false);
		// add drag handle to document
		document.body.appendChild(box);
		// keep track of drag handle
		this.boxes.push(box);
	}

	extend(destination, ...sources) {
		sources.forEach(source => {
			for (let prop in source) {
				if (source.hasOwnProperty(prop)) {
					destination[prop] = source[prop];
				}
			}
		});
		return destination;
	}

	positionBoxes(rect) {
		// set the top and left for each drag handle
		[
			{left: rect.left - 6, top: rect.top - 6},               // top left
			{left: rect.left + rect.width - 6, top: rect.top - 6},               // top right
			{left: rect.left + rect.width - 6, top: rect.top + rect.height - 6}, // bottom right
			{left: rect.left - 6, top: rect.top + rect.height - 6}, // bottom left
		].forEach((pos, idx) => {
			this.extend(this.boxes[idx].style, {
				top: Math.round(pos.top + window.pageYOffset) + 'px',
				left: Math.round(pos.left + window.pageXOffset) + 'px',
			});
		});
	}

	handleMousedown(evt) {
		// note which box
		this.dragBox = evt.target;
		// note starting mousedown position
		this.dragStartX = evt.clientX;
		// store the width before the drag
		this.preDragWidth = this.img.width || this.img.naturalWidth;
		// set the proper cursor everywhere
		this.setCursor(this.dragBox.style.cursor);
		// listen for movement and mouseup
		document.addEventListener('mousemove', this.handleDrag, false);
		document.addEventListener('mouseup', this.handleMouseup, false);
	}

	handleMouseup() {
		// reset cursor everywhere
		this.setCursor('');
		// stop listening for movement and mouseup
		document.removeEventListener('mousemove', this.handleDrag);
		document.removeEventListener('mouseup', this.handleMouseup);
	}

	handleDrag(evt) {
		if (!this.img) {
			// image not set yet
			return;
		}
		// update image size
		if (this.dragBox == this.boxes[0] || this.dragBox == this.boxes[3]) {
			// left-side resize handler; draging right shrinks image
			this.img.width = Math.round(this.preDragWidth - evt.clientX - this.dragStartX);
		}
		else {
			// right-side resize handler; draging right enlarges image
			this.img.width = Math.round(this.preDragWidth + evt.clientX - this.dragStartX);
		}
		// reposition the drag handles around the image
		const rect = this.img.getBoundingClientRect();
		this.positionBoxes(rect);
		this.positionSizeDisplay(rect);
	}

	setUserSelect(value) {
		[
			'userSelect',
			'mozUserSelect',
			'webkitUserSelect',
			'msUserSelect'
		].forEach(prop => {
			// set on contenteditable element and <html>
			this.quill.root.style[prop] = value;
			document.documentElement.style[prop] = value;
		});
	}

	setCursor(value) {
		[
			document.body,
			this.img,
			this.quill.root
		].forEach(el => el.style.cursor = value);
	}

	checkImage() {
		if (this.img) {
			this.hide();
		}
	}

	showSizeDisplay() {
		if (!this.options.displaySize) {
			return;
		}
		this.display = document.createElement('div');
		// apply styles
		const styles = {
			position: 'absolute',
			font: '12px/1.0 Arial, Helvetica, sans-serif',
			padding: '4px 8px',
			textAlign: 'center',
			backgroundColor: 'white',
			color: '#333',
			border: '1px solid #777',
			boxSizing: 'border-box',
			opacity: '0.80',
			cursor: 'default',
		};
		this.extend(this.display.style, styles, this.options.displayStyles || {});
		document.body.appendChild(this.display);
	}

	hideSizeDisplay() {
		document.body.removeChild(this.display);
		this.display = undefined;
	}

	positionSizeDisplay(rect) {
		if (!this.display || !this.img) {
			return;
		}
		const size = this.getCurrentSize();
		this.display.innerHTML = size.join(' &times; ');
		if (size[0] > 120 && size[1] > 30) {
			// position on top of image
			const dispRect = this.display.getBoundingClientRect();
			this.extend(this.display.style, {
				left: Math.round(rect.left + rect.width + window.pageXOffset - dispRect.width - 8) + 'px',
				top: Math.round(rect.top + rect.height + window.pageYOffset - dispRect.height - 8) + 'px',
			});
		}
		else {
			// position off bottom right
			this.extend(this.display.style, {
				left: Math.round(rect.left + rect.width + window.pageXOffset + 8) + 'px',
				top: Math.round(rect.top + rect.height + window.pageYOffset + 8) + 'px',
			});
		}
	}

	getCurrentSize() {
		return [
			this.img.width,
			Math.round(this.img.width / this.img.naturalWidth * this.img.naturalHeight),
		];
	}

}
`

Someone know how to ?

Syntax highlighter

Hi
This might be the wrong place for my question but here goes...

I have added angular2-highlight-js and it works very well inside quill.

But when I want to display syntax highlighted code outside quill I am not able to get it working.
On the element i set:
highlight-js-content=".highlight"
This is according to the docs in highlight. But quill saves it:
<pre class="ql-syntax" spellcheck="false">

Any idea on how I can make this work?

In advance thank you.

Compiling with ng build --aot throws "can't find symbol KeyRegistry exported from module"

Using AngularCLI and trying to build in AOT mode, I get this error: "can't find symbol KeyRegistry exported from module". Honestly at this stage I have no idea what this means or why - I am raising it here in the hope that someone knows what this means - I will post back here if I can figure out the issue

>ng build --aot
  0% compilingcan't resolve module C:/Git/QueMesaAdmin/QueMesaAdminApp/node_modules/ngx-quill/node_modules/@angular/core/src/di/reflective_key.d.ts from C:/Git/QueMesaAdmin/QueMesaAdminApp/node_modules/ngx-quill/node_modules/@angular/core/src/di/reflective_key.d.ts
can't find symbol KeyRegistry exported from module C:/Git/QueMesaAdmin/QueMesaAdminApp/node_modules/ngx-quill/node_modules/@angular/core/src/di/reflective_key.d.ts
Error: can't find symbol KeyRegistry exported from module C:/Git/QueMesaAdmin/QueMesaAdminApp/node_modules/ngx-quill/node_modules/@angular/core/src/di/reflective_key.d.ts
    at ReflectorHost.findDeclaration (C:\Users\chuckj\src\angular\modules\@angular\compiler-cli\src\reflector_host.ts:200:15)
    at C:\Users\chuckj\src\angular\modules\@angular\compiler-cli\src\codegen.ts:179:44
    at Array.forEach (native)

unexpected actions

hi, the does the demo work for you? I checked your live demo and the editor behaves very buggy. the cursor randomly goes to the beginning of the text, some spaces are removed and there happen a huge number of selectionaction event on the background.

Throw 'Unexpected token export' when import the module

system: OSX 10.12.4
angular: 2.4.1
code:

import { QuillModule } from 'ngx-quill';
console.log('QuillModule', QuillModule);

errors:

Uncaught SyntaxError: Unexpected token export
es5-shim.js?hash=adc3c62…:17 Uncaught TypeError: Cannot read property 'meteorInstall' of undefined
    at es5-shim.js?hash=adc3c62…:17
    at es5-shim.js?hash=adc3c62…:2789
(anonymous) @ es5-shim.js?hash=adc3c62…:17
(anonymous) @ es5-shim.js?hash=adc3c62…:2789
promise.js?hash=db5ea3b…:17 Uncaught TypeError: Cannot read property 'meteorInstall' of undefined
    at promise.js?hash=db5ea3b…:17
    at promise.js?hash=db5ea3b…:581
(anonymous) @ promise.js?hash=db5ea3b…:17
(anonymous) @ promise.js?hash=db5ea3b…:581
ecmascript-runtime.js?hash=361600e…:17 Uncaught TypeError: Cannot read property 'meteorInstall' of undefined
    at ecmascript-runtime.js?hash=361600e…:17
    at ecmascript-runtime.js?hash=361600e…:4630
(anonymous) @ ecmascript-runtime.js?hash=361600e…:17
(anonymous) @ ecmascript-runtime.js?hash=361600e…:4630
babel-runtime.js?hash=542c082…:17 Uncaught TypeError: Cannot read property 'meteorInstall' of undefined
    at babel-runtime.js?hash=542c082…:17
    at babel-runtime.js?hash=542c082…:160
(anonymous) @ babel-runtime.js?hash=542c082…:17
(anonymous) @ babel-runtime.js?hash=542c082…:160
random.js?hash=31dadb9…:18 Uncaught TypeError: Cannot read property 'meteorInstall' of undefined
    at random.js?hash=31dadb9…:18
    at random.js?hash=31dadb9…:355
(anonymous) @ random.js?hash=31dadb9…:18
(anonymous) @ random.js?hash=31dadb9…:355
mongo-id.js?hash=345d169…:19 Uncaught TypeError: Cannot read property 'Random' of undefined
    at mongo-id.js?hash=345d169…:19
    at mongo-id.js?hash=345d169…:142
(anonymous) @ mongo-id.js?hash=345d169…:19
(anonymous) @ mongo-id.js?hash=345d169…:142
geojson-utils.js?hash=b204c7d…:17 Uncaught TypeError: Cannot read property 'meteorInstall' of undefined
    at geojson-utils.js?hash=b204c7d…:17
    at geojson-utils.js?hash=b204c7d…:439
(anonymous) @ geojson-utils.js?hash=b204c7d…:17
(anonymous) @ geojson-utils.js?hash=b204c7d…:439
minimongo.js?hash=66cc6ab…:23 Uncaught TypeError: Cannot read property 'MongoID' of undefined
    at minimongo.js?hash=66cc6ab…:23
    at minimongo.js?hash=66cc6ab…:3917
(anonymous) @ minimongo.js?hash=66cc6ab…:23
(anonymous) @ minimongo.js?hash=66cc6ab…:3917
check.js?hash=63d7478…:17 Uncaught TypeError: Cannot read property 'meteorInstall' of undefined
    at check.js?hash=63d7478…:17
    at check.js?hash=63d7478…:613
(anonymous) @ check.js?hash=63d7478…:17
(anonymous) @ check.js?hash=63d7478…:613
retry.js?hash=1e40961…:18 Uncaught TypeError: Cannot read property 'Random' of undefined
    at retry.js?hash=1e40961…:18
    at retry.js?hash=1e40961…:110
(anonymous) @ retry.js?hash=1e40961…:18
(anonymous) @ retry.js?hash=1e40961…:110
ddp-common.js?hash=d42359b…:17 Uncaught TypeError: Cannot read property 'check' of undefined
    at ddp-common.js?hash=d42359b…:17
    at ddp-common.js?hash=d42359b…:493
(anonymous) @ ddp-common.js?hash=d42359b…:17
(anonymous) @ ddp-common.js?hash=d42359b…:493
reload.js?hash=628b069…:18 Uncaught TypeError: Cannot read property 'Symbol' of undefined
    at reload.js?hash=628b069…:18
    at reload.js?hash=628b069…:309
(anonymous) @ reload.js?hash=628b069…:18
(anonymous) @ reload.js?hash=628b069…:309
ddp-client.js?hash=bc32a16…:17 Uncaught TypeError: Cannot read property 'check' of undefined
    at ddp-client.js?hash=bc32a16…:17
    at ddp-client.js?hash=bc32a16…:4983
(anonymous) @ ddp-client.js?hash=bc32a16…:17
(anonymous) @ ddp-client.js?hash=bc32a16…:4983
ddp.js?hash=25dc3f4…:14 Uncaught TypeError: Cannot read property 'DDP' of undefined
    at ddp.js?hash=25dc3f4…:14
    at ddp.js?hash=25dc3f4…:27
(anonymous) @ ddp.js?hash=25dc3f4…:14
(anonymous) @ ddp.js?hash=25dc3f4…:27
allow-deny.js?hash=c9344ef…:18 Uncaught TypeError: Cannot read property 'LocalCollection' of undefined
    at allow-deny.js?hash=c9344ef…:18
    at allow-deny.js?hash=c9344ef…:536
(anonymous) @ allow-deny.js?hash=c9344ef…:18
(anonymous) @ allow-deny.js?hash=c9344ef…:536
mongo.js?hash=c4281c0…:17 Uncaught TypeError: Cannot read property 'AllowDeny' of undefined
    at mongo.js?hash=c4281c0…:17
    at mongo.js?hash=c4281c0…:909
(anonymous) @ mongo.js?hash=c4281c0…:17
(anonymous) @ mongo.js?hash=c4281c0…:909
xolvio_cleaner.js?hash=a137467…:17 Uncaught TypeError: Cannot read property 'Mongo' of undefined
    at xolvio_cleaner.js?hash=a137467…:17
    at xolvio_cleaner.js?hash=a137467…:102
(anonymous) @ xolvio_cleaner.js?hash=a137467…:17
(anonymous) @ xolvio_cleaner.js?hash=a137467…:102
practicalmeteor_chai.js?hash=92e20bf…:17 Uncaught TypeError: Cannot read property 'Symbol' of undefined
    at practicalmeteor_chai.js?hash=92e20bf…:17
    at practicalmeteor_chai.js?hash=92e20bf…:5639
(anonymous) @ practicalmeteor_chai.js?hash=92e20bf…:17
(anonymous) @ practicalmeteor_chai.js?hash=92e20bf…:5639
practicalmeteor_sinon.js?hash=b2d5a9f…:17 Uncaught TypeError: Cannot read property 'chai' of undefined
    at practicalmeteor_sinon.js?hash=b2d5a9f…:17
    at practicalmeteor_sinon.js?hash=b2d5a9f…:6256
(anonymous) @ practicalmeteor_sinon.js?hash=b2d5a9f…:17
(anonymous) @ practicalmeteor_sinon.js?hash=b2d5a9f…:6256
hwillson_stub-collections.js?hash=fd53027…:17 Uncaught TypeError: Cannot read property 'Mongo' of undefined
    at hwillson_stub-collections.js?hash=fd53027…:17
    at hwillson_stub-collections.js?hash=fd53027…:106
(anonymous) @ hwillson_stub-collections.js?hash=fd53027…:17
(anonymous) @ hwillson_stub-collections.js?hash=fd53027…:106
webapp.js?hash=3026d45…:18 Uncaught TypeError: Cannot read property 'meteorInstall' of undefined
    at webapp.js?hash=3026d45…:18
    at webapp.js?hash=3026d45…:70
(anonymous) @ webapp.js?hash=3026d45…:18
(anonymous) @ webapp.js?hash=3026d45…:70
livedata.js?hash=7cf1831…:14 Uncaught TypeError: Cannot read property 'DDP' of undefined
    at livedata.js?hash=7cf1831…:14
    at livedata.js?hash=7cf1831…:31
(anonymous) @ livedata.js?hash=7cf1831…:14
(anonymous) @ livedata.js?hash=7cf1831…:31
autoupdate.js?hash=1fd9cf3…:19 Uncaught TypeError: Cannot read property 'Retry' of undefined
    at autoupdate.js?hash=1fd9cf3…:19
    at autoupdate.js?hash=1fd9cf3…:206
(anonymous) @ autoupdate.js?hash=1fd9cf3…:19
(anonymous) @ autoupdate.js?hash=1fd9cf3…:206
barbatus_typescript-runtime.js?hash=7df7215…:17 Uncaught TypeError: Cannot read property 'meteorInstall' of undefined
    at barbatus_typescript-runtime.js?hash=7df7215…:17
    at barbatus_typescript-runtime.js?hash=7df7215…:215
(anonymous) @ barbatus_typescript-runtime.js?hash=7df7215…:17
(anonymous) @ barbatus_typescript-runtime.js?hash=7df7215…:215
global-imports.js?hash=4c0db35…:3 Uncaught TypeError: Cannot read property 'Mongo' of undefined
    at global-imports.js?hash=4c0db35…:3
(anonymous) @ global-imports.js?hash=4c0db35…:3
app.js?hash=151d68c…:1 Uncaught ReferenceError: meteorInstall is not defined
    at app.js?hash=151d68c…:1
(anonymous) @ app.js?hash=151d68c…:1

Reset the quill editor

I'm using angular 4 + reactive forms and bringing rich text in from my api. On initialization the quill editor puts the rich text in exact how I want, but if I overwrite the initialized editor it will strip all my markup inside the opening p and closing /p tags. How can I make every patchValue replace the editor body with exactly what I want it to from the api?

Setting modules input results in error

Setting modules input causes error quill Cannot import modules/0. Are you sure it was registered? .
My exaple is:
<quill-editor formControlName="journalEntry" modules="{toolbar: [['bold', 'italic', 'underline', 'strike']]}") </quill-editor>

Thank you

error on compiling with angular-cli (1.0.0-rc.0)

When trying to use ngx-quill with angular-cli (1.0.0-rc.0) and angular (4.0.0-rc.2) I get an error on compiling regarding quill.module.ts

ERROR in Error encountered resolving symbol values statically. Calling function 'makeDecorator', function calls are not supported. Consider replacing the function or lambda with a reference to an exported function

Prepend protocol to inserted links

Is there a way to prepend protocol to inserted links or make all link absolute?
For example, if user enters google.com as a link Quill generates link with href="google.com" which opens it incorrectly as a relative link.

Thanks

Can you please give an exam,ple of how to configure the Toolbar?

Could you please give an exmple of how to configure the toolbar? I have added it to my module but not sure where the configuration goes, thanks

{```

toolbar: [
['bold', 'italic', 'underline', 'strike'], // toggled buttons
['blockquote', 'code-block'],
...
['link', 'image', 'video'] // link and image, video
]
};

Triggering clipboard with initial model value

It seems like when a new instance of quill-editor is created it triggers an event on the clipboard.
This behavior doesn't happen in the original version of quilljs. AI looked into the code and seems like you're using pasteHTML to insert the initial value, which makes it trigger the clipboard.

Context: On my application I'm listening for content that is pasted into the editor so I can clean up the content being pasted, the problem is that every time a new instance of quill-editor is created, it triggers a clipboard event which ends up changing the format of the value being passed by ngModel.

Here's a code example:

    /// this should be triggered ONLY when a new text is pasted from the clipboard.
    quill.clipboard.addMatcher(Node.ELEMENT_NODE, (node, delta) => {
      return new Delta().insert(node.textContent);
    });

cc: @EunusHosen

Will not compile with "noImplicitAny"

Line 93 in "quill-editor.component.ts" throws an error when compiled with Typescripts "noImplicitAny" flag set.

"[ts] Element implicitly has an 'any' type because type 'Object' has no index signature."

The code:

if (toolbarElem) {
  modules['toolbar'] = toolbarElem; // Throws error
}

Responsive Toolbar Collapse

Feature Request

It would be nice if we could collapse the toolbar under a hamburger button to not take up so much room on small screens.

onContentChange() Debounce option

Would be nice to set a x number of seconds to debounce the onContentChange function.

So when you want to save data to your back end, you can write it every x seconds.

custom toolbar

Hello,

I m trying to use custom toolbar and it s not working.
Can you give me some direction how to use it?

many thanks

Include 'tslint.json' into .npmignore

when using ngx-quill as dependency in angular 2+ (in my case ^4.0.0) and try to run ´ng lint´ then you will get the following error:

Failed to load /Users/foo/projects/bar/node_modules/ngx-quill/tslint.json: Could not find custom rule directory: /Users/foo/projects/bar/node_modules/ngx-quill/node_modules/codelyzer

The tslint.json is needed for development but not as part of the distribution.

So, please add 'tslint.json' into .npmignore and publish a new version ... THX

How to focus the editor?

Is there any way to focus the text field with this module? I noticed that Quill has a .focus() method, but I don't know if I can use it or how to do it.

Uncaught Error: Expected 'styles' to be an array of strings.

Hi.

I've just downloaded your component and im trying to make it work. I've added the component to my appModule as explained in the ReadMe file.

i get this error when trying to transpile my code:

angular 2/angular-seed/angular-webpack/node_modules/ng2-quill-editor/quillEditor.component.ts:81:57
Property 'assign' does not exist on type 'ObjectConstructor'.

i also get a bunch of errors, like this one:

Uncaught Error: Expected 'styles' to be an array of strings.
at assertArrayOfStrings (eval at (http://localhost:8081/vendor.js:95:2)

Most of the errors occur in the vendor.ts file.

I had to set the "noImplicitAny": false to false to avoid a whole bunch of other errors regarding implicitly type any.

Is there any fix to these errors? Am i missing something?

I'm running with typescript version 2.0.10 - angular version ~2.2.0

Kind regards Chris

Error: Unexpected value 'QuillModule' imported by the module 'AppModule' at d

I've been trying to get Quill working in my Angular 2 project and I can't figure out this error from ngx-quill. I followed the instructions for the systemjs.config file, added import { QuillModule } from 'ngx-quill'; to my app.module file, added it to the Imports, added "ngx-quill": "1.0.0" to my packages.json file and "@types/quill": "^0.0.29" to devDependencies.

Any idea what this console error means?
Error: Unexpected value 'QuillModule' imported by the module 'AppModule' at d

I need the index.js

Hi,

I am using Angular-seed project which is using SystemJS. It requires the index.js instead of index.ts. For example, the ng2-ckeditor is doing the same thing - wrapping the ckeditor. And I could get it working with my Angular-seed project. Can you add tsconfig.json and do the same thing to in your project?

Regards
Cheng

Where do I get quill.snow.css from?

Hi again,

I don't want to use the Quill CDN for quill.snow.css and would like to reference it from the Node modules in my angualar-cli json file so it is packaged in WebPack. However, I can't seem to find it in ngx-quill or quill - where can I find it?

Source code has linting errors

Hi again,

When I include the SRC version, my AngularCLI (with strict linting) includes any issues found in the ngx-quill node module:

I am not sure why it is doing it but no other node modules do this (my linting is set to exclude Node Modules). Perhaps it is something to do with the fact that it is using the source?

AOT Build Error - Cannot find quill.module.js

Hello,

Everything works well when using JIT with ionic serve, but when I try to compile with "ionic build --release --prod", then I get this error when loading my app in the browser:

main.js:93 Uncaught Error: Module build failed: Error: ENOENT: no such file or directory, open '/Users/etc/app/node_modules/ngx-quill/src/quill.module.js'

There's a related issue commenting on how Node Modules are typically pre built here: ionic-team/ionic-app-scripts#632

If you have any advice then I'd really appreciate it. Here below are my system details.

Thank you
Tom


ionic info:

Cordova CLI: 6.3.1
Ionic Framework Version: 2.2.0
Ionic CLI Version: 2.2.1
Ionic App Lib Version: 2.2.0
Ionic App Scripts Version: 1.2.2
ios-deploy version: 1.9.1
ios-sim version: 5.0.3
OS: macOS Sierra
Node Version: v6.9.1
Xcode version: Xcode 8.3.2 Build version 8E2002

package.json excerpt:

"dependencies": {
"@angular/common": "2.4.8",
"@angular/compiler": "2.4.8",
"@angular/compiler-cli": "2.4.8",
"@angular/core": "2.4.8",
"@angular/forms": "2.4.8",
"@angular/http": "2.4.8",
"@angular/platform-browser": "2.4.8",
"@angular/platform-browser-dynamic": "2.4.8",
"@angular/platform-server": "2.4.8",
"@ionic-native/core": "^3.4.4",
"@ionic-native/onesignal": "^3.4.4",
"@ionic/storage": "2.0.0",
"cropperjs": "^0.8.1",
"intl": "^1.2.4",
"ionic-angular": "2.2.0",
"ionic-native": "2.4.1",
"ionicons": "3.0.0",
"moment": "^2.14.1",
"ng2-password-strength-bar": "^1.1.2",
"ng2-resource-rest": "1.12.2",
"rxjs": "5.0.1",
"sw-toolbox": "3.4.0",
"zone.js": "0.7.2"
},
"devDependencies": {
"@ionic/app-scripts": "1.2.2",
"typescript": "2.0.9"
},

Support for reactive forms (formControl)

First of all, thanks for your work on this great plugin! It works nicely with template driven forms.

I would, however, also like to use it with reactive forms. Thus i need to use "formControl" instead of "ngModel".

It appears that this is not fully supported(?)

My specific problem is that I am not able to use the "patchValue" method (of FormGroup) to insert content into the quill editor. The editor window remains empty after patching.

Entering new content by hand, and saving it, works fine however.

Please correct me if I'm mistaken.

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.