Reputation: 61
In angular 17 using ngx-editor. How to Create a custom dropdown menu in editor menu that appends to the editor body?
For example, the dropdown(email address) and options are [email protected], [email protected], and [email protected]. When any one is chosen, its text will be displayed in the editor body.
like below image email address,
enter image description here
I tired the following,
In HTML,
<div class="editor">
<ngx-editor-menu [editor]="options" [toolbar]="toolbar"></ngx-editor-menu>
<ngx-editor [editor]="options" formControlName="BODY"></ngx-editor>
</div
In Typescript:
import { Editor, TBItems, Toolbar, ToolbarCustomMenuItem, ToolbarDropdown, ToolbarDropdownGroupKeys, ToolbarItem, Validators } from 'ngx-editor';
@Component({
selector: 'app-template',
templateUrl: './template.component.html',
styleUrls: ['./template.component.scss']
})
export class TemplateComponent implements OnInit {
public modalData:any;
options: Editor = new Editor();
toolbar: Toolbar = [];
customDropdown: TBItems[];
public pluginsToCreate:any[] = [];
public formGroup: FormGroup;
constructor(private fb: FormBuilder) { }
ngOnInit(): void {
this.formGroup = this.fb.group({
BODY: [this.modalData.mailData.MESSAGE, Validators.required()]
})
const dropdownOptions: any= this.modalData.customer.map((option: any) => ({ value: option.DATA, label: option.TITLE }));
if (dropdownOptions.length > 0) {
this.customDropdown = [dropdownOptions] ;
}
this.toolbar = [
['bold', 'italic'],
['underline', 'strike'],
['code', 'blockquote'],
['ordered_list', 'bullet_list'],
[{ heading: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] }],
['link', 'image'],
['text_color', 'background_color'],
['align_left', 'align_center', 'align_right', 'align_justify'],
['horizontal_rule'],
this.customDropdown
];
}
}
Upvotes: 1
Views: 596
Reputation: 25
I recently had to implement this, it was hard to start at first because the Ngx-Editor documentation is brief, but after doing further reading i was able to get it working. I want to preface that im not a Ngx-Editor / Prosemirror Expert and there may be a better way of doing it.
<ngx-editor-menu />
takes a binding named [customMenuRef]
, in this binding you will pass a ref to a custom component that you want to display in the menu bar. You also need to pass the Editor
to your custom component to have access to the Editor's state and position for dynamic inserting. You named your editor instance as options
so your html template for TemplateComponent
should look something like this:
<div class="editor">
<ngx-editor-menu [editor]="options"
[toolbar]="toolbar"
[customMenuRef]="customDropDownMenu"></ngx-editor-menu>
<ngx-editor [editor]="options" formControlName="BODY"></ngx-editor>
</div>
<!-- Custom -->
<ng-template #customDropDownMenu>
<my-custom-dropdown-menu [editor]="options"></my-custom-dropdown-menu>
</ng-template>
Then your custom dropdown component should look something like what's below, in this component we will access the Editor
properties to find its current state and cursor location to update with the new selection. I don't know if you are using a styling library or not, the template in the custom ref component will be an angular dropdown menu component for the sake of creating a simplicity. but you can modify it to your needs.
Also you state you are using Angular 17, so we will be using the new Control Flow Blocks which use @For {}
instead of *ngFor
directive.
@Component({
selector: 'my-custom-dropdown-menu',
template: `
<div class="NgxEditor__Seperator"></div>
<div>
<button type="button" mat-button [matMenuTriggerFor]="appMenu">
Insert Emails
</button>
<mat-menu #appMenu>
@for (emailOption of emailOptions; track emailOption) {
<button type="button" (click)="insertEmail(emailOption)">{{ emailOption }}</button>
}
</mat-menu>
</div>
`
})
export class MyCustomDropdownMenuComponent {
@Input editor: Editor; // the editor reference being passed
public readonly emailOptions: string[] = ["[email protected]", "[email protected]", "[email protected]"];
insertEmail(email: string): void {
const {state, dispatch} = this.editor.view;
const {tr, selection} = state;
const insertPosition = selection.$from.pos;
dispatch(tr.insertText(email, insertPosition));
}
}
The method insertEmail()
in your CustomDropdownMenuComponent
takes in a string that will be our email to insert. the logic of the method is to destructure the state
and dispatch
objects from the editor reference we passed to our component.
Then from the state
object we destructure again to get the tr
and selection
object. we then get the current cursor position from the selection
object and assign it to our variable insertPosition
, then we use the dispatch
method to update the editor state and insert our email
param.
I'll create a stackblitz later on. in the meantime i hope this helps.
Upvotes: 0