ckeditor5-angular icon indicating copy to clipboard operation
ckeditor5-angular copied to clipboard

Integration with Angular Material

Open wynfred opened this issue 5 years ago • 24 comments

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

wynfred avatar Aug 16 '18 20:08 wynfred

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 avatar Aug 20 '18 12:08 ma2ciek

@ma2ciek Thanks!

wynfred avatar Aug 21 '18 16:08 wynfred

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 avatar Aug 24 '18 18:08 ma2ciek

@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

wynfred avatar Aug 24 '18 22:08 wynfred

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.

wynfred avatar Aug 25 '18 06:08 wynfred

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 avatar Aug 27 '18 10:08 ma2ciek

@ma2ciek could you take a look at my second comment? didn't @ you on that one

wynfred avatar Aug 27 '18 15:08 wynfred

Could make more clear how you made the template configurable? Maybe I misunderstood something, but the whole approach seems wrong to me because:

  1. You use matInput on the textarea that is hidden and doesn't take place in content editing.
  2. 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).

ma2ciek avatar Aug 27 '18 16:08 ma2ciek

  1. 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> And this.elementRef.child......child can get to the <textarea>, which will be passed to ckeditor.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.

wynfred avatar Aug 28 '18 16:08 wynfred

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?

ma2ciek avatar Aug 29 '18 11:08 ma2ciek

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.

wynfred avatar Aug 29 '18 15:08 wynfred

@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?

wynfred avatar Sep 13 '18 22:09 wynfred

I'd go with a separate component which would proxy some inputs and outputs. But I'm not sure how easy and clean that solution would be.

ma2ciek avatar Sep 19 '18 09:09 ma2ciek

@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.

naveedahmed1 avatar Oct 07 '18 21:10 naveedahmed1

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 avatar Oct 08 '18 12:10 ma2ciek

@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.

naveedahmed1 avatar Oct 08 '18 15:10 naveedahmed1

@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

naveedahmed1 avatar Oct 08 '18 18:10 naveedahmed1

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.

ma2ciek avatar Oct 11 '18 13:10 ma2ciek

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>&nbsp;</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:

textarea

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

naveedahmed1 avatar Oct 11 '18 15:10 naveedahmed1

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.

wynfred avatar Feb 07 '19 21:02 wynfred

ping @ma2ciek ⬆️

Reinmar avatar Mar 04 '19 12:03 Reinmar

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.

ma2ciek avatar Mar 05 '19 11:03 ma2ciek

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.

wynfred avatar Mar 05 '19 18:03 wynfred

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.

jorroll avatar May 16 '19 07:05 jorroll