ckeditor5-angular
ckeditor5-angular copied to clipboard
Integration with Angular Material
Is there a way to support matInput
in angular material, so it can work like a normal textarea with material design?
Update: https://github.com/wynfred/mat-contenteditable now has a directive that interacts with ckeditor5
I'm not sure if that can be easily achieved right now.
I've found this guide - https://material.angular.io/guide/creating-a-custom-form-field-control, but it'd require few changes in the <ckeditor>
component.
I'll try to dig more into this issue later.
If someone is interested in that too, please, give a 👍.
@ma2ciek Thanks!
I've created a POC on the t/21 branch - you can see how it's implemented. But we won't be able to ship with the current solution as it depends directly on the Material library.
IMO it'd be the best to create a wrapper for the <ckeditor>
component and export it as e.g. <mat-ckeditor>
component as the Material design is not the only one and people will have different requirements. It could proxy <ckeditor>
events and properties and implements the MatFormFieldControl
, so the Material lib could handle it.
OTOH there aren't many integrations implementing this feature and Material Design is the most popular styling in the Angular ecosystem so IMO it's worth investing in this topic as there's interest.
@ma2ciek Thanks for the POC!
I just found ckeditor supports a tagName
so it is able to create a
I'm new to Angular so I'm not sure it will make the
I tried this myself and finally got it kind of worked.
Basically what I did is changing the template to
template: '<mat-form-field><textarea matInput></textarea></mat-form-field>'
and pass the textarea element to ckeditor.create
using elementRef
on
https://github.com/ckeditor/ckeditor5-angular/blob/master/src/ckeditor/ckeditor.component.ts#L210
This way ckeditor can use an @Input()
to determine which template should be used, so user can choose whether to use mat design or not. There are still some CSS issues that I'm not sure how to solve.
I just found ckeditor supports a tagName so it is able to create a
Yes, we support tagName
, but only for the original (source) element which is being replaced by the <div contenteditable=true>
. So your solution won't work as this textarea
is hidden and all events, data, and so on, are provided in the div
.
@ma2ciek could you take a look at my second comment? didn't @ you on that one
Could make more clear how you made the template configurable? Maybe I misunderstood something, but the whole approach seems wrong to me because:
- You use
matInput
on thetextarea
that is hidden and doesn't take place in content editing. - With the static
<mat-form-field><textarea matInput></textarea></mat-form-field>
angular needs to know what the<mat-form-field>
stands for, so this implies making the Material Angular a dependency (which is ok for your needs, but not for us, as someone might need an integration with e.g.ng-bootstrap
).
- Maybe we don't have to make it work like a matInput since ckeditor should handle most of the functions, but we can make it looks like a matInput.
2.So i was thinking about something like
<div *ngIf="config.useMatDesign"><mat-form-field><textarea matInput></textarea></mat-form-field></div>
Andthis.elementRef.child......child
can get to the<textarea>
, which will be passed tockeditor.create
If MatDesign is set to false, just use the old way to create element. Ckeditor has to depend on mat-design though, but users don't have to.
Maybe we don't have to make it work like a matInput since ckeditor should handle most of the functions, but we can make it looks like a matInput.
But you're still talking about the textarea element, which is hidden. Could you provide some code sample - simple repository / fork of this repository? Did you run the editor on that branch https://github.com/ckeditor/ckeditor5-angular/tree/t/21?
Yes, I tried that branch and it works fine. I'm trying to find a way to make it configurable, but maybe I did it wrong. I'll make a branch/PR to show my implementation later.
@ma2ciek you're right, my implementation isn't right. what i saw before is a textarea
with a tool bar from ckeditor so i thought it works. it's not a div contenteditable so it doesn't interact with the tool bar correctly.
so is it possible to proceed with the proxy solution or just make the component with matformfieldcontrol
a separate component?
I'd go with a separate component which would proxy some
@ma2ciek any update on support of MatFormFieldControl
?
I agree with you that the proper way would be to create a wrapper for the <ckeditor>
component and export it as <mat-ckeditor>
.
Actually <mat-form-field>
is a component used to wrap Angular Material components and apply common Text field styles such as the underline, floating label, and hint/error messages.
Therefore by implementing custom form field control, we would be able to use <mat-ckeditor>
inside <mat-form-field>
, so that when the editor is focused, we see the blue underline similar to what we see when user set focus on matInput
, similarly when the control has error it will show red underline, would allow floating labels etc.
For now, I think that this should be another library, let's say ckeditor5-material-angular
that would depend on the ckeditor5-angular
as I don't see any valid approach to support the Material design without having @angular/material
a peer or explicit dependency in the package.json
file.
I made a simple demo on the https://github.com/ckeditor/ckeditor5-angular/tree/t/21, where the MatFormFieldControl
needs to be explicitly imported. But maybe there's another solution without importing it to satisfy the angular DI that I don't know.
@ma2ciek you are right apparently it seems that in order to support Material design we will have to add @angular/material as dependency in the package.json file.
So, creating a new library ckeditor5-angular-material
would be the best option.
It would be really great if the toolbar in ckeditor5-angular-material
also support Angular Material Icons and Buttons. So that we can get the nice ripple effect and are aligned with material specs.
@ma2ciek so we have an answer here https://github.com/angular/material2/issues/4444#issuecomment-427930349 . We would need a separate package like ckeditor5-angular-material
Thanks for the research @naveedahmed1!
Actually
is a component used to wrap Angular Material components and apply common Text field styles such as the underline, floating label, and hint/error messages.
I've checked it and with the default styling only hint and the underline on focus work well. Rest is hidden or don't work (I assume that it happens because of the contenteditable div that replaces the textarea element, which can cause some CSS issues).
I think how much work would be needed to make CKEditor 5 buttons and some other elements look more material as it would require deep integration into our UI library but that's definitely out of the scope of this package.
Thanks for the explanation @ma2ciek .
I edited SimpleUsageComponent
and its template:
Component
import { Component } from '@angular/core';
import * as ClassicEditorBuild from '@ckeditor/ckeditor5-build-classic';
import { CKEditor5 } from '../../ckeditor/ckeditor';
import { ChangeEvent, FocusEvent, BlurEvent } from '../../ckeditor/ckeditor.component';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
@Component( {
selector: 'app-simple-usage',
templateUrl: './simple-usage.component.html',
styleUrls: [ './simple-usage.component.css' ]
} )
export class SimpleUsageComponent {
public Editor = ClassicEditorBuild;
public isDisabled = false;
public editorData =
`<p>Getting used to an entirely different culture can be challenging.
While it’s also nice to learn about cultures online or from books, nothing comes close to experiencing cultural diversity in person.
You learn to appreciate each and every single one of the differences while you become more culturally fluid.</p>`;
public componentEvents: string[] = [];
public form: FormGroup;
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
details: ["", Validators.required]
});
}
toggleDisableEditors() {
this.isDisabled = !this.isDisabled;
}
onReady( editor: CKEditor5.Editor ): void {
this.componentEvents.push( 'The editor is ready.' );
}
onChange( event: ChangeEvent ): void {
this.componentEvents.push( 'Editor model changed.' );
}
onFocus( event: FocusEvent ): void {
this.componentEvents.push( 'Focused the editing view.' );
}
onBlur( event: BlurEvent ): void {
this.componentEvents.push( 'Blurred the editing view.' );
}
}
Template
<h2>Simple usage</h2>
<p><strong>Note:</strong> Open the console for additional logs.</p>
<p>
<button mat-button (click)="toggleDisableEditors()">{{ isDisabled ? 'Enable editors' : 'Disable editors' }}</button>
</p>
<p>value: {{form.get('details').value}}</p>
<p>hasError required: {{form.get('details').hasError('required')}}</p>
<p>valid: {{form.get('details').valid}}</p>
<form [formGroup]="form">
<h3>Classic build</h3>
<mat-form-field>
<ckeditor formControlName="details"
[data]="editorData"
[editor]="Editor"
[disabled]="isDisabled"
id="classic-editor"
name="classic-editor"
(ready)="onReady($event)"
(change)="onChange($event)"
(focus)="onFocus($event)"
(blur)="onBlur($event)">
</ckeditor>
</mat-form-field>
</form>
<h4>Component events:</h4>
<ul>
<li *ngFor="let eventInfo of componentEvents">
<span>{{ eventInfo }}</span>
</li>
</ul>
If you run with these changes you will notice that:
Initial value of the control is null/empty and if you have a required validator applied to control you will notice that since control value is show as empty it will have error. i.e. you will see:
hasError required: true
hence validity of the control is false.
Now if you change content of the textarea even by just pressing any key, you will notice that value of the field is not longer empty and control is now shown as valid.
Regarding why error underline is not shown when form control is invalid, may be its related to this https://github.com/angular/material2/issues/12277 (I think there is a solution provided there as well).
There is another issue as well. If you clear all contents of the textarea. You will notice it actually doesn't make the textarea value as empty/null. After clearing the value of rich textarea it still has below value:
<p> </p>
Regarding your comment about the styling of the buttons on toolbar, I had some idea but thought that I might be missing something. When using CKEditor 4 in my Angular App, what I did was that I removed all borders of the CKEditor richtextarea except the border-bottom of the toolbar. It looked something like this:
May be we can try something similar here?
Also did you get a chance to look at ng2-ckeditor
plugin for angular? Its for CKEditor 4 but may be can provide some direction:
https://github.com/chymz/ng2-ckeditor
Some updates on this issue: I added a directive in mat-contenteditable that makes ckeditor work with form-field. @ma2ciek Let me know if you are interested in making the directive part of this package.
ping @ma2ciek ⬆️
Hi, @wynfred.
Sorry for the delay.
I've just checked the integration with the material design directives that you provided. I've got some small issues that would be needed to fix (e.g. enter creates an error now and the BalloonDirective uses internal stuff), but the overall look was rather positive. Nevertheless, the problem with imports is still present. It's better (as the directive can be imported alone without the need to import the whole @angular/material
by default) but since the files would be build together we would need to add @angular/material
to the peer dependencies. And I'm still not sure about that since we don't want to force users to install an additional package, which is optional for usage.
Hi @ma2ciek ,
I think you can a create new angular project within this repo for the directive which has it's own dependencies. And it will be nice to provide a build (like the new Balloon block editor with mat-icon button and border removed) to be used together with the directive.
I didn't use it with a classic build so I guess the enter creates error
is build related. Can you try it with an inline or balloon build and see if the error still exists?
The BalloonDirective has a lot of hacks and I don't I think it's a good idea to provide it in an official package. This is also a reason I recommend an "official ckeditor material build" above.
Oh boy. So it looks like you've (wisely) already decided not to make @angular/material
a peer dependency, but just to pile on: I use Angular's Material library extensively (and rather exclusively), and I'm still against ckeditor5-angular
marking @angular/material
as a peer dependency.
There are a few reasons why, but the most important is that users should be able to hook any custom component into ckeditor's UI. For example, you should be able to use any arbitrary custom component as the toolbar's "bold" button (this would also allow you to use a mat-icon-button
component as the toolbar's "bold" button).
If marking @angular/material
as a peer dependency is required in order to mixin angular material components, that seems to be indicative of a larger problem with the ckeditor5-angular
api.
To be clear, I don't want to hate on the ckeditor5-angular
api--every project needs to start somewhere and it's really nice to see an official angular integration for a rich text editor--but, in terms of future directions to move in, I'd strongly prefer to see ckeditor5-angular
work on supporting a generic way of hooking custom components into the UI.
As a side note, further up in this thread there was talk of ckeditor5-angular
implementing the MatFormFieldControl
interface. From the material docs (emphasis mine):
It is possible to create custom form field controls that can be used inside
<mat-form-field>
. This can be useful if you need to create a component that shares a lot of common behavior with a form field, but adds some additional logic.
In general, it's not obvious to me that a rich text editor shares a lot of common behavior with a MatFormField. Where it is appropriate for a rich text editor's styling to overlap with a MatFormField, I suspect that the manner of achieving that look would be different. For example
- A rich text editor has a toolbar,
MatFormField
elements do not - The MatFormField
mat-error
pops up at the bottom of an input, but the bottom of a rich text editor can easily be off screen - etc
If the angular team ever implements an official @angular/material
rich text editor, it's not obvious to me that they would bother implementing MatFormFieldControl
.
PS: while making @angular/material
a peerDependency seems heavy handed, making @angular/cdk
a peerDependency seems fine (it's explicitly designed to be a peerDependency, after all).
PPS: I'll point out that official Material themed apps provided by google, such as the iOS Gmail app, do not implement a mat-form-field
styled rich text editor.