How to submit a field inside a hidden dialog in MVC 3 - c#

I've got an MVC 3 form in a strongly typed view where one of the fields I need submitted is inside of a jQuery dialog. I have not been able to get this field to be part of the POST parameters submitted. Why oh why?
The View:
#model My.Models.DialogFieldModel
#{
ViewBag.Title = "Index";
}
<script type="text/javascript">
$(document).ready(function () {
$('#aDialog').dialog({
autoOpen: true,
height: 250, width: 400,
modal: true,
buttons: {
"Ok!": function () {
$(this).dialog("close");
}
}
});
});
</script>
<h2>Index</h2>
#using (Html.BeginForm("PostDialogField", "DialogField"))
{
#Html.ValidationSummary(true)
<fieldset>
#Html.HiddenFor(m => m.ID)
#Html.DisplayFor(m => m.message)
<div id="aDialog">
<h3>Fill in this message!</h3>
<div class="editor-field">
#Html.EditorFor(m => m.message)
</div>
</div>
<p><input type="submit" value="Submit Message" /></p>
</fieldset>
}
The Model:
using System;
namespace My.Models
{
public class DialogFieldModel
{
public int ID { get; set; }
public String message { get; set; }
public DialogFieldModel()
{
message = "Default";
}
}
}
The controller:
using System;
using System.Web;
using System.Web.Mvc;
using WellTrkd.Models;
namespace My.Controllers
{
public class DialogFieldController : Controller
{
public ActionResult Index()
{
DialogFieldModel dfm = new DialogFieldModel(); // set default message
return View(dfm);
}
[HttpPost]
public ActionResult PostDialogField(DialogFieldModel dfm)
{
String message = dfm.message;
if (message != "Default")
//Yay!
return RedirectToAction("Index");
else // Boo
return RedirectToAction("Index");
}
}
}
Unfortunately the #message field is never submitted along with the rest of the HTML POST parameters (checked in network tab of chrome dev view) unless I take it out of the dialog. The result is that in the PostDialogField action dfm.message contains the "Default" string, even if I've changed the message in the dialog.
I know I could add a hidden field to the form that is kept synchronized with the field in the dialog, but I feel I'm missing something. Any thoughts oh wise ones?

Your problem is that the element you turn into a dialog is moved out of the form towards a new dialog-element at the bottom of the DOM. And since it's not part of the form any more, it won't be submitted when the form is submitted.
If you'd destroy the dialog when closing it, it would be moved back to where it was, but I can't see if that's what is desired. The other option is to sync elements.

Related

MVC Partial View Validation Message not showing

