Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CKEditor Blazor integration

I am trying to use CKeditor with Blazor. I used Online builder to create a custom build, with ImageUpload and Base64UploadAdapter, and it is integrated in BlazorApp.

I can successfully show it on the page, and put / get HTML content from it. Source of the working version for Blazor app is here https://gitlab.com/dn-misc/BlazorCKEditor1/

But as I would like to inser image as Base64 encoded string directly in HTML content, when I try to upload image I get following error: Assertion Failed: Input argument is not an HTMLInputElement (from content-script.js)

like image 548
Dino Novak Avatar asked Oct 20 '25 13:10

Dino Novak


1 Answers

I have successfully implemented Chris Pratt implementation. Check this out:

IMPORTANT: this works with ClassicEditor ONLY.

Blazor component, I called mine InputCKEditor.razor. Yeah I know, no very original.

@namespace SmartApp.Components

@inherits InputTextArea
@inject IJSRuntime JSRuntime

<textarea @attributes="AdditionalAttributes"
          id="@Id"
          class="@CssClass"
          value="@CurrentValue"></textarea>

@code {
    string _id;
    [Parameter]
    public string Id
    {
        get => _id ?? $"CKEditor_{_uid}";
        set => _id = value;
    }

    readonly string _uid = Guid.NewGuid().ToString().ToLower().Replace("-", "");

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
            await JSRuntime.InvokeVoidAsync("CKEditorInterop.init", Id, DotNetObjectReference.Create(this));

        await base.OnAfterRenderAsync(firstRender);
    }

    [JSInvokable]
    public Task EditorDataChanged(string data)
    {
        CurrentValue = data;
        StateHasChanged();
        return Task.CompletedTask;
    }

    protected override void Dispose(bool disposing)
    {
        JSRuntime.InvokeVoidAsync("CKEditorInterop.destroy", Id);
        base.Dispose(disposing);
    }
}

Then, you have to put this in your interop.js

CKEditorInterop = (() => {
    var editors = {};

    return {
        init(id, dotNetReference) {
            window.ClassicEditor
                .create(document.getElementById(id))
                .then(editor => {
                    editors[id] = editor;
                    editor.model.document.on('change:data', () => {
                        var data = editor.getData();

                        var el = document.createElement('div');
                        el.innerHTML = data;
                        if (el.innerText.trim() === '')
                            data = null;

                        dotNetReference.invokeMethodAsync('EditorDataChanged', data);
                    });
                })
                .catch(error => console.error(error));
        },
        destroy(id) {
            editors[id].destroy()
                .then(() => delete editors[id])
                .catch(error => console.log(error));
        }
    };
})();

Now time to use it:

<form>
    <label class="col-xl-3 col-lg-3 col-form-label text-sm-left text-lg-right">Description</label>
    <div class="col-lg-9 col-xl-6">
        <InputCKEditor @bind-Value="_model.Description" class="form-control form-control-solid form-control-lg"></InputCKEditor>
        <ValidationMessage For="@(() => _model.Description)" />
    </div>
</form>
like image 158
Javier Contreras Avatar answered Oct 23 '25 06:10

Javier Contreras