3
votes

I am using ckeditor with the jQuery adapter in an ajax scenario. The ckeditor is bound to a textarea element using jQuery.

The App:

I am designing an eLearning app for a client. Each course has many steps, drawn from a database table.

The UI of the app is just a form with Previous and Next buttons to navigate through the course.

The issue is with the admin facility, where Tutors can create and update the steps in each course.

Again, the admin facility is just a form with previous and next buttons. However, there is a field called StepText that I want to have html rather than plain text. Hence the requirement for a Rich Text Editor.

The scrolling from step to step is down with Ajax calls, that go off to the controller, get the data for the next (or previous) step, and inject it into the page. The ajax calls return html, created from a partial view.

The Problem:

The textarea is in this partial view. The CKEditor binds correctly the first time the page is loaded, but not for any subsequent ajax responses.

The result is that the ckEditor does not appear with all its goodness and I just get the unappealing nakedness of the textarea.

Any ideas why this is happening?

SETUP (Shown for Refrence) (Edited):

The following scripts are used:

<script type="text/javascript" src="@Url.Content("~/ckeditor/ckeditor.js")"></script>
 <script type="text/javascript" src="@Url.Content("~/ckeditor/adapters/jquery.js")"></script>

This allows me to use ckEditor with jQuery as follows:

$(document).ready(function () {
        $('#StepText').ckeditor(function () { }, { toolbar: 'Basic' });
    });

The text area is in a partial .cshtml file, within a div that acts as the target for the ajax call:

Edited!! to show how the ajax call is made !!

<div id="ajaxEditor">
  @using (Ajax.BeginForm(MVC.Admin.StepEditor.Edit(), new AjaxOptions { UpdateTargetId = "ajaxEditor", HttpMethod = "POST" },
    new { @name = "EditStepForm", @id = "EditStepForm" }))
{
    <div class="editor-label">
                @Html.LabelFor(model => model.StepText)
            </div>
            <div class="editor-field">
                @Html.TextAreaFor(model => model.StepText, 20, 68, null)
                @Html.ValidationMessageFor(model => model.StepText)
            </div>
   }
</div>

Response to Chris Marisic:

I tried rebinding the ckeditor in the OnSuccess of the ajax call, but when debugging the jQuery in Chrome, I get the following error:

Uncaught [CKEDITOR.editor]  The instance "myTextArea" already exists.

Which is what you would expect: the control name is still the same and there has not been a full page refresh.

3
Please give more code (i.e. what do you mean saying "ajax call overwrites"). And give the link to jQuery adapter for ckeditor (or is it build-in to standard ckeditor release?).artyom.stv
No, its not built in. If you want to use ckEditor with jQuery, you have to include the adapters file after the ckeditor.js file, as shown in the edited version above.awrigley
Is it as simple as calling the code that you run on document.ready to run after the ajax?Chris Marisic
What happens? Does the CKEditor isntance rollback to a textarea or are you just losing some UI elements?Gazillion
OK, an ajax call overwrites the page, putting back the same elements and ids but different data. On the textarea, where I had the CKeditor instance, now I have just the bald, naked textarea. The app is essentially a form that loads, via ajax, consecutive records from a database. will update the question given that these comments reflect confusion.awrigley

3 Answers

2
votes

Calling Chris Marisic:

If Chris Marisic wants to upgrade his comment to an answer, I will give him the biscuit.

I have solved as follows:

Ajax Form:

<div id="ajaxEditor">
@using (Ajax.BeginForm(MVC.Admin.StepEditor.Edit(), new AjaxOptions { UpdateTargetId = "ajaxEditor", HttpMethod = "POST", OnComplete="ReBindCKEditor" },
        new { @name = "EditStepForm", @id = "EditStepForm" }))
    {
    <div class="editor-label">
                @Html.LabelFor(model => model.StepText)
            </div>
            <div class="editor-field">
                @Html.TextAreaFor(model => model.StepText, 20, 68, null)
                @Html.ValidationMessageFor(model => model.StepText)
            </div>
   }
</div>

Javascript:

As the error coming back (see response to Chris Marisic in question) states that the CKEDITOR instance already exists, I need to get rid of it. So:

