Here is the scenario. I want to use CKEditor for a rich text field on a form, but for whatever reason I cannot get the contents from the textarea to the server and back to the page without encoding problems. Here is the little sample program I wrote up to try and figure out what is going on. First, my view model:
HomeViewModel.cs
namespace CkEditorTest.Models
{
public class HomeViewModel
{
[Required]
[DataType(DataType.Html)]
[Display(Name = "Note")]
public string Note { get; set; }
}
}
Now my controller:
HomeController.cs
using System.Web.Mvc;
using CkEditorTest.Models;
namespace CkEditorTest.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new HomeViewModel());
}
[HttpPost]
[ValidateInput(false)]
public ActionResult Index(HomeViewModel model)
{
return View(model);
}
}
}
And finally, my view:
Index.cshtml
#model CkEditorTest.Models.HomeViewModel
#{
ViewBag.Title = "CKEditor Test";
}
#section head
{
<script type="text/javascript" src="#Url.Content("~/Scripts/ckeditor/ckeditor.js")"></script>
<script type="text/javascript" src="#Url.Content("~/Scripts/ckeditor/adapters/jquery.js")"></script>
<script type="text/javascript">
$(document).ready(function () {
$("#Note").ckeditor();
});
</script>
}
<h2>CKEditor Test</h2>
#using (Html.BeginForm())
{
#Html.LabelFor(m => m.Note)<br /><br />
#Html.TextAreaFor(m => m.Note)<br />
<input type="submit" />
}
#if (!String.IsNullOrEmpty(Model.Note))
{
<div id="noteText">#Model.Note</div>
}
No matter what I do, I cannot display the Model.Note property as html on my view. By the time it reaches the view it is HTML encoded (i.e. <p> etc...). Here is what the form looks like pre-post:
pre-post http://www.matthewkimber.com/images/so/pre-post.png
And here is what the result is in the div below the "Submit" button:
post result http://www.matthewkimber.com/images/so/posted.png
I've set a breakpoint within Visual Studio and it shows as bare angle brackets (no encoding on HTML elements, just characters).
breakpoint results http://www.matthewkimber.com/images/so/dataInsideTheActionMethod.png
This, of course, is the stripped down test. I've tried encoding it, decoding it both in the view and in the controller to no avail.
By default everything is encoded when you use razor. I think you're looking for the Raw method.
It would also be a good idea to check the response using Fiddler or Firebug.
Try this:
#Html.DisplayTextFor(modelItem => item.Note)
You can also use HtmlString("")
Related
This is my form in AlertSelect.cshtml:
#model edxl_cap_v1_2.Models.ContentViewModels.AlertViewModel
#{
ViewData["Title"] = "AlertSelect";
Layout = "~/Views/Shared/_CapCategoryLayout.cshtml";
}
<head>
<meta name="viewport" content="width=device-width" />
<title>#ViewBag.Title</title>
</head>
#{
<h4>#Model.Alerts.Count Alerts</h4>
<form>
<select asp-for="SelectedAlertIndex" asp-items="#Model.Alert_Identifiers">
<option>Select one</option>
</select>
<asp-controller ="Alerts" asp-action="LoadAlert" method="post">
<br />
<input type="submit" name="LoadAlert" value="LoadAlert" />
</form>
}
And this is my LoadAlert() controller action:
[HttpPost]
public IActionResult LoadAlert(Alert obj, string LoadAlert)
{
if (!string.IsNullOrEmpty(LoadAlert))
{
ViewBag.Message = "Alert loaded successfully";
}
return View("/Views/Alerts/Index", obj);
}
AlertSelect.cshtml displays the standard header and left column and the drop down list and when selected and submitted, the page resets instead of displaying /Views/Alerts/Index.cshtml with the data filled in correctly. However the url reflects the selected item: http://localhost:61453/alerts/AlertSelect?SelectedAlertIndex=2&LoadAlert=LoadAlert.
I'm close but clearly missing something and any help would very welcome. I'd also like to display the selected item on the index.cshtml page to reinforce for the user the alert message they're working on, but the viewmodel (with two properties) for the Select Tag Helper is different from the model (with 16 properties) for the index page.
Made the change to my form per both comments and answer below, and instead of returning the view specified in LoadAlert() I have a mismatch of models: "InvalidOperationException:
The model item passed into the ViewDataDictionary is of type
'edxl_cap_v1_2.Models.Alert', but this ViewDataDictionary instance requires
a model item of type 'System.Collections.Generic.IEnumerable`1[edxl_cap_v1_2.Models.Alert]'.
AlertSelect.cshtml specifies:
#model edxl_cap_v1_2.Models.ContentViewModels.AlertViewModel
Alert.cs specifies:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace edxl_cap_v1_2.Models
{
public class Alert
{
[Key]
public int AlertIndex { get; set; }
[MaxLength(150)]
public string Alert_Identifier { get; set; }
public string Sender { get; set; }
public DateTime Sent { get; set; }
...
/Views/Alerts/Index.cshtml specifies:
#model IEnumerable<edxl_cap_v1_2.Models.Alert>
#using edxl_cap_v1_2.Models
#{
ViewData["Title"] = "Index";
Layout = "~/Views/Shared/_CapCategoryLayout.cshtml";
}
and _CapCategoryLayout specifies:
#model edxl_cap_v1_2.Models.ContentViewModels.AlertViewModel
<!DOCTYPE html>
I'm still not seeing where
The model item passed into the ViewDataDictionary is of type
'edxl_cap_v1_2.Models.Alert'
comes into play except as the namespace of Alert.cs. What do I need to change to keep out of these type mismatches?
The correct way to define form tag helpers in Core MVC should be like this (see documentation here):
<form asp-controller="Alerts" asp-action="LoadAlert" method="post">
<select asp-for="SelectedAlertIndex" asp-items="#Model.Alert_Identifiers">
<option>Select one</option>
</select>
<br />
<input type="submit" name="LoadAlert" value="LoadAlert" />
</form>
Note that default form submit method is GET if unspecified, as described in form element definition:
method = get|post [CI]
This attribute specifies which HTTP method will
be used to submit the form data set. Possible (case-insensitive)
values are "get" (the default) and "post".
The GET method will pass form values through URL with query string, hence you get the URL like following:
http://localhost:61453/alerts/AlertSelect?SelectedAlertIndex=2&LoadAlert=LoadAlert
Since no HttpGetAttribute action method found with those 2 query string parameters, the page will "reset" and displaying the same form again instead returning another view.
I am not getting validation messages? Any idea how to resolve? please take a look at the view, the model, and the controller code below. I also attached the js files maybe im missing files?
#model MvcApplication1.Models.Assesment
<link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
<script src="../../Scripts/jquery.validate.min.js" type="text/javascript"></script>
<script src="../../Scripts/jquery.validate.min.js" type="text/javascript"></script>
#using (Html.BeginForm())
{
#Html.TextBoxFor(m => m.name)
#Html.ValidationMessageFor(m=>m.name,"*Hello")
}
<input type="submit" value="submit" />
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Web.Mvc;
using System.ComponentModel.DataAnnotations;
namespace MvcApplication1.Models
{
public class Assesment
{
[Required]
public string name { get; set; }
}
}
public class RegisterController : Controller
{
[HttpGet]
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(Assesment assesment)
{
return View();
}
}
Your <input type="submit"> should be inside the form.
Also, you should pass the invalid model to the view when handling the POST
[HttpPost]
public ActionResult Index(Assesment assesment)
{
return View(assesment);
}
By the way, a typical HttpPost action looks like this:
[HttpPost]
public ActionResult Index(Assesment assesment)
{
if( ModelState.IsValid )
{
// Handle POST data (write to DB, etc.)
//...
// Then redirect to a new page
return RedirectToAction( ... );
}
// show the same view again, this time with validation errors
return View(assesment);
}
I'm not so experienced using MVC. I'm dealing with this situation. Everything works well until call the HttpPost method where has all its members null. I don't know why is not persisting all the data on it.
And everything works well, because I can see the data in my Html page, only when the user submit the information is when happens this.
[HttpGet]
public ActionResult DoTest()
{
Worksheet w = new Worksheet(..);
return View(w);
}
[HttpPost]
public ActionResult DoTest(Worksheet worksheet)
{
return PartialView("_Problems", worksheet);
}
This is class which I'm using.
public class Worksheet
{
public Worksheet() { }
public Worksheet(string title, List<Problem> problems)
{
this.Title = title;
this.Problems = problems;
}
public Worksheet(IEnumerable<Problem> problems, WorksheetMetadata metadata, ProblemRepositoryHistory history)
{
this.Metadata = metadata;
this.Problems = problems.ToList();
this.History = history;
}
public string Title { get; set; }
public List<Problem> Problems { get; set; } // Problem is an abstract class
public WorksheetMetadata Metadata { get; set; }
public ProblemRepositoryHistory History { get; set; }
}
And my razor view.... the razor view shows successfully my view. I realized something rare, please note in my 5 and 6 lines that I have HiddenFor method, well if I used that, when calls HTTPPOST persists the data, I don't know why.
#model Contoso.ExercisesLibrary.Core.Worksheet
<div id="problemList">
<h2>#Html.DisplayFor(model => model.Metadata.ExerciseName)</h2>
#Html.HiddenFor(model => model.Metadata.ExerciseName)
#Html.HiddenFor(model => model.Metadata.ObjectiveFullName)
#for (int i = 0; i < Model.Problems.Count; i++)
{
<div>
#Html.Partial(Contoso.ExercisesLibrary.ExerciseMap.GetProblemView(Model.Problems[i]), Model.Problems[i])
</div>
}
</div>
UPDATE
I'm using a static class to get the view name, but as I'm testing I'm just using this Partial view
#model Contoso.ExercisesLibrary.AbsoluteArithmetic.Problem1
<div>
<span style="padding:3px; font-size:18px;">#Model.Number1</span>
<span style="padding:5px; font-size:18px;">+</span>
<span style="padding:5px; font-size:18px;">#Model.Number2</span>
<span style="padding:5px; font-size:18px;">=</span>
<span style="font-size:18px">
#Html.EditorFor(model => model.Result, new { style = "width:60px; font-size:18px;" })
#Html.ValidationMessageFor(model => model.Result)
</span>
</div>
#section Scripts {
}
And here the user do the post
#model Contoso.ExercisesLibrary.Core.Worksheet
<form method="post">
#Html.Partial("_Problems", Model)
<input type="submit" value="Continue" />
</form>
The Model Binder will 'bind' or link input fields on your view to the model. It will not bind display fields (like label), that is why you need the HiddenFor it will add an <input type="hidden" which will then be bound to the Model when you Post.
You can use 'TempData'. It is used to pass data from current request to subsequent request means incase of redirection.
This link also helps you.
TempData
SO Tempdata
Make sure your form tag looks like the following, for instance the controller name, action method, the form method and an id for the form. I am referring to the #using statement. In my case the controller name is RunLogEntry, the action method is Create and the id is form.
Normal Post from View to Controller
#using (Html.BeginForm("Create", "RunLogEntry", FormMethod.Post, new { id = "form", enctype = "multipart/form-data" }))
{
<div id="main">
#Html.Partial("_RunLogEntryPartialView", Model)
</div>
}
If you want to post via Jquery, could do the following:
$.post("/RunLogEntry/LogFileConfirmation",
$("#form").serialize(),
function (data) {
//this is the success event
//do anything here you like
}, "html");
You must specify a form with correct attribute in your view to perform post action
<form action="Test/DoTest" method="post">
...
</form>
or
#using(Html.BeginForm("DoTest", "Test", FormMethod.Post)) {
...
}
The second is recommended.
Put your entire HTML code under:
#using(Html.BeginForm())
tag.
I found a lot of simulair questions but not a good clean solution that is working. I see a lot of custom code for getting this to work but why is that? Should this not working from the start?
What I think is strange, is that in IE9 it works but in Firefox and Chrome it is failing.
Everytime that im trying in Firefox or Chrome, I get the message "The field Birthday must be a date".
When I try the code below in a new MVC 4 RTM project, I can't get it to work. I see the DateTime.Now default as dd-MM-yyyy (Holland) in all the browsers but I can't submit it in Firefox and Chrome.
The globalization tag isn't set in web.config so it must be using the default. Im from Holland so it should get the client culture I guess.
public class RegisterModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
//[DataType(DataType.Date)]
public DateTime Birthday { get; set; }
}
[AllowAnonymous]
public ActionResult Register()
{
RegisterModel vm = new RegisterModel()
{
Birthday = DateTime.Now
};
return View(vm);
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
try
{
//WebSecurity.CreateUserAndAccount(model.UserName, model.Password);
//WebSecurity.Login(model.UserName, model.Password);
return RedirectToAction("Index", "Home");
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
Markup
<!-- language: lang-none -->
#model DateTimeWithDatePicker.Models.RegisterModel
#{
ViewBag.Title = "Register";
}
<hgroup class="title">
<h1>#ViewBag.Title.</h1>
<h2>Create a new account.</h2>
</hgroup>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<fieldset>
<legend>Registration Form</legend>
<ol>
<li>
#Html.LabelFor(m => m.UserName)
#Html.TextBoxFor(m => m.UserName)
</li>
<li>
#Html.LabelFor(m => m.Birthday)
#Html.EditorFor(m => m.Birthday)
</li>
</ol>
<input type="submit" value="Register" />
</fieldset>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
I was able to fix this by modifying the jQuery validator function for dates:
<script type="text/javascript">
$(function () {
var dateFormat="dd/mm/yy"; // en-gb date format, substitute your own
$("#Birthday").datepicker({
"dateFormat": dateFormat
});
jQuery.validator.addMethod(
'date',
function (value, element, params) {
if (this.optional(element)) {
return true;
};
var result = false;
try {
$.datepicker.parseDate(dateFormat, value);
result = true;
} catch (err) {
result = false;
}
return result;
},
''
);
});
The problem was jQuery validation and localization. It seems that there are localization files for messages and methods of the jQuery plugin. See my blog for a detail explaination of the problem and how I solved it.
http://www.locktar.nl/programming/mvc/localization-validation-in-mvc/
Edit:
I just released a new blog post with a refresh of all the localization problems and how to fix it for a DateTime property. See my new post MVC localization validation.
The globalization tag isn't set in web.config so it must be using the
default. Im from Holland so it should get the client culture I guess.
No, that's not correct. You could be perfectly fine from Holland and have configured your browser to use Chinese culture. The reason you cannot get this to work is probably because in FF and Chrome you don't have the correct culture.
Since you haven't specified a culture in your globalization element in web.config, ASP.NET MVC will use the one sent from the browser in the request header. For example if you configure your browser for en-US culture the following header will be set along each request:
Accept-Language:en-US,en;q=0.8
Here's how this is configured in Chrome:
So make sure you've put your desired language first in the list.
And if you want a reliable way to use the same syntax as the one defined in your DisplayFormat attribute during model binding you could write a custom model binder as shown here: https://stackoverflow.com/a/7836093/29407
Using Visual Studio 2010, MVC project
When my form is submitted (currently via javascript, but same results with a submit button), the action is getting an empty model with both of the fields in it being zero instead of containing the value I entered into the textbox. The Request object does contain the correct name/value pair in the Form collection.
Model values going the other way work fine - so based on my [HttpGet] CallDisplayHome() action, the form loads with the textbox value being 1.
If anyone has a clue as to why it would not work coming back via POST, I would sure appreciate it.
Model being used:
namespace TCSWeb.Models
{
public class CallDisplayModel
{
public int SelectedRowIndex;
public int SelectedLineID;
}
}
View:
#model TCSWeb.Models.CallDisplayModel
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<body>
/*
There a Jscript datatable here and a bunch of scripts for working with it in the header I am skipping because I am hoping they are not relevant
*/
<div>
#using (Html.BeginForm("Testing", "CallDisplay", FormMethod.Post, new { name = "submitSelLine" }))
{
#Html.TextBoxFor(m => m.SelectedLineID)
<p>
<input type="submit" value="Log On" />
</p>
}
</div>
<button onclick="SubmitSelCallRecord()">#LangRes.Strings.calldisplay_opencallrecord</button>
My controller actions:
[HttpGet]
public ActionResult CallDisplayHome()
{
TCSWeb.Models.CallDisplayModel temper = new CallDisplayModel();
temper.SelectedLineID = 1;
temper.SelectedRowIndex = 1;
return View(temper);
}
[HttpPost]
public ActionResult Testing(TCSWeb.Models.CallDisplayModel cdmodel)
{
return RedirectToAction("CallDisplayHome"); //breaking here, cmodel has zero for selectedlineid
}
You need to declare your CallDisplayModel variables as properties:
public int SelectedRowIndex { get; set; }
[Required]
public int SelectedLineID { get; set; }
You can also add a little bit of validation to make sure that the user provides the correct information.
Change your post method to the following:
[HttpPost]
public ActionResult Testing(TCSWeb.Models.CallDisplayModel temper)
{
//check if valid
if(ModelState.IsValid)
{
//success!
return RedirectToAction("CallDisplayHome");
}
//update error! redisplay form
return View("CallDisplayHome", temper);
}
And display the errors in your view like so:
#Html.ValidationMessageFor(m => m.SelectedLineID)
#Html.TextBoxFor(m => m.SelectedLineID)
I'm unsure what your submitSelCallRecord button is doing, as it is referencing the javascript that was omitted.