I have this AjaxForm in my partial view:
#using (Ajax.BeginForm("CreateStarter", "Player", new AjaxOptions { HttpMethod = "POST"}))
{
#Html.HiddenFor(m => m.OwnerID)
#Html.HiddenFor(m => m.Species)
#Html.HiddenFor(m => m.Gender)
#Html.ValidationSummary(true)
<div class="editor-label">#Html.LabelFor(m => m.Nickname)</div>
<div class="editor-field">
#Html.EditorFor(m => m.Nickname)
#Html.ValidationMessageFor(m => m.Nickname,"", new { #class = "text-danger" })
</div>
<input type="submit" value="Choose my pokemon">
}
In my controller post action i verify whether or not the model is valid. If it is not i return the partial view. If the model is not valid, the partial view is returned, but the validation message is not shown. Am I missing something?
This is my action:
[HttpPost]
public ActionResult CreateStarter(PokemonViewModel pokemonViewModel)
{
if (ModelState.IsValid)
{
Pokemon pokemonEntity = PokemonMapper.GetPokemonEntity(pokemonViewModel);
_userService.AddStarterPokemonToPlayer(pokemonViewModel.OwnerID, pokemonEntity);
return RedirectToAction("PlayerProfile");
}
else
{
return PartialView("_CreateStarter", pokemonViewModel);
}
}
And this is my model:
public class PokemonViewModel
{
public int ID { get; set; }
public int Species { get; set; }
public string Gender { get; set; }
[Required]
public string Nickname { get; set; }
public int OwnerID { get; set; }
}
Dealing with partial views and ajax is not straight forward but it is not hard either. You need to do a few things:
Create a container (<div>) in your main page wherein you house you
partial view.
In Ajax.BeginForm, specify what to do in:
InsertionMode
UpdateTargetId
OnFailure
OnBegin
In your controller you cannot simply return the view if the model is not valid, because that will send an HTTP 200 OK status indicating the request succeeded. You need to inform the client that something is not right.
Step 1 and Step 2
Imagine you have a main page and within that you will need to create a container for your partial and put your partial there. Also note the OnXxx function handlers.
<html>
<!-- Your main page -->
<div id="your-partial-form-id-here>
#using (Ajax.BeginForm( /* other arguments */
new AjaxOptions
{
HttpMethod = "POST",
OnBegin = "DoBeforeUpdatingPage",
OnFailure = "DoWhenThereIsAnIssue",
OnSuccess = "DoOnSuccess",
UpdateTargetId = "id-of-html-element-to-update-on-success",
}))
{
//...code
}
</div>
</html>
All the OnXxx handlers are javascript method names which will handle each scenario. Here is what you may do in each:
<script type="text/javascript">
function DoBeforeUpdatingPage() {
// Maybe nothing or maybe form validation
}
function DoWhenThereIsAnIssue(responseFromServer) {
// In your case the responseFromServer is the partial view
// so you just need to inject the html into the container
// you have created earlier.
$('your-partial-form-id-here').html(responseFromServer);
// Also since there was an issue, you may want to clear
// the thing which is updated when it is success
$('#id-of-html-element-to-update-on-success').empty();
}
function DoOnSuccess(responseFromServer) { // whatever... }
</script>
Step 3
Return BadRequest to the client so the javascript OnFailure handler is invoked; in our case the DoWhenThereIsAnIssue will be invoked.
public ActionResult SomeAction(SomeModel model)
{
if (!ModelState.IsValid)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return PartialView("_NameOfPartial", model);
}
}

Counting how many times a button was clicked

I am wondering how can I count the number of times a button in my view was clicked using sessions and not using jQuery, just asp.net.
Here is my action method (empty) :
public ActionResult ClickCounter()
{
return View();
}
and my view :
#{
ViewBag.Title = "ClickCounter";
}
<h2>ClickCounter</h2>
#using (#Html.BeginForm())
{
<!-- form content here -->
#Session["num"] = 0;
<form method="post">
<fieldset>
<legend>Button clicks counter</legend>
<div>
<label for="Clciks">Clicks:</label>
<h2>#Session["num"]</h2>
</div>
<div>
<label> </label>
<input type="submit" value="Click!" class="submit" />
</div>
</fieldset>
</form>
}
Excuse me for the lame questions, but I am a complete novice and trying to understand how this stuff work. I tried googleing.
I just want to display the click count in the h2 in my view using sessions for the purpose.
Any tips will be appreciated.
If it is for simply increasing the clicked count on form submit, You can update your http post action method to read the session value if exist and increase and set it back. If not exist, initialize it.
const string sessionVariableName = "num";
public ActionResult ClickCounter()
{
if (Session[sessionVariableName] == null)
{
Session[sessionVariableName] = 0;
}
return View();
}
[HttpPost]
public ActionResult ClickCounter(string dummyParam)
{
if (Session[sessionVariableName] == null) // should not happen!
{
Session[sessionVariableName] = 0;
}
else
{
var n = (int)Session[sessionVariableName];
n++;
Session[sessionVariableName] = n;
}
return View();
}
Make sure that you are doing a GET form method on submit.
You also need to remove the (re) initialization in the view this line #Session["num"] = 0; as we are doing that in the action method. Also you should not have nested forms as it is invalid. Html.BeginForm helper will render the markup for the form tag. So remove the inner form tag you have.
You have tagged this question as asp.net-mvc, why not take advantage of the framework?
Model
class MyModel
{
public int ClickCount { get; set; }
}
View
#model MyModel
#{
ViewBag.Title = "ClickCounter";
}
<h2>#ViewBag.Title</h2>
<form method="post">
<!-- hidden input of the current click count -->
#Html.HiddenFor(m => m.ClickCount)
<fieldset>
<legend>Button clicks counter</legend>
<div>
#Html.LabelFor(m => m.ClickCount)
<h2>#Model.ClickCount</h2>
</div>
<div>
<button type="submit">Submit!</button>
</div>
</fieldset>
</form>
Controller
const string clickCountSessionKey = "clickCount";
[HttpGet]
public ActionResult ClickCounter()
{
// initialize the model
var model = new MyModel() { ClickCount = 0 };
var previousClickCount = Session[clickCountSessionKey];
if (previousClickCount != null)
{
model.ClickCount = (int)previousClickCount;
}
return View(model);
}
[HttpPost]
public ActionResult ClickCounter(MyModel model)
{
// increment the click count of the model
model.ClickCount++;
// track the click count in the session
Session[clickCountSessionKey] = model.ClickCount;
return View(model);
}

MVC 4 partial view causes page to become unresponsive on submit

Situation: In my C#/MVC 4 solution I am employing a view with a partial view within. The view is a form with a submit button. The partial view is with a div that is hidden, but can be displayed if the checkbox is selected.
Issue: If the partial view is hidden, the submit works normally. If the partial view is not hidden the submit causes the page to become unresponsive, if one waits the 3 plus minutes or so the submit eventually works as expected.
The code is below. Thank you in advance for your consideration. I am a novice developer, therefore all comments, suggestions and critiques are welcome.
Code:
Model
namespace MyModels
{
public class MainModel
{
public SelectListItem Things { get; set;}
public IEnumerable<OtherModel> MoreThings { get; set;}
}
}
View
//named MyView
#model MyModels.MainModel
#using MyModels
#if (Model != null){
using (Html.BeginForm("MyViewName", "MyControllerName", FormMethod.Post, new { id = "view-form" }))
{
#Html.LabelFor(model => model.things)
#Html.DropDownList("", (Selectist)ViewBag.things)
#Html.ValidationMessageFor(model => model.field1)
#Html.CheckBoxWithLabel("aNameAttribute", Model.valueAttribute.ToString(), "anIdAttribute", Model.valueAtttribue ==1, "aLabel", "a_Toggle_Class")
<div class="treeview" style="display: none;">
<fieldset>
<legend>Title</legend>
//view causing issues replaces the div below
<div id="replacedDiv"></div>
</fieldset>
</div>
<p>
<input type="submit" value="Submit" />
</p>
}
}
<script type="text/javascript">
$(document).ready(function () {
$.ajax({
url: "/MyController/MyPartialView",
contentType: "application/html; charset=utf-8",
cache: "false",
type: "GET",
datatype: "html"
})
.success(function (result) {
$('#replacedDiv").html(result);
})
});
</script>
Partial View
//named _MyPartialView
#model MyModels.MainModel
#using MyModels
#foreach (var moreThings in ViewBag.moreThings)
{
<div id="replacedDiv">
<label>
<input type="checkbox" id=#moreThings.id value=#moreThings.name />#moreThings.name </label>
</div>
}
Controller
namespace Main.Controllers
{
public class MyController
{
[HttpGet]
public ActionResult Index(MainModel model)
{
return View(model);
}
public ActionResult MyView()
{
var model = new MainModel();
return View(model);
}
public ActionResult MyPartialView(MainModel model)
{
<OtherModel> moreThings = BLotherModel.GetMoreThings();
ViewBag.moreThings = moreThings;
return PartialView("_MyPartialView", promotion);
}
[HttpPost]
public ActionResult MyView(FormCollection collection)
{
MainModel model = new MainModel();
return SaveModel(model);
}
}
}
In your ajax you are using:
$('#replacedDiv").html(result);
But your partial view contains <div id="replacedDiv"> that are generated in a loop
replace your partial view code with :
#foreach (var moreThings in ViewBag.moreThings)
{
<label>#moreThings.name </label>
<input type="checkbox" id=#moreThings.id value=#moreThings.name />
}
and it should be OK

C# pre-populating textarea from database

I have a small website with a Homepage which displays x2 paragraphs of text. These paragraphs are populated from a single record in a database containing 3 fields ("ID", "para1" and "para2"). This record is updated via a form which contains x2 textareas (assigned to update "para1" and "para2"), allowing me to type into the textarea, click "update" and then the two paragraphs on the Homepage will update to reflect the new text.
To navigate to the form page that contains the textareas from the homepage I click an "admin" link which takes me to a logon page, I enter username and password and click "login" and this forwards me on to the "update" page.
What I'd like, is the x2 textarea inputs to be pre-populated with the data stored in the "para1" and "para2" fields in the database table. This way, if someone wanted to make a minor edit to either of the paragraphs on the homepage then they won't need to re-type the whole thing from scratch.
I'm using C# Razor in Microsoft Visual Web Developer Express. I am a total beginner at not just this, but any form of development work, so I'm learning as I'm going, please be gentle :-)
Code examples below:
(the View page):
#model DFAccountancy.Models.Data
#{
ViewBag.Title = "Update";
}
<h2>Update</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"> </script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Data</legend>
<div class="editor-label">
#Html.LabelFor(model => model.para1)
</div>
<div class="editor-field">
#Html.TextAreaFor(model => model.para1, new { cols = 75, #rows = 5 })
#*#Html.EditorFor(model => model.para1)*#
#Html.ValidationMessageFor(model => model.para1)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.para2)
</div>
<div class="editor-field">
#Html.TextAreaFor(model => model.para2, new { cols = 75, #rows = 5 })
#*#Html.EditorFor(model => model.para2)*#
#Html.ValidationMessageFor(model => model.para2)
</div>
<p>
<input type="submit" value="Update" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
(the Model):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations;
namespace DFAccountancy.Models
{
public class Data
{
[DataType(DataType.MultilineText)]
public int ID { get; set; }
public string para1 { get; set; }
public string para2 { get; set; }
}
public class DataDBContext : DbContext
{
public DbSet<Data> Data { get; set; }
}
}
(the Controller):
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using DFAccountancy.Models;
namespace DFAccountancy.Controllers
{
public class DataController : Controller
{
private DataDBContext db = new DataDBContext();
//
// GET: /Data/
public ViewResult Index()
{
return View(db.Data.ToList());
}
//
// GET: /Data/Details/5
public ViewResult Details(string id)
{
Data data = db.Data.Find(id);
return View(data);
}
//
// GET: /Data/Update/5
public ActionResult Update()
{
return View();
}
//
// POST: /Data/Update/5
[HttpPost]
public ActionResult Update(Data data)
{
if (ModelState.IsValid)
{
data.ID = 1; //EF need to know which row to update in the database.
db.Entry(data).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index", "Home");
}
return View(data);
}
}
}
You just need to update your Update method to include passing in an Id
//
// GET: /Data/Update/5
public ActionResult Update()
{
Data data = db.Data.Find("1");
return View(data);
}
In addition the link to the update method will need to be updated to pass that Id in as well. Assuming you're using the standard list template for MVC.
<td>
#Html.ActionLink("Update", "Update") |
...
</td>

File Upload Causes Model Validation to Fail

I have an MVC3 form bound to a model with a file upload control. (Extra HTML removed for brevity):
#model Models.MessageModel
<script type="text/javascript">
var numAttachments = 0;
$(function () {
$(".add-attachment").click(function () {
$(".attachments").append("<div><input type=\"file\" name=\"attachments\" id=\"attachment" + numAttachments + "\" /></div>");
});
});
</script>
#using (Html.BeginForm())
{
#Html.ValidationSummary()
<div class="field-label">Subject:
#Html.EditorFor(model => model.Subject)
</div>
<div class="attachments">
</div>
<div>
Add Attachment
</div>
<div class="message-text">#Html.TextAreaFor(model => model.Text, new { cols = 107, rows = 10 })</div>
<input type="submit" value="Send Message" />
</div>
}
Users can choose to add multiple attachments by clicking the "add attachment" link, attachments are not required.
My model is as follows:
public class MessageModel
{
[Required]
public string Subject { get; set; }
[Required]
public string Text { get; set; }
public IEnumerable<HttpPostedFileBase> Attachments { get; set; }
}
(NOTE: I've also tried moving the attachments out of the model, into an argument to my action method with the same results)
My Action:
[HttpPost]
public ActionResult New(MessageModel message)
{
// this check passes if no file is uploaded
// but once a file is uploaded, this evaluates to false
// even if the model is valid
if (ModelState.IsValid)
{
// do stuff
}
}
This form works fine and validation passes when no file is selected for upload. When I choose a file for upload, ModelState.IsValid becomes false. How can I cause validation to ignore uploaded files?
You need to make sure your form is using the correct "enctype".
#using (Html.BeginForm("New", "Controller", FormMethod.Post, new { enctype = "multipart/form-data" }))
MVC 3 file upload and model binding

Categories

Resources