Switch partial based on radio button in ASP.NET Core MVC - asp.net-core-mvc

I don't know how to perform this in ASP.NET Core MVC.
I've got two radio buttons of the same group, I wish to switch a partial view based on the current radio button selection. I think I'll have to use js somehow but I don't know how.
Currently I wrote
Html.Kendo().RadioButton().Name("manual").Checked(true).HtmlAttributes(new { #name = "group1" }).Label("Manuale");
Html.Kendo().RadioButton().Name("guidata").Checked(true).HtmlAttributes(new { #name = "group1" }).Label("Guidata");
<div id="dyn_container"/>
But I don't know how to load data and eventually pass a model, any suggestions?
Thanks

The essence of RadioButton is actually an Input, so you can use the method of monitoring Input changes to determine how to continue to perform the following operations.
Below is my test code, you can refer to it:
View:
<table>
<tr>
<td class="Form_Label"><label for="txType">Select List:</label> </td>
<td>
#Html.Kendo().RadioButton().Name("manual").Checked(true).HtmlAttributes(new { #name = "group1",#value = "Manuale" }).Label("Manuale")
#Html.Kendo().RadioButton().Name("guidata").Checked(true).HtmlAttributes(new { #name = "group1",#value = "Guidata" }).Label("Guidata")
</td>
</tr>
</table>
<div id="dyn_container"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<script >
$('input:radio[name=group1]').change(function() {
var url = '/Home/Manuale';
if ($(this).val() === 'Guidata') {
url = '/Home/Guidata';
}
$("#dyn_container").load(url);
});
</script>
Then, you can execute the corresponding action in the controller by:
[HttpGet]
public ActionResult Manuale()
{
return PartialView("_Manuale");
}
[HttpGet]
public ActionResult Guidata()
{
return PartialView("_Guidata");
}
Test Result:
Update:
This is caused by inconsistency between your constructor and instantiation, you need to inject ILogger<_Page1Model> into the page.
Add this line of code in View:
#inject ILogger<_Page1Model> logger1
Then modify Html.PartialAsync:
#await Html.PartialAsync("_Page1", new _Page1Model(logger1));

Excuse me, I have a little bit modified your example (I don't have the original code under my hands so I wrote a new one).
I don't have a controller, using the RazorPages, so currently I've done this way
#page
#using Microsoft.AspNetCore.Mvc.Localization
#using Microsoft.AspNetCore.Mvc.RazorPages
#using Test.Localization
#using Test.Web.Pages
#using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Table
#using Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers
#using Volo.Abp.Users
#model Test.Web.Pages.IndexModel
#inject IHtmlLocalizer<TestResource> L
#inject ICurrentUser CurrentUser
#section styles {
<abp-style src="/Pages/Index.css" />
}
#section scripts {
<abp-script src="/Pages/Index.js" />
}
<div class="container">
<table>
<tr>
<td class="Form_Label"><label for="txType">Select List:</label> </td>
<td>
#Html.RadioButton("group1","HotelGrandPlaza", false)
#Html.RadioButton("group1","Test", false)
</td>
</tr>
</table>
<div id="project" style="display:none">
#await Html.PartialAsync("_Page1", new _Page1Model());
</div>
<div id="engineering" style="display:none">
#{await Html.RenderPartialAsync("_Page2", new _Page2Model());}
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<script >
$('input:radio[name=group1]').change(function() {
if ($(this).val() === 'Test') {
$("#project").hide();
$("#engineering").show();
} else {
{
$("#project").show();
$("#engineering").hide();
}
}
});
</script>
</div>
In this case when I switch it works... but problems arrive when I have a constructor with Dependency Injection....
Just with this simple case
public class _Page1Model : PageModel
{
private readonly ILogger<_Page1Model> _logger;
public _Page1Model(ILogger<_Page1Model> logger)
{
_logger = logger;
}
public void OnGet()
{
}
}
}
I can't use the cshtml I've defined before... any suggestions?
Thanks

Related

How do I bind table values to a Map and post with Spring MVC and Thymeleaf?

I have an entry form where users select a subset of items which populate a table. I need to bind each row's first and and third column value to a key and value, respectively, enter them into a Map<Integer, Integer> passed in through the controller, and post the data. I've poured through many different solutions online and have yet to find one that works. The map always returns empty.
Wrapper class for Map
#Getter #Setter
public class ItemForm {
private Map<Integer, Integer> items = new HashMap<>();
}
Controllers
#GetMapping(...)
public String showItemPage(Model model) {
...
model.addAttribute("itemForm", new ItemForm());
...
}
#PostMapping(...)
public String processItemUpdate(#ModelAttribute("itemForm") ItemForm itemForm, BindingResult bindingResult) {
...
}
Template
<tr th:each="item : *{items}">
<td>
<input type="text" th:value="${item.key}">
</td>
<td></td>
<td>
<input type="text" th:field="*{items[__${item.key}__]}">
</td>
</tr>
I understand that I will need something like th:field="*{items[__${item.key}__]}" to access the map, but as to extracting and combining the key-value pair I'm a bit lost.
edit:
Is something along these lines possible?
#Getter #Setter
public class ItemFormPair {
private int ID, quantity;
}
#Getter #Setter
public class ItemForm {
private List<ItemFormPair> items = new ArrayList<>();
}
<tr th:each="item, stat : *{items}">
<td>
<input type="text" th:field="*{items[__${stat.index}__].ID}">
</td>
<td></td>
<td>
<input type="text" th:field="*{items[__${stat.index}__].quantity}">
</td>
</tr>
edit:
I don't really want to spend any more time on this problem and there doesn't appear to be an elegant solution available so I'm simply going to use an Ajax POST request.
You bound the single key/values of the map to the form but not the map itself. That won't work that way. I'm quite sure there is no way to get the map as whole piece back from the form. Maybe with a converter.
An alternative could be to assign name/id to the input fields and read all key/values back to map in the processItemUpdate method:
This solution works on my site. I redefined my answer more precisely:
input.html
<!DOCTYPE HTML>
<html lang="de" xmlns:th="http://www.thymeleaf.org">
<head />
<body>
<form th:action="#{/inputPost}" method="post" th:fragment="form">
<table>
<tr th:each="item,iter : ${itemForm.items.entrySet()}">
<td><input type="text" th:id="${iter.index + '.ID'}"
th:name="${iter.index + '.ID'}" th:value="${item.key}"></td>
<td><input type="text" th:id="${iter.index + '.VALUE'}"
th:name="${iter.index + '.VALUE'}" th:value="${item.value}"></td>
</tr>
</table>
<input type="submit" name="Send" value="Send" /> <input type="submit"
name="Add" value="Add new Line" />
</form>
</body>
</html>
success.html
<!DOCTYPE HTML>
<html lang="de" xmlns:th="http://www.thymeleaf.org">
<head></head>
<body>
<table border="1">
<tr th:each="item : ${itemForm.items.entrySet()}">
<td th:text="${item.key}"></td>
<td th:text="${item.value}"></td>
</tr>
</table>
</body>
</html>
Controller
#GetMapping("/inputForm")
public String dummy(Model model) {
ItemForm form = new ItemForm();
form.getItems().put(form.getItems().size(), 42);
model.addAttribute("itemForm", form);
return "input";
}
#PostMapping("/inputPost")
public String processItemUpdate(HttpServletRequest request, Model model) {
Map<String, String[]> params = request.getParameterMap();
ItemForm form = new ItemForm();
String operation = null;
for (Entry<String, String[]> entry : params.entrySet()) {
if (entry.getKey().endsWith(".ID")) { // only react on ID values. The values will be directly fetched from
// map
String[] tokens = StringUtils.split(entry.getKey(), ".");
Integer id = Integer.parseInt(tokens[0]);
Integer idValue = Integer.parseInt(entry.getValue()[0]);
String[] value = params.get(id + ".VALUE"); // fetch the value to defined ID
Integer valueValue = Integer.parseInt(value[0]);
form.getItems().put(idValue, valueValue);
} else if (entry.getKey().equalsIgnoreCase("Send")) { // determine operation
operation = "send";
} else if (entry.getKey().equalsIgnoreCase("Add")) { // determine operation
operation = "add";
}
}
model.addAttribute("itemForm", form);
if (operation.equals("add")) { // add a new line and resend form again
form.getItems().put(form.getItems().size(), 42);
return "input";
}
return "success";
}

How to send dropdown value from the html form to the controller?

index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Super Spy App</title>
</head>
<body>
<h1>Our Super Cool Spy App</h1>
<h2>Create a Mission</h2>
<form action="/addMission" method="post">
<p><input type="submit" value="Create a Mission"></p>
</form>
<form action="/viewMission" method="get">
<h2>View Missions for</h2>
<select id="agents" name="agents">
<option value="Johnny English">Johnny English</option>
<option value="Natasha Romanova">Natasha Romanova</option>
<option value="Austin Powers">Austin Powers</option>
</select>
<input type="submit" value="Go">
</form>
</body>
</html>
ViewMissions.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>View Missions</title>
</head>
<body>
<h1> Here are the missions for</h1>
<div th:if="${missionList.empty}">
<h2>No Current Missions</h2>
</div>
<div th:unless="${missionList.empty}">
<table border="1">
<tr>
<th>Title</th>
<th>Gadget 1</th>
<th>Gadget 2</th>
<th colspan="2">Operation</th>
</tr>
<tr th:each="mission : ${missionList}">
<td th:text="${mission.title}"></td>
<td th:text="${mission.gadget1}"></td>
<td th:text="${mission.gadget2}"></td>
<td>edit</td>
<td>delete</td>
</tr>
</table>
</div>
<p> Back to home </p>
</body>
</html>
Controller Class
#GetMapping("/")
public String Home() {
return "index";
}
#PostMapping("/addMission")
public String addMission(Model model) {
model.addAttribute("mission", new Mission());
return "create_mission";
}
#GetMapping("/createMission")
public String ViewMission1(Model model) {
List<Mission> mission1 = database.getMissions();
model.addAttribute("missionList", mission1);
return "view_missions";
}
#PostMapping("/createMission")
public String createMission(#ModelAttribute Mission mission) {
int returnValue = database.createMission(mission);
System.out.println(returnValue);
return "view_missions";
}
#GetMapping("/viewMission")
public String viewMission2(Model model) {
List<Mission> mission1 = database.getMissions();
model.addAttribute("missionList", mission1);
return "view_missions";
}
getMissions method
public List<Mission> getMissions() {
MapSqlParameterSource namedParameters = new MapSqlParameterSource();
String query = "SELECT * FROM missions";
BeanPropertyRowMapper<Mission> missionMapper = new BeanPropertyRowMapper<Mission>(Mission.class);
List<Mission> missions = jdbc.query(query, namedParameters, missionMapper);
return missions;
}
Mission.java (the getter setter are already set but I didn't paste them here to prevent hustle and bustle)
public class Mission {
private Long id;
private String agent;
private String title;
private String gadget1;
private String gadget2;
}
So, in the above examples, I want to send the value selected from the dropdown list to my controller.
Im my html, if I select any value from the dropdown and press 'Go' it shows me the whole database for all the 3 agents but not the particular one that I selected.
Any suggestions how to curb this error.
I have tried searching for a solution on internet but they were using JSP which I haven't studied yet.
You can get the value submitted from the view to the controller in many ways. As you have a single value is passed from View to Controller you can use
#RequestParam
Your viewMission may look like this
#GetMapping("/viewMission")
public String viewMission2(#RequestParam#RequestParam(name = "agents", required = true) String agents, Model model) {
List<Mission> mission1 = database.getMissions(String agents);
model.addAttribute("missionList", mission1);
return "view_missions";
}
You have to pass the selected value to your query to filter the list based on the selected agent and your query will be
public List<Mission> getMissions(String agents) {
MapSqlParameterSource namedParameters = new MapSqlParameterSource();
String query = "SELECT * FROM missions WHERE agent ='" + agent +"'";
BeanPropertyRowMapper<Mission> missionMapper = new BeanPropertyRowMapper<Mission>(Mission.class);
List<Mission> missions = jdbc.query(query, namedParameters, missionMapper);
return missions;
}
Which will filter the list.

MVC model values not getting passed to controller

I am trying out with very basic MVC project using MVC 3.0 and Razor. Referring the turorial at this link.
I have created a strongly typed view for editing the Contacts using my model class called "Contact".
namespace Practice.Models
{
public class Contact
{
public string firstName;
public string lastName;
public string mobileNumber;
}
}
The "Index" method displays all the contacts using a list type view. When I click on "Edit" link against a contact, it displays the contact details in textboxes using an Edit View.
However, when I submit the Edit form, I am not getting the model values in controller action method. It shows null for each property. What exactly I may be missing here in such simple example?
DirectoryController
[HttpPost]
public ViewResult Edit(Contact model)
{
contactRepository.Update(model);
return View("Details", model);
}
View
#model Practice.Models.Contact
#{
ViewBag.Title = "Edit";
}
<h2>Edit</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("Edit","Directory"))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Contact</legend>
#Html.TextBoxFor(m => m.firstName)
#Html.TextBoxFor(m => m.lastName)
#Html.TextBoxFor(m => m.mobileNumber)
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
You are missing { get; set; } on your model properties

Ajax BeginForm not calling action in controller

I have been trying to implement a system where a user can add/delete child entities of the current entity, however I have been having trouble using the Ajax.BeginForm helper method (this is my first real use of it). Basically when I click the submit button, it won't call the specified action in my controller.
I have searched for several hours now without success, most of the issues people have had before is due to certain libraries not being referenced, but I'm pretty sure that's not the issue here.
Anyway, here are the important parts of my code:
Partial View
#using MyProject.Domain.Entities;
#model MyProject.WebUI.ViewModels.UserCustomerViewModel
<div id = "meh222">
#using (
Ajax.BeginForm("AddCustomer", "User",
new AjaxOptions() { UpdateTargetId = "meh222", HttpMethod = "Post" })
)
{
<label>Customers</label>
#*#Ajax.ActionLink("Add Customer", "AddCustomer", "User", new { userID = Model.UserID, customerID = 1001 },
new AjaxOptions() { UpdateTargetId = "meh222", HttpMethod = "POST" }, new { #class = "standardbutton" })*#
<fieldset>
<legend>CustomersLegend</legend>
#Html.HiddenFor(m => m.UserID)
#Html.DropDownListFor(m => m.Customers, new SelectList(Model.AllCustomers, "CustomerID", "Name"))
<p>
<input type="submit" value="Add Customer" />
</p>
</fieldset>
}
<table class="tbl2" id="tbl2">
<thead>
<tr>
<th>CustomerID</th> <th>Name</th> <th>Actions</th>
</tr>
</thead>
<tbody>
#if (Model != null)
{
foreach (Customer item in Model.Customers)
{
<tr>
<td>
#Html.DisplayTextFor(i => item.CustomerID)
</td>
<td>
#Html.DisplayTextFor(i => item.Name)
</td>
<td>
#Ajax.ActionLink("Delete", "DeleteCustomer", "User", new { userID = Model.UserID, customerID = item.CustomerID },
new AjaxOptions() { UpdateTargetId = "meh222" }, new { #class = "standardbutton" })
</td>
</tr>
}
}
</tbody>
</table>
</div>
Main View
<div>
#{ Html.RenderPartial("CustomerPartial", Model.CustomerVM); }
</div>
Controller
[AcceptVerbs(HttpVerbs.Post)]
public PartialViewResult AddCustomer(UserCustomerViewModel vm)
{
return PartialView("CustomerPartial");
}
View Model
public class UserCustomerViewModel
{
public int UserID;
public IEnumerable<Customer> Customers { get; set; }
public IEnumerable<Customer> AllCustomers { get; set; }
}
Scripts in my _Layout
<script src="#Url.Content("~/Scripts/jquery-1.5.1.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery-ui-1.8.11.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery-ui-1.8.11.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/MicrosoftMvcAjax.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/MicrosoftAjax.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script>
Just a couple of notes:
If you look in my partial view, you can see an Ajax.ActionLink commented out. This was what I used before (and it worked), however I needed to hardcode the CustomerID, hence the change to Ajax.BeginForm so I can access the value of the DropDownList that is populated from the ViewModel's AllCustomers property.
The real controller action obviously has more processing, but that isn't the problem here as if I put a breakpoint in the action it isn't even hit.
I have added the list of scripts just so people know exactly what I am referencing in case that is the issue.
Does anyone have any ideas why my submit isn't accessing the AddCustomer action in my controller?
If I haven't decribed the issue properly or you need more information, please let me know and I will try and give more information.
Thanks very much.
----UPDATE----
Error data I got from Chrome's console
This is under the '500 Internal Server Error' in the console.
d.support.ajax.d.ajaxTransport.send jquery-1.5.1.min.js:19
d.extend.ajax jquery-1.5.1.min.js:19
e jquery.unobtrusive-ajax.min.js:5
(anonymous function) jquery.unobtrusive-ajax.min.js:5
E jquery-1.5.1.min.js:19
d.event.handle jquery-1.5.1.min.js:19
d.event.add.k.handle.m jquery-1.5.1.min.js:19

Mvc3 IEnumerable<QuestionModel> have a List<QuestionOptionModel> property. When I post, I get null list

I'm making a survey application. Survey has Questions and Questions have QuestionOption. haveThere is a example here.
I am trying to use this technique in a large form with a list(List) but when I post back, the Viewmodel.Order that should’ve contained list of items and activities return with the lists empty.
My QuestionModel.cs like this.
public int Id { get; set; }
public string QuestionText { get; set; }
public System.Nullable<bool> OptionType1 { get; set; }
public System.Nullable<bool> OptionType2 { get; set; }
public List<QuestionOptionModel> OptionList = new List<QuestionOptionModel>();
When I post back "IEnumerable questions" List OptionList comes null. How can I do this?
public ActionResult CallSurvey()
{
IEnumerable<QuestionModel> questionModelList = (IEnumerable<QuestionModel>)SessionHelper.GetSessionObject(SessionKeys.SurveyKey);
questionModelList = questionSrv.GetQuestionModel();
return View(questionModelList);
}
questionModelList include all my survey question and question options. When I post it, post back is coming with only null optionList.
[HttpPost]
public ActionResult CallSurvey(IEnumerable<QuestionModel> questions)
{ ..... }
CallSurvey.cshtml
<body>
#using ((Html.BeginForm()))
{
#ViewBag.Test
<section class="slides layout-regular template-kendo">
#foreach (var item in Model)
{<article>
#Html.Partial("QuestionEditor", item)
</article>
}
<div class="slide-area" id="prev-slide-area"></div>
<div class="slide-area" id="next-slide-area"></div>
</section>
}
</body>
QuestionEditor.cshtml
#model LSMM.Business.Model.Survey.QuestionModel
#using LSMM.Web.Helpers
<div>
#using (Html.BeginCollectionItem("Questions"))
{
<table id="table1">
<tr>
<td>
<div id="#Model.Id" class="hint">
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.QuestionText)
#Html.HiddenFor(m => m.OptionType1)
#Html.HiddenFor(m => m.OptionType2)
#for (int i = 0; i < Model.OptionList.Count; ++i)
{
#Html.LabelFor(m => m.OptionList[i].Id)
#Html.LabelFor(m => m.OptionList[i].QuestionId)
#Html.LabelFor(m => m.OptionList[i].Checked)
#Html.LabelFor(m => m.OptionList[i].Description)
#Html.LabelFor(m => m.OptionList[i])
}
<span id="sorular">#Model.Id. #Model.QuestionText</span>
<br />
<br />
</div>
</td>
</tr>
<tr>
<td>
<div class="hint2">
#Html.Partial("QuestionOptionEditor", Model)
</div>
</td>
<td>
<div id="#Model.Id-Img">
<h2 style="top: 200px; right: 0;">
<img src="../../Content/css/img/#Model.Id-Img.png"></h2>
</div>
</td>
</tr>
</table>
and QuestionOptionEditor.cshtml
#model LSMM.Business.Model.Survey.QuestionModel
#using LSMM.Web.Helpers
#foreach (var option in #Model.OptionList)
{
<p>
#if (#Model.OptionType1 == false)
{
#Html.Partial("QuestionOptionModel", option)
}
else
{
#Html.Partial("../Shared/DisplayTemplates/QuestionOptionModel", option)
}
</p>
}
Here QuestionOptionModel views like this;
#model LSMM.Business.Model.Survey.QuestionOptionModel
#(Html.RadioButtonFor(m => m.Id, true, new { Id = #Model.Id, Name = #Model.QuestionId })) #Html.Label("Evet")
<br />
<br />
#(Html.RadioButtonFor(m => m.Id, false ,new { Id=#Model.Id, Name = #Model.QuestionId})) #Html.Label("Hayır")
The name attribute on your radio buttons is not "correct" according to the naming rules used by the default ModelBinder. That's why you aren't seeing the values you expect, the ModelBinder couldn't find what it was looking for.
This is easy to fix and you should end up with less code.
Take advantage of the framework and let it do work for you:
MVC can loop IEnumerables for you. Let it. There's little reason to write foreach loops in views these days.
Get rid of those partials. Use MVC's Templates feature instead.
Then your view can be as simple as:
<body>
#using ((Html.BeginForm()))
{
#ViewBag.Test
<section class="slides layout-regular template-kendo">
<table id="table1">
#* Table header goes here *#
#Html.EditorFor(model => model.OptionList)
</table>
<div class="slide-area" id="prev-slide-area"></div>
<div class="slide-area" id="next-slide-area"></div>
</section>
}
</body>
When you use an EditorTemplate or a DisplayTemplate, MVC will automatically fix the name attribute on your form fields. It won't do that with a partial.
Another suggestion, also if you don't mind. Get rid of those nulla-bools on your model and write different QuestionOptionModel types. DisplayTemplate/EditorTemplates are wise to the type system and MVC will pick the template that matches the type it is presented.
So you could have another class that derives from QuestionOptionModel:
public class ExtendedQuestionOptionModel : QuestionOptionModel
{
//Stuff goes here.
}
and you could have a List<QuestionOptionModel> hanging off your model that contains:
QuestionOptionModel
ExtendedQuestionOptionModel
QuestionOptionModel
and when you do this:
#Html.EditorFor(model => model.QuestionOptions)
MVC will go through the option list and render the Editor Template for QuestionOptionModel, then the Editor Template for ExtendedQuestionOptionModel, then the Editor Template for QuestionOptionModel again.
The game you want to play is to try to minimize the C# code in the views, because the views cannot be unit tested. Code that isn't unit tested will eventually come back and get you.
Your question is abit ambiguous. Frankly i hope this is what you are loooking for : IEnumerable can be transformed to list using
List<foo>listFoo =iEnumerableEntityFoo.toList()

Resources