In ASP, we can set the property for Textbox as follows:
<asp:TextBox id="txtbox1" enable=False></asp:TextBox>
and in C# code we write:
txtbox1.enable=True;
How can we do the same work in MVC3?
You can use a method a bit more sophisticated like this.
In the MVC3 controller you can define the boolean to control the TextBox
public ActionResult Index()
{
ViewBag.isTextBoxEnabled = true;
return View();
}
In the .cshtml you can verify the state of the boolean and add or not the disabled attribute to the newly created <input type="text" />
#{
var htmlOptions = new Dictionary<string, object>();
if (!ViewBag.isTextBoxEnabled)
{
htmlOptions.Add("disabled", "disabled");
}
}
#Html.TextBox("txtbox1", "", htmlOptions)
Or you can also set it strongly typed in your model (in case you avoid the ViewBag)
#Html.TextBox("SomeText","", new { disabled = "disabled" } )
Related
I'm using the Ajax.BeginForm helper in ASP.NET MVC3 to submit a form that replaces itself with new values in the form set on the server. However when I use helpers like Html.TextBoxFor I get the values that was submitted, not the values I inserted into the model on the server.
For example; I set SomeValue to 4 in my action and show it in a textbox. I change the value to 8, hit submit and would expect the value to be changed back to 4 in the textbox, but for some reason it stays 8. But if I output SomeValue without using Html helpers it says 4. Anybody have some clue about what is going on?
My controller:
public ActionResult Index(HomeModel model)
{
model.SomeValue = 4;
if (Request.IsAjaxRequest())
return PartialView(model);
return View(model);
}
public class HomeModel
{
public int? SomeValue { get; set; }
}
My View (please not that I have all the needed javascript in my layout page):
<div id="ajaxtest">
#using(Ajax.BeginForm(new AjaxOptions{ InsertionMode = InsertionMode.Replace,
UpdateTargetId = "ajaxtest", HttpMethod = "Post" })) {
#Html.TextBoxFor(model => model.SomeValue)
<input type="submit" value="Update" />
}
</div>
you can use
ModelState.Clear()
in your controller method to make the html helpers use your changed model. Otherwise they use the values from the form submit
Have a look at: Asp.net MVC ModelState.Clear
in your POST method you need to do
ModelState.Clear();
to reflect the changes made after the post
#using (Ajax.BeginForm("SaveItemAndProperties", "HomeBuilder",
new AjaxOptions
{
UpdateTargetId = "divSaveItemAndProps",
InsertionMode = InsertionMode.Replace
}))
{
#Html.EditorForModel()
<input type="submit" value="Submit" />
}
In Model which is called from EditorForModel
#Html.EditorFor(m => m.PropertyValues)
PropertyValues is a list of properties and is a calling a EditorTemplate.
From the Action I change the value and then try to update the data back to the View
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public PartialViewResult SaveItemAndProperties(PropertyBuilderViewModel modelValues)
{
//Change on property in modelValues
return PartialView("PropertyBuilderControl", modelmodelValues);
}
When i am debugging i see the data propertly but it does not display in the view.
Any idea why it is doing so.
What are you changing in your action? HTML helpers such as TextBoxFor, HiddenFor, DropDownListFor, CheckBoxFor, ... first look at ModelState when binding and after that in the model. So if in your controller action you intend to do something like this:
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public PartialViewResult SaveItemAndProperties(PropertyBuilderViewModel modelValues)
{
modelValues.Foo = "some new value";
return PartialView("PropertyBuilderControl", modelmodelValues);
}
make sure you remove that value from the model state or you won't see any updates once you render the view again:
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public PartialViewResult SaveItemAndProperties(PropertyBuilderViewModel modelValues)
{
ModelState.Remove("Foo");
modelValues.Foo = "some new value";
return PartialView("PropertyBuilderControl", modelmodelValues);
}
I'm attempting to add a classic Accept Terms and Conditions checkbox on the log on page of an MVC application.
If the user accepts the Terms and Conditions, but fails to log on for some other reason (bad password etc), then I want the Accept T&Cs checkbox not to be checked, so the user is forced to accept the T&Cs on every log on attempt.
The problem is that using Html.CheckboxFor(), after a postback the checkbox retains its previous value, despite the value of the bound Model property.
Here's the code, stripped down to essentials. If you run this code up, check the checkbox, and click the button, you'll be returned to the form with the checkbox still checked, even though the bound model property is false.
The Model:
namespace Namespace.Web.ViewModels.Account
{
public class LogOnInputViewModel
{
[IsTrue("You must agree to the Terms and Conditions.")]
public bool AcceptTermsAndConditions { get; set; }
}
}
The validation attribute:
public class IsTrueAttribute : ValidationAttribute
{
public IsTrueAttribute(string errorMessage) : base(errorMessage)
{
}
public override bool IsValid(object value)
{
if (value == null) return false;
if (value.GetType() != typeof(bool)) throw new InvalidOperationException("can only be used on boolean properties.");
return (bool)value;
}
}
The View:
#model Namespace.Web.ViewModels.Account.LogOnInputViewModel
#using (Html.BeginForm()) {
#Html.CheckBoxFor(x => x.AcceptTermsAndConditions)
<input type="submit" value="Log On" />
}
The Controller:
[HttpGet]
public ActionResult LogOn(string returnUrl)
{
return View(new LogOnInputViewModel { AcceptTermsAndConditions = false });
}
[HttpPost]
public ActionResult LogOn(LogOnInputViewModel input)
{
return View(new LogOnInputViewModel { AcceptTermsAndConditions = false });
}
I saw the suggestion on asp.net to add a #checked attribute to the CheckboxFor. I tried this, making the view
#model Namespace.Web.ViewModels.Account.LogOnInputViewModel
#using (Html.BeginForm()) {
#Html.CheckBoxFor(x => x.AcceptTermsAndConditions, new { #checked = Model.AcceptTermsAndConditions })
<input type="submit" value="Log On" />
}
And I saw the same behaviour.
Thanks for any help/insights!
Edit: Although I want to override the posted back value, I wish to retain the message if validation of AcceptTermsAndConditions fails (there is a validation attribute on AcceptTermsAndConditions requiring it to be true), so I can't use ModelState.Remove("AcceptTermsAndConditions") which was the otherwise sound answer #counsellorben gave me. I've edited the code above to include the validation attribute - apologies to #counsellorben for not being clearer originally.
You need to clear the ModelState for AcceptTermsAndConditions. By design, CheckBoxFor and other data-bound helpers are bound first against the ModelState, and then against the model if there is no ModelState for the element. Add the following to your POST action:
ModelState.Remove("AcceptTermsAndConditions");
I have discovered what appears to be a bug using MVC 3 with the RemoteAttibute and the ActionNameSelectorAttribute.
I have implemented a solution to support multiple submit buttons on the same view similar to this post: http://blog.ashmind.com/2010/03/15/multiple-submit-buttons-with-asp-net-mvc-final-solution/
The solution works however, when I introduce the RemoteAttribute in my model, the controllerContext.RequestContext.HttpContext.Request no longer contains any of my submit buttons which causes the the "multi-submit-button" solution to fail.
Has anyone else experienced this scenario?
I know this is not a direct answer to your question, but I would propose an alternative solution to the multiple submit-buttons using clientside JQuery and markup instead:
Javascript
<script type="text/javascript">
$(document).ready(function () {
$("input[type=submit][data-action]").click(function (e) {
var $this = $(this);
var form = $this.parents("form");
var action = $this.attr('data-action');
var controller = $this.attr('data-controller');
form.attr('action', "/" + controller + "/" + action);
form.submit();
e.preventDefault();
});
});
</script>
Html
#using (Html.BeginForm())
{
<input type="text" name="name" id="name" />
<input type="submit" value="Save draft" data-action="SaveDraft" data-controller="Home" />
<input type="submit" value="Publish" data-action="Publish" data-controller="Home" />
}
It might not be as elegant as a code-solution, but it offers somewhat less hassle in that the only thing that actually changes is the action-attribute of the form when a submitbutton is clicked.
Basically what it does is that whenever a submit-button with the attribute data-action set is clicked, it replaces its parent forms action-attribute with a combination of the attributes data-controller and data-action on the clicked button, and then fires the submit-event of the form.
Of course, this particular example is poorly generic and it will always create /Controller/Action url, but this could easily be extended with some more logic in the click-action.
Just a tip :)
i'm not sure that its a bug in mvc 3 as it's not something that you were expecting. the RemoteAttribute causes javascript to intercept and validate the form with an ajax post. to do that, the form post is probably canceled, and when the validation is complete, the form's submit event is probably called directly, rather than using the actual button clicked. i can see where that would be problematic in your scenario, but it makes sense. my suggestion, either don't use the RemoteAttributeand validate things yourself, or don't have multiple form actions.
The problem manifests itself when the RemoteAttribute is used on a model in a view where mutliple submit buttons are used. Regardless of what "multi-button" solution you use, the POST no longer contains any submit inputs.
I managed to solve the problem with a few tweeks to the ActionMethodSelectorAttribute and the addition of a hidden view field and some javascript to help wire up the pieces.
ViewModel
public class NomineeViewModel
{
[Remote("UserAlreadyRegistered", "Nominee", AdditionalFields="Version", ErrorMessage="This Username is already registered with the agency.")]
public string UserName { get; set; }
public int Version {get; set;}
public string SubmitButtonName{ get; set; }
}
ActionMethodSelectorAttribute
public class OnlyIfPostedFromButtonAttribute : ActionMethodSelectorAttribute
{
public String SubmitButton { get; set; }
public String ViewModelSubmitButton { get; set; }
public override Boolean IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
{
var buttonName = controllerContext.HttpContext.Request[SubmitButton];
if (buttonName == null)
{
//This is neccessary to support the RemoteAttribute that appears to intercepted the form post
//and removes the submit button from the Request (normally detected in the code above)
var viewModelSubmitButton = controllerContext.HttpContext.Request[ViewModelSubmitButton];
if ((viewModelSubmitButton == null) || (viewModelSubmitButton != SubmitButton))
return false;
}
// Modify the requested action to the name of the method the attribute is attached to
controllerContext.RouteData.Values["action"] = methodInfo.Name;
return true;
}
}
View
<script type="text/javascript" language="javascript">
$(function () {
$("input[type=submit][data-action]").click(function (e) {
var action = $(this).attr('data-action');
$("#SubmitButtonName").val(action);
});
});
</script>
<% using (Html.BeginForm())
{%>
<p>
<%= Html.LabelFor(m => m.UserName)%>
<%= Html.DisplayFor(m => m.UserName)%>
</p>
<input type="submit" name="editNominee" value="Edit" data-action="editNominee" />
<input type="submit" name="sendActivationEmail" value="SendActivationEmail" data-action="sendActivationEmail" />
<%=Html.HiddenFor(m=>m.SubmitButtonName) %>
<% } %>
Controller
[AcceptVerbs(HttpVerbs.Post)]
[ActionName("Details")]
[OnlyIfPostedFromButton(SubmitButton = "editNominee", ViewModelSubmitButton = "SubmitButtonName")]
public ActionResult DetailsEditNominee(NomineeViewModel nom)
{
return RedirectToAction("Edit", "Nominee", new { id = nom.UserName });
}
[AcceptVerbs(HttpVerbs.Post)]
[ActionName("Details")]
[OnlyIfPostedFromButton(SubmitButton = "sendActivationEmail", ViewModelSubmitButton = "SubmitButtonName")]
public ActionResult DetailsSendActivationEmail(NomineeViewModel nom)
{
return RedirectToAction("SendActivationEmail", "Nominee", new { id = nom.UserName });
}
[OutputCache(Location = OutputCacheLocation.None, NoStore = true)]
public ActionResult UserAlreadyRegistered(string UserName, int Version)
{
//Only validate this property for new records (i.e. Version != zero)
return Version != 0 ? Json(true, JsonRequestBehavior.AllowGet)
: Json(! nomineeService.UserNameAlreadyRegistered(CurrentLogonDetails.TaxAgentId, UserName), JsonRequestBehavior.AllowGet);
}
I encountered the same issue.
I also attached an on submit event to prepare the form before submit. Interestingly, when I insert a break point in the on submit function, and then continue, the problem has disappeared.
I ended up with an Ajax form by removing the Remote attribute and validate the field using the ModelState.
I want to get some options (say payment method cash, credit card etc.) and bind these to radio buttons. I believe there is no RadioButtonList in MVC 3.
Also, once radios are bound I want to show the previously selected option to the user while editing the answer.
As always you start with a model:
public enum PaiementMethod
{
Cash,
CreditCard,
}
public class MyViewModel
{
public PaiementMethod PaiementMethod { get; set; }
}
then a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel();
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
and finally a view:
#model MyViewModel
#using (Html.BeginForm())
{
<label for="paiement_cash">Cash</label>
#Html.RadioButtonFor(x => x.PaiementMethod, "Cash", new { id = "paiement_cash" })
<label for="paiement_cc">Credit card</label>
#Html.RadioButtonFor(x => x.PaiementMethod, "CreditCard", new { id = "paiement_cc" })
<input type="submit" value="OK" />
}
And if you want some more generic solution which encapsulates this in a helper you may find the following answer helpful.
This is how I like to bind RadioButtonLists. The view model has a collection of my strongly typed objects. For example, maybe PaymentOptions is a code table. Along with the collection is a SelectedPaymentOptionKey (or Selected*Id if you prefix your primary keys with Id). Initially this key will just be default 0, but on postback, it will hold the value of the selected item.
public class PaymentSelectionVM
{
public ICollection<PaymentOption> PaymentOptions { get; set; }
public int SelectedPaymentOptionKey { get; set; }
}
public ViewResult PaymentSelection()
{
var paymentOptions = db.PaymentOptions.ToList();
return View(
new PaymentSelectionVM {
PaymentOptions = paymentOptions,
//This is not required, but shows how to default the selected radiobutton
//Perhaps you have a relationship between a Customer and PaymentOption already,
//SelectedPaymentOptionKey = someCustomer.LastPaymentOptionUsed.PaymentOptionKey
// or maybe just grab the first one(note this would NullReferenceException on empty collection)
//SelectedPaymentOptionKey = paymentOptions.FirstOrDefault().PaymentOptionKey
});
}
Then in the View:
#foreach (var opt in Model.PaymentOptions)
{
#*Any other HTML here that you want for displaying labels or styling*#
#Html.RadioButtonFor(m => m.SelectedPaymentOptionKey, opt.PaymentOptionKey)
}
The m.SelectedPaymentOptionKey serves two purposes. First, it groups the Radio buttons together so that the selection is mutually exclusive(I would encourage you to use something like FireBug to inspect the generated html just for your own understanding. The wonderful thing about MVC is the generated HTML is fairly basic and standard so it shouldn't be hard for you to eventually be able to predict the behavior of your views. There is very little magic going on here.). Second, it will hold the value of the selected item on postback.
And finally in the post handler we have the SelectedPaymentOptionKey available:
[HttpPost]
public ActionResult PaymentSelection(PaymentSelectionVM vm)
{
currentOrder.PaymentOption = db.PaymentOptions.Find(vm.SelectedPaymentOptionKey);
....
}
The advantage of this over using SelectListItems is you have access to more of the object's properties in the case that you are displaying a grid/table and need to display many values of the object. I also like that there are no hard coded strings being passed in the Html helpers as some other approaches have.
The disadvantage is you get radio buttons which all have the same ID, which is not really a good practice. This is easily fixed by changing to this:
#Html.RadioButtonFor(m => m.SelectedPaymentOptionKey, opt.PaymentOptionKey, new { id = "PaymentOptions_" + opt.PaymentOptionKey})
Lastly, validation is a bit quirky with most all of the radio button techniques I've seen. If I really needed it, I would wire some jquery up to populate a hidden SelectedPaymentOptionsKey whenever the radio buttons are clicked, and place the [Required] or other validation on the hidden field.
Another workaround for the validation problem
ASP.NET MVC 3 unobtrusive validation and radio buttons
This looks promising but I haven't had a chance to test it:
http://memoriesdotnet.blogspot.com/2011/11/mvc-3-radiobuttonlist-including.html
You should bind your options to SelectList in ViewModel and set Selected attribute to true for previously selected option