function BindCKEditor() {
    var elem = $('#StepText');
    elem.ckeditor(function () {}, { toolbar: [
        ['Source'],
        ['Preview'],
        ['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Scayt'],
        ['Undo', 'Redo', '-', 'Find', 'Replace', '-', 'SelectAll', 'RemoveFormat'],
        ['Image', 'Table', 'HorizontalRule'],
        ['Styles', 'Format'],
        ['Bold', 'Italic', 'Strike'],
        ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', 'Blockquote'],
        ['Link', 'Unlink', 'Anchor']
        ]
    });
}

BindCKEditor() just does what it says on the tin. It configures the editor and applies it.

Now, when I overwrite the text area with the ajax response, I need to delete that instance and recreate it. So:

function ReBindCKEditor() {
    delete CKEDITOR.instances['StepText'];
    BindCKEditor();
}

Note that I use ReBindCKEditor() in the OnComplete for the Ajax Form. I use oncomplete and not onsuccess, because I want it to rebind regardless of the success or otherwise of the ajax call.

SUCCESS!:

I can't recommend ckeditor highly enough. I thought this was going to be a nightmare to solve, but once I sat down and put such thoughts aside, I found that there was very concise documentation and good user forums.

I looked at a lot of other Rich Text Editors, and none were as good or as well supported. Yahoo's one looked good, but was a nightmare and documentation poor unless you are a javascript expert which I ain't.

I nearly went with lwrte, it looked good, but no support behind it.

I looked at other that shall remain unnamed.

But ckEditor really has impressed me. I can now roll it out where I want to with no fear or trepidation.

Note:

I am not working for CKEditor nor have any connection with them. This is genuine enthusiasm. Yes, such a thing does exist.

1
votes

this worked best for me using the jquery ckeditor adapter. I just call this when I load up some ajax with a ckeditor textarea.

function initCKEditor() {
$('.wysiwyg').ckeditor(function(e){
        delete CKEDITOR.instances[$(e).attr('name')];
    },{
        toolbar:
            [
                ['Bold','Italic','Underline','Strike','-','NumberedList','BulletedList','-','Paste','PasteFromWord','-','Outdent','Indent','-','Link','-','Maximize','-','Source']
            ],
        skin: 'office2003'
    }
);  

}

1
votes

I had this problem too, but the senario was a bit different, I used ckeditor in an Ajax.BeginForm but my form(partial view) is coming up in a jquery ui dialog, the first answer gave me a great idea to fix that. I did rebinding for ckeditor when dialog is coming up!

  1. I've edited this code and I added required attribute/validation (field has required attribute in my model) , it works perfect! look at this magical codes: $('input[type=submit]').bind('click', function () { $('.ckeditor').ckeditorGet().updateElement(); });
  2. Also I added some configuration codes for ckeditor! starts by CKEDITOR.editorConfig =
  3. I have different dialogs come up on parent page and the only thing I need to do is, adding new { @class = "ckeditor" }) to TextAreaFor(partial view) and adding this line in parent page delete CKEDITOR.instances['FieldName'];
  4. Bravo ckeditor team

in the parent page i call the dialog:

function openDialog() {
    $('#dialog').dialog('open');

    delete CKEDITOR.instances['Ingredient'];

    CKEDITOR.editorConfig = function (config) {
        config.uiColor = "#EFEFEF";
        config.toolbar =
        [{ name: 'basicstyles', items: ['Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript', '-', 'RemoveFormat']}];
        config.width = 400;
        config.height = 100;
        config.autoParagraph = false;
        config.removePlugins = 'elementspath';
        config.resize_enabled = false;
        config.enterMode = CKEDITOR.ENTER_BR;
        config.startupFocus = true;
    };

    $('.ckeditor').ckeditor();
    $('input[type=submit]').bind('click', function () {
        $('.ckeditor').ckeditorGet().updateElement();
    });
}

in my partial view with Ajax.BeginForm:

<div class="editor-field">
        @Html.TextAreaFor(model => model.Ingredient, new { @class = "ckeditor" })
        @Html.ValidationMessageFor(model => model.Ingredient)
</div>

as you see I did NOT use OnCompelete or OnBegin. hope it helps somebody!