I've read a couple of questions and answers on this matter but none of them gets me any closer to solving this issue. My problem is I can't seem to retain the RadioButtonFor checked value to the post action method. It's alot of unrelated stuff so i'll try to boil it down to the related issue.
So I've got a ViewModel like this:
public class QEventHandlerVM
{
//Removed unrelated
public QueueEventHandlerModel QEventHandler { get; set; }
public string SelectedOption { get; set; }
}
The Get Method for the edit view. I'm setting the SelectedOption here so I can work with it on the frontend. The task is to show and hide div depending on the SelectedOption value. This part is working as far as I know, the value gets set and is used in the view via jQuery.
[HttpGet]
public ActionResult Edit(int id)
{
try
{
//Removed unrelated...
QueueEventHandlerModel model;
QEventHandlerVM vm = new QEventHandlerVM();
ApiCommunicator.Get("QueueEventHandlers/" + id, out model);
vm.QEventHandler = model;
if (...)
vm.SelectedOption = "toQueue";
if (...)
vm.SelectedOption = "redirection";
if (...)
vm.SelectedOption = "hangup";
return View(vm);
}
catch (HttpResponseUnsuccessfulException e){...}
}
The Edit view simplified:
#model Foo.WebSites.Web.Views.QueueEventHandlers.QEventHandlerVM
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
//Unrelated stuff...
#Html.HiddenFor(s => s.SelectedOption)
#Html.RadioButtonFor(s => s.SelectedOption, "toQueue", new { id = "rdToQueue", #class = "q-event" })
<label for="rdToQueue">Transfer call to queue</label>
#Html.RadioButtonFor(s => s.SelectedOption, "redirect", new { id = "rdRedirect", #class = "q-event" })
<label for="rdRedirect">Transfer call to destination</label>
#Html.RadioButtonFor(s => s.SelectedOption, "hangup", new { id = "rdHangup", #class = "q-event" })
<label for="rdHangup">Hangup</label>
<button type="submit" class="btn btn-primary btn-sm">Save</button>
}
The Post method
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(QEventHandlerVM model, int id)
{
if (!ModelState.IsValid) {... return View(model); }
if (model.SelectedOption == "hangup" || model.SelectedOption == "redirection")
{
model.QEventHandler.QueueId = null;
}
else if (model.SelectedOption == "toQueue")
{
model.QEventHandler.Phrase = string.Empty;
model.QEventHandler.Redirection = string.Empty;
}
else if (model.SelectedOption == "hangup")
{
model.QEventHandler.Redirection = string.Empty;
model.QEventHandler.QueueId = null;
}
try {// Update}
catch (HttpResponseUnsuccessfulException e){...}
}
Now, here the SelectedOption value is the same as it was from the Get method. There's probably something simple I've missed and there's most likely some better way to go about this.
Thanks in advance.
That's because you have a HiddenFor and associated edit fields for your SelectedOption property. Simply remove the HiddenFor and that should work:
#Html.HiddenFor(s => s.SelectedOption)
Related
I have an ASP.NET MVC project with entities based on EF6 (model first). So my entities are all auto-generated for me. I have an entity, Site and I just want the user to select a Site before proceeding. I have tried a couple of ways, all of them work, but they seem very messy and unnecessary.
I was curious about the cleanest way to create a DropdownList of Sites, then get the selected site when the form is submitted (by Id or whatever other mechanism is better).
Currently I have:
The index where the user is asked to select a site:
public ActionResult Index()
{
ViewBag.Sites = new SelectList(db.Sites.ToList(), "Id", "Name");
return View();
}
The view:
#using (Html.BeginForm("SetSite", "Home"))
{
#Html.Label("sites", "Site:");
#Html.DropDownList("Sites", null, new { #class = "selectpicker" });
<div style="width:100%;height:25px"></div>
<button class="btn btn-default" style="width:100%">Go</button>
}
And the SetSite action, where the form is submitted
[HttpPost]
public ActionResult SetSite()
{
if (Request.Form["Sites"] != null)
{
Session["Site"] = db.Sites.Find(Request.Form["Sites"]);
return RedirectToAction("Home");
}
else
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
}
A few problems arise from this method. First, I really wanted to take advantage of the #model functionality in razor and point it towards my Site class, but since it's auto-generated, I don't want to go poking around and adding a whole bunch of view properties. (beggars can't be choosers?)
Second, the Request.Form['Sites'] returns a string, and converting it to and int is ugly as hell.
As I mentioned, I'd like to use the #model functionality with Html.DropDownListFor. Is that possible when working with a list of Sites from the DB?
How can I get this done cleanly?
Solution 1:-
Controller:-
private List<Country> GetCountries()
{
var list = new Entity.Result<List<Entity.Country>>();
list = _context.Countries.Select(tc => new Entity.Country
{
Id = tc.Id,
Name = tc.Name,
IsdCode = tc.Code,
}).ToList();
return list.Data.Select(x => new Country
{
id = x.Id,
Name = x.Name,
}).ToList();
}
HttpGet Method:-
public ActionResult Add(int id)
{
try
{
}
catch (Exception ex)
{
}
finally
{
ViewBag.countryList = GetCountries();
}
return View()
}
View Method:-
#Html.DropDownListFor(x => x.countryId, new SelectList(ViewBag.countryList, "id", "Name"), KahandasDhanji.Common.Localization.Application.Resources.ddlCountry,
new { #id = "ddlCountry", #rows = 1 })
In Http Post Form Submitimg u handle that model value in HTTPPOST Method.
Solution 2:-
FormCollection class we can capture the form's values within the controller.
[HttpPost]
public ActionResult Add(FormCollection form)
{
string strDDLValue = form["Sites"].ToString();
return View(MV);
}
Hope Its Work !!!
You can use a ViewModel to avoid converting the string value from Request.Form. Below is how your ViewModel class should look like
public class MyViewModel
{
public MyViewModel()
{
this.DropdownItems = new List<SelectListItem>();
}
public int SelectedSiteId { get; set; }
public List<SelectListItem> DropdownItems { get; set; }
}
Change the get action method in your controller as below
public ActionResult Index()
{
List<Site> sites = db.Sites.ToList();
MyViewModel model = new MyViewModel();
foreach(var site in sites)
{
model.DropdownItems.Add(new SelectListItem() { Text = site.Name, Value = site.ID.ToString() });
}
return View(model);
}
Add #model MyViewModel at the first line in your view code and use Html.DropDownListFor method to generate the dropdownlist
#model MyViewModel
#using (Html.BeginForm("SetSite", "Home"))
{
#Html.Label("SelectedSiteId", "Site:");
#Html.DropDownListFor(m => m.SelectedSiteId, Model.DropdownItems, new { #class = "selectpicker" })
<div style="width:100%;height:25px"></div>
<button class="btn btn-default" style="width:100%">Go</button>
}
The post action method in your controller should look like below. model.SelectedSiteId will be the selected value of the dropdownlist and the type is int so you won't have to do any conversion such as Convert.ToInt32(Request.Form['Sites']).
[HttpPost]
public ActionResult SetSite(MyViewModel model)
{
Session["Site"] = model.SelectedSiteId;
return RedirectToAction("Home");
}
I am starting in asp.net Mvc and making test, i am doing a simple chat using PubNub api and i want finish it using only razor code only and one page only.
Model Chat.cs:
namespace SimpleChat.Models
{
public class Chat
{
public string NuevoMensaje { get; set; }
public string TextArea { get; set; }
}
}
View:
#model SimpleChat.Models.Chat
#using (Html.BeginForm("Index","Chat",FormMethod.Post))
{
#Html.LabelFor(model => model.NuevoMensaje, "Nuevo Mensaje")
#Html.TextBoxFor(model => model.NuevoMensaje)
<input type="submit" class="btn-default" value="Enviar" />
#Html.TextAreaFor(model => model.TextArea)
}
Controller:
static string variante = "";
public ActionResult Index()
{
pubnub.Subscribe<string>("Chat", subCallback, connecCallBack, errorCallback);
//Chat nuevochat = new Chat();
return View();
}
[HttpPost]
public ActionResult Index(Chat chat)
{
pubnub.Publish<string>("Chat", chat.NuevoMensaje, userCallback, puberror);
chat.NuevoMensaje = "";
chat.TextArea =variante;
return View("Index",chat);
}
private void subCallback(string obj)
{
string[] retorno = obj.Split(',','"');
variante += "Richard dice:" + retorno[0] + "\n";
}
When i press submit don't get the new data, why?
If you want to render the updated value of TextArea property of your view model, You should clear the model state dictionary.
Model state dictionary has the initial values of your form inputs. So when razor (re)render the same view, It gives priority to the content in model state dictionary than the view model object you passed to the view.
You can use the ModelState.Clear() method to clear the model state dictionary values before returning to the view.
chat.TextArea = variante;
ModelState.Clear();
return View("Index",chat);
Assuming your variante variable has the updated text.
I have a strongly typed view and I'm trying to pass the input from a textbox upon a button click to an action using BeginForm. My code keeps passing a null object to the action method in the controller. How do I pass the object to the controller via the form ?
#using (#Html.BeginForm("GetQueueInfoWorkorder","Home", FormMethod.Post, new { id = Model}))
{
#Html.TextBoxFor(x=> x.ID);
<input type="Submit" value ="Search" class="ui-button-icon-secondary"/>
}
Here is the actionmethod :
[HttpPost]
public ActionResult GetQueueInfoWorkorder(UserResponse id)
{
//Check queue complete
int woNumber = Convert.ToInt32(id);
tb_QueueCompleted completed = db.tb_QueueCompleted.SingleOrDefault(x => x.WorkOrderNumber == woNumber);
if (completed != null)
{
var u = new UserResponse { ID = completed.QueueId.ToString() };
GetLogInfoCompleted(u);
return View("GetLogInfo");
}
//check queue pending
return View();
}
I think you're fairly close, but make these changes & it should work as expected:
Model:
public class UserResponse
{
public int ID { get; set; }
}
View:
#model UserResponse
#using (Html.BeginForm("GetQueueInfoWorkorder", "Home"))
{
#Html.TextBoxFor(x => x.ID);
<input type="Submit" value ="Search" class="ui-button-icon-secondary"/>
}
Action method:
public ActionResult GetQueueInfoWorkorder(UserResponse model)
{
int woNumber = model.ID;
//...
}
if the #model of your view is UserResponse , then on submission of this page the model (UserResponse) will automatically get submitted to the controller. Where have you declared the #model for the view.
I've been working around on form submission and I ran into something I can't figure out. I know this issue has been posted many times but I can't seem to work things out.
MODEL:
public class IndexModel
{
public string techNo { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
}
CONTROLLER:
public ActionResult Index(IndexModel _oTechModel)
{
//some codes on pulling data from the database
return View("Index", _oTechModel);
}
[HttpPost]
public ActionResult ProcessTechnician(FormCollection form, string SubmitButton)
{
IndexModel _oTechModel = new IndexModel();
switch (SubmitButton)
{
case "save": //add new technician
{
}
break;
case "update": //update technician details
{
try
{
if (isValid(form["techNo"].ToUpper(), "technician") == false) { /*do nothing*/}
else
{
_GTechnician.TECHNICIAN_NO = form["techNo"].ToUpper();
_GTechnician.FIRSTNAME = form["lastName"].ToUpper(); //form is empty here
_GTechnician.LASTNAME = form["firstName"].ToUpper(); //form is empty here
_GTechnicianFacade.updateTechnician(_GTechnician, _GAppSetting.ConnectionString);
}
}
catch (Exception ex) { throw ex; }
}
break;
case "search": //search technician
{
try
{
if (isValid(form["techNo"].ToUpper(), "technician") == false) { /*do nothing*/}
else
{
//some codes here
_oTechModel.techNo = form["techNo"].ToUpper();
_oTechModel.firstName = fNameStr;
_oTechModel.lastName = lNameStr;
_oTechModel.setEnable = true;
}
}
catch (Exception ex) { throw ex; }
}
break;
case "delete":
{
}
break;
}
return RedirectToAction("Index", _oTechModel);
}
VIEW:
#model Maintenance_.Models.IndexModel
#using (Html.BeginForm("ProcessTechnician", "Home", FormMethod.Post))
{
<td>Technician No :</td>
#Html.TextBoxFor(m => m.techNo)
<button type="submit" name="SubmitButton" value="search"></button>
<td>First Name :</td>
#Html.TextBoxFor(m => m.firstName, new { #class = "form-control", style = "width:380px;" })
<td>Last Name :</td>
#Html.TextBoxFor(m => m.lastName, new { #class = "form-control", style = "width:380px;" })
<button name="SubmitButton" value="delete" type="submit">Delete</button>
<button name="SubmitButton" value="update" type="submit">Update</button>
<button name="SubmitButton" value="save" type="submit">Save</button>
}
When I hit the search button it submits the form and displays the firstname and lastname of the technician on the textbox but when I change the values of the textboxes right after I hit the update button it clears the textbox and the data has lost. What am I doing wrong?
Your first line in the post method is
IndexModel _oTechModel = new IndexModel();
You then redirect to the index page with this 'empty' model. (in the case Update statement you are not assigning any values to _otechModel)
you need to change couple of things.
First change parameter
FormCollection form
to
IndexModel _oTechModel
as you have passed IndexModel from the form so it will be automatically binded to the parameter _oTechModel.
Next remove the line.
IndexModel _oTechModel = new IndexModel();
Then change
RedirectToAction("Index", _oTechModel);
it should be. Look at the Documentation
RedirectToAction("Index", new {_oTechModel= _oTechModel});
I have the following model used for a quiz, I am trying to submit a form and pass the existing model back to the Action as it has already been initialized in the Index action.
public class QuizModel
{
private List<string> _Responses;
public List<string> Responses
{
get
{
if (_Responses == null)
{
_Responses = new List<string>() { "Response A", "Response B", "Response C", "Response D" };
}
return _Responses;
}
}
public int? SelectedIndex { get; set; }
public string Question { get; set; }
}
With the following View:
<div class="title">Question</div>
<span id="question">#Model.Question</span>
#if (!Model.UserHasAnswered)
{
using (Html.BeginForm("Submit", "Quiz", FormMethod.Post))
{
for (int i = 0; i < Model.Responses.Count; i++)
{
<div class="reponse">#Html.RadioButtonFor(m => m.SelectedIndex, i)#Model.Responses[i]</div>
}
<input type="submit" value="This is the value" />
}
}
else
{
<div id="explanation">#Model.Explanation</div>
}
And Controller...
//
// GET: /Quiz/
public ActionResult Index()
{
QuizModel model = new QuizModel()
{
Question = "This is the question",
Explanation = "This is the explanation",
UserHasAnswered = false
};
return PartialView(model);
}
//
// POST: /Quiz/Submit
[HttpPost]
public ActionResult Submit(QuizModel model)
{
if (ModelState.IsValid)
{
int? selected = model.SelectedIndex;
model.UserHasAnswered = true;
}
return View("Index", model);
}
When the model comes to the Submit action it only contains the SelectedIndex and not Question or Explanation properties. How can I tell my view to pass the original model it received back to the Submit action?
When you first display the index, your Question and Explanation are displayed correctly.
Then you submit the form, and Question and Explanation doesn't make it to the Controller Action.
That is because your FORM doesn't have input fields containing the Question and Explanation.
Add this to your form:
#Html.HiddenFor(x => x.Question)
#Html.HiddenFor(x => x.Explanation)
If the Explanation is editable by the user, instead of adding a Hidden for it do this:
#Html.TextAreaFor(x => x.Explanation)
Remember: All the info you need to send to your controller need to be in INPUTS inside of your FORM.
This way, your view will became:
<div class="title">Question</div>
<span id="question">#Model.Question</span>
#if (!Model.UserHasAnswered)
{
using (Html.BeginForm("Submit", "Quiz", FormMethod.Post))
{
#Html.HiddenFor(x => x.Question)
#Html.HiddenFor(x => x.Explanation)
for (int i = 0; i < Model.Responses.Count; i++)
{
<div class="reponse">#Html.RadioButtonFor(m => m.SelectedIndex, i)#Model.Responses[i]</div>
}
<input type="submit" value="This is the value" />
}
}
else
{
<div id="explanation">#Model.Explanation</div>
}
I believe your index action should be like below in your scenario :
public ActionResult Index(QuizModel model)
{
if(model == null)
{
model = new QuizModel()
{
Question = "This is the question",
Explanation = "This is the explanation",
UserHasAnswered = false
};
}
return PartialView(model);
}
Hope this will help !!