0

Normally I just use normal @Html.BeginForm forms and post them with ajax as per below:

$("btnSubmit").on("click", function () {
    var $form = $("form#frmNewArticle");
    $form.submit();
});

$("form#frmNewArticle").submit(function () {
    $.post($(this).attr("action"), $(this).serialize(), function (data) {
        if (!data.IsOK) {
            // in the case of an error, just display info about it to the user
        }
        else {
            // if the post went well, go back to the article listing
            window.location.href = '@Url.Content("~/Articles")';
        }
        return;
    });

    return false;
});

I've been doing this with great success all over my MVC site but now I want a rich text editor, so I've now implemented tinymce and I want to use it's inline mode which makes use of contenteditable html elements:

<h2>Article Title</h2>
<h3 class="editable"></h3>

<h2>Article Content</h2>
<div class="editable"></div>

<input type="button id="btnSubmit" value="Create" />

<script>
    tinymce.init({
        selector: "div.editable",
        theme: "modern",
        plugins: [
            ["advlist autolink link image lists charmap print preview hr anchor pagebreak spellchecker"],
            ["searchreplace wordcount visualblocks visualchars code fullscreen insertdatetime media nonbreaking"],
            ["save table contextmenu directionality emoticons template paste"]
        ],
        add_unload_trigger: false,
        schema: "html5",
        inline: true,
        toolbar: "undo redo | styleselect | bold italic underline | alignleft aligncenter alignright alignjustify | bullist numlist | outdent indent | link image     | print preview media",
        statusbar: false
    });
    tinymce.init({
        selector: "h3.editable",
        theme: "modern",
        add_unload_trigger: false,
        schema: "html5",
        inline: true,
        toolbar: "undo redo",
        statusbar: false
    });
</script>

I've seen other questions on here that suggested making the editable elements populate actual input fields on the page which get posted but that seems a bit inefficient to me but then I'm not the most knowledgeable in this area...

In the past I have specified the exact data that gets posted (as per below), but that doesn't feel right to me either...

$.ajax({
    type: 'POST',
    url: '/includes/contact.php',
    data: { name: $('#contact-name').val(),
            from: $('#contact-email').val(),
            subject: $('#contact-subject').val(),
            message: $('#contact-message').val()
    }, // end data
    success: function clearFields() {
        // clear form fields
    } // end success
}); // end ajax

Can someone tell me if I'm just being pedantic about how this is done or if I'm onto something with either of these approaches?

If I'm not, how is it meant to be done?

Thanks in advance!

Ortund
  • 8,095
  • 18
  • 71
  • 139
  • You can use `[ValidateInput(false)]` attribute at `Controller Action` – Koti Panga Nov 12 '14 at 10:46
  • @VenkataPanga what I'm struggling with is getting the data to the controller – Ortund Nov 12 '14 at 10:47
  • Yes To post HTML tags Content to controller action we should apply the attribute decoration either at before Action method with `[ValidateInput(false)]` OR at model property Like `[AllowHtml]` – Koti Panga Nov 12 '14 at 10:53

1 Answers1

0

Below is the working TestCase for you.

div html content wont be serialized so while posting the form, workaround to achieve this case is take help of hidden textarea fields for content-editable sections.

In this case HeaderTitleHtml and ArticleHtml fields rendered as hidden TextAreas and before form submit we set these values with respective editable controls HTML.

See below button click handler code for understanding:

$("#btnSubmit").on("click", function () {
    $("#HeaderTitleHtml").html($("h3.editable").html());
    $("#ArticleHtml").html($("div.editable").html());
    $("form#frmNewArticle").submit();
});

Complete Source-Code

Model: ArticleModel

namespace TestCases.MVC.Models
{
    public class ArticleModel
    {
        [AllowHtml]
        public string HeaderTitleHtml { get; set; }

        [AllowHtml]
        public string ArticleHtml { get; set; }
    }

}

Controller Actions:

public class ArticlesController : Controller
{
    public ActionResult ArticleContent()
    {
        return View();
    }

    [HttpPost]
    public ActionResult SaveArticle(ArticleModel model)
    {
        if (ModelState.IsValid) {

            //Perform some business logic here
            //Save Title and Article Html in database.

            return Json(new { IsOK = true });
        }

        return Json(new { IsOK = false });
    }
}

View: ArticleContent.cshtml

@model TestCases.MVC.Models.ArticleModel
<style>
    .htmlTextHolder {
        display: none;
    }
</style>
@using (Html.BeginForm("SaveArticle", "Articles", FormMethod.Post, new { id = "frmNewArticle" })) {
    @Html.TextAreaFor(model => model.HeaderTitleHtml, new { @class = "htmlTextHolder" })
    @Html.TextAreaFor(model => model.ArticleHtml, new { @class = "htmlTextHolder" })
    <div>
        <h2>Article Title</h2>
        <h3 class="editable">Title Text goes here</h3>

        <h2>Article Content</h2>
        <div class="editable">Article Rich Text goes here</div>

        <p>
            <input type="button" id="btnSubmit" value="Create" />
        </p>

    </div>
}
@section scripts{
    <script src="//tinymce.cachefly.net/4.1/tinymce.min.js"></script>
    <script>
        tinymce.init({
            selector: "div.editable",
            theme: "modern",
            plugins: [
                ["advlist autolink link image lists charmap print preview hr anchor pagebreak spellchecker"],
                ["searchreplace wordcount visualblocks visualchars code fullscreen insertdatetime media nonbreaking"],
                ["save table contextmenu directionality emoticons template paste"]
            ],
            add_unload_trigger: false,
            schema: "html5",
            inline: true,
            toolbar: "undo redo | styleselect | bold italic underline | alignleft aligncenter alignright alignjustify | bullist numlist | outdent indent | link image     | print preview media",
            statusbar: false
        });

        tinymce.init({
            selector: "h3.editable",
            theme: "modern",
            add_unload_trigger: false,
            schema: "html5",
            inline: true,
            toolbar: "undo redo",
            statusbar: false
        });

        $("#btnSubmit").on("click", function () {
            $("#HeaderTitleHtml").html($("h3.editable").html());
            $("#ArticleHtml").html($("div.editable").html());
            $("form#frmNewArticle").submit();
        });

        $("form#frmNewArticle").submit(function () {
            $.post($(this).attr("action"), $(this).serialize(), function (data) {
                if (!data.IsOK) {
                    // in the case of an error, just display info about it to the user
                }
                else {
                    // if the post went well, go back to the article listing
                    window.location.href = '@Url.Content("~/Articles")';
                }
                return;
            });

            return false;
        });

    </script>
}

Regarding AllowHtml (vs) ValidateInput attributes

To send serialized data and HTML content from rich text editor like tinymce to Controller Action we should add attributes to allow HTML Content on Action method or at Model Field.

In model

public class ArticleModel 
{
    [AllowHtml]
    public string htmlContainer { get; set; }

    public string otherProperty { get; set; }
}

In Controller Action (Not Recommended)

ASP.NET has built-in request validation that automatically helps protect against XSS and HTML injection attacks. If you want to explicitly disable this validation you could decorate the action you are posting to with the [ValidateInput(false)] attribute.

[HttpPost]
[ValidateInput(false)]   
public ActionResult SaveArticle(ArticleModel model)
{
    var JResult = new JsonResult();
    if (ModelState.IsValid)
    {
        ...
    }
    return JResult;
}

[ValidateInput(false)] is not best case as per security concerns
See this: MVC Side effect of using [HttpPost, ValidateInput(false)]

Best Practice MSDN said here: http://msdn.microsoft.com/en-us/magazine/hh708755.aspx Just use [AllowHtml] on your model property you want take html.

Community
  • 1
  • 1
Koti Panga
  • 3,660
  • 2
  • 18
  • 21