binding to list property of an object returns nothing mvc3 - asp.net-mvc-3

Context: ASP MVC 3/4, VB.net
I am trying to bind to an object that has a list property of complex type.
Problem statement: upon post back I get values in all the properties other than the complex type property, it is returned as nothing. I am doing something wrong because of which default model binder is not able to bind my property which is a list of complex type.
What am i doing wrong?
My View Model classes:
Public Class Customer
Public Property ID As Integer
Public Property FirstName As String
Public Property LastName As String
Public Appointments As IList(Of Appointment)
End Class
Public Class Appointment
Public Property ClientName As String
Public Property [Date] As DateTime
Public Property TermsAccepted As Boolean
End Class
My action methods in controller look like this
Function CreateCusotmerWithMultipleBookings() As ActionResult
Dim Modeldata As New Customer With {.ID = 1, .FirstName = "First Name", .LastName = "Last Name"}
Modeldata.Appointments = New List(Of Appointment) From {New Appointment, New Appointment, New Appointment}
Return View(Modeldata)
End Function
<HttpPost()> _
Function CreateCusotmerWithMultipleBookings(FormData As Customer) As ActionResult
Return View(FormData)
End Function
My view looks like this:
#ModelType Customer
#Using Html.BeginForm
#Html.EditorForModel
If Model.Appointments IsNot Nothing AndAlso Model.Appointments.Count > 0 Then
For i As Integer = 0 To Model.Appointments.Count - 1
#:<b >Appointment #i</b><br />
#Html.TextBoxFor(Function(x) x.Appointments(i).ClientName) #:<br />
#Html.TextBoxFor(Function(x) x.Appointments(i).Date) #:<br />
#Html.TextBoxFor(Function(x) x.Appointments(i).TermsAccepted)#:<br />
Next
End If
End Using
HTML that gets generated from this view is something like: (I have removed unnecessary mark up to ease reading)
<body>
<form action="/testarea/Appointment/CreateCusotmerWithMultipleBookings" method="post">
<input id="ID" name="ID" type="number" value="1" />
<input class="text-box single-line" id="FirstName" name="FirstName" type="text" value="First Name" />
<input class="text-box single-line" id="LastName" name="LastName" type="text" value="Last Name" />
<input id="Appointments_0__ClientName" name="Appointments[0].ClientName" type="text" value="" />
<input id="Appointments_0__Date" name="Appointments[0].Date" type="text" value="1/1/0001 12:00:00 AM" />
<input id="Appointments_0__TermsAccepted" name="Appointments[0].TermsAccepted" type="text" value="False" />
<input id="Appointments_1__ClientName" name="Appointments[1].ClientName" type="text" value="" />
<input id="Appointments_1__Date" name="Appointments[1].Date" type="text" value="1/1/0001 12:00:00 AM" />
<input id="Appointments_1__TermsAccepted" name="Appointments[1].TermsAccepted" type="text" value="False" />
<input id="Appointments_2__ClientName" name="Appointments[2].ClientName" type="text" value="" />
<input id="Appointments_2__Date" name="Appointments[2].Date" type="text" value="1/1/0001 12:00:00 AM" />
<input id="Appointments_2__TermsAccepted" name="Appointments[2].TermsAccepted" type="text" value="False" />
<input type="submit" value="Create customer with multiple appointments" />
</form>
Question again:
Why does the formdata.Appointments in following controller has nothing value in it?
<HttpPost()> _
Function CreateCusotmerWithMultipleBookings(FormData As Customer) As ActionResult
Return View(FormData)
End Function

Related

Thymeleaf handling null object in Asterisk

I have a Spring boot App with a Thymeleaf Template in which I define a th:object="${guest}" and I try to access it with the Asterisk syntax like th:value="*{lastName}".
If th:object="${guest} is null it produces an Error: Property or field 'firstName' cannot be found on null. I understand why the error appears. But what is the best way to handle it?
Since I am using th:object="${guest} to prefill values in an form. My current working solution is:
<form th:if="${guest}" th:object="${guest}" ... >
<form th:unless="${guest} ... >
and this is the part I do not understand why can't Thmyleafe workout in the Asterisk that the object is null? But the th:if / th:unless statment gets that th:object is null. There is no case in which accessing an attribute of a null Object is suitable.
And if the object is not there at all it is fine.
Is there a better way with less code?
I also tried:
th:value="*{lastName}?:_" - same error.
th:object="${guest}:?_" - this cant be parsed at all.
Is there a way without the th:if / th:unless. The optimal solution would be to do it in one line so everything stays clean. Can I somehow parse an if in the th:object${}
I could not find something in the documentation or else on the web. Only if Asterisk or ${..} is null
Full Code:
The Fragment:
<form th:fragment="guest_form">
<input type="text" id="firstName" name="firstName" placeholder="Vorname" th:value="*{firstName}" required />
<input type="text" id="lastName" name="lastName" placeholder="Nachname" th:value="*{lastName}" required />
<input type="tel" id="phone" name="phone" placeholder="Telefon Nummer" th:value="*{phone}" />
<input type="email" id="email" name="email" placeholder="Emailadresse" th:value="*{email}" />
<input type="street" id="street" name="street" placeholder="Straße" th:value="*{street}" />
<input type="city" id="city" name="city" placeholder="Stadt" th:value="*{city}" />
<input type="zip" id="zip" name="zip" placeholder="Postleitzahl" th:value="*{zip}" />
<input type="hidden" name="code" th:value="${code}" />
<input th:replace="::csrf_token" />
<input type="submit" id="submitButton" value="Bestätigen" />
</form>
The fragment use:
<form th:include=" fragments/fragments.html::guest_form" th:if="${guest}" th:object="${guest}" action="/register" method="POST"></form>
<form th:include="fragments/fragments.html::guest_form" th:unless="${guest}" action="/register" method="POST"></form>
The function:
#PostMapping("/register")
public String register(#RequestParam String code, Model model, HttpSession session,
RedirectAttributes redirectAttributes, #ModelAttribute("guest") #Valid Guest g,
BindingResult bindingResult) {
model.addAttribute("guest", g);
model.addAttribute("code", code);
// form is not fine
if (bindingResult.hasErrors()) {
return "guest/register";
}
// form is not fine
guestRepo.save(g);
Booking booking = new Booking(g, invRepo.findByCode(code).get());
bookingRepo.save(booking);
session.setAttribute(saBooking, booking);
return "redirect:/ticket";
}
g can be null because you can get redirect to this url - to fill out the form - in this moment g is null. I think I could check if g == null an then Guest g = new Guest(); but this doesnt target my question...

Override generated checkbox-name (via asp-for)

I want to change the name of a view-inputfield that is generated by an "asp-for" tag. This works when I add a "name=newname" with input fields of type 'text' or 'number'. But not when the inputfield is of type 'checkbox'. Then the "name=newname" is ignored. Any ideas why this is and how to solve it when using asp-for? I'm using AspNetCore.Mvc 1.0.0-rc2-final.
<input type="number" asp-for="#item.StatementSeqNr" name="StatementSeqNr" class="form-control" /> produces correct name:
<input name="StatementSeqNr" class="form-control" type="number" data-val="true" id="item_StatementSeqNr" value="5" />
<input type="checkbox" asp-for="#item.StatementActive" name="StatementActive" /> produces incorrect name:
<input checked="checked" data-val="true" id="item_StatementActive" name="item.StatementActive" type="checkbox" value="true" />
This is currently not possible with the new TagHelpers.
You can achieve this by using the HtmlHelpers instead of the TagHelpers. They are still fully supported in asp.net core:
#Html.EditorFor(item => item.StatementActive, null, "StatementActive")

Spring - form with 2 modelAttributes

I know how to use a form with 1 modelAttribute but how should I set 2 model attributes in same form ?
I have:
<sf:form id="details" action="/updateuser" method="POST" modelAttribute="updateuser">
<sf:input type="text" name="firstName" path="firstName"/></br>
<sf:input type="text" name="lastName" path="lastName" /></br>
<sf:input type="text" name="email" path="email" /><sf:errors path="email" cssClass="error"></sf:errors> </br>
<sf:input type="text" name="facebook" path="facebook" /> </br>
<sf:input type="text" name="twitter" path="twitter" /> </br>
</sf:form>
firstName lastName and email are for modelAttribute="updateuser" but I need another modelAttribute for facebook and twitter
How should I implement this ?
Thanks!
EDIT:
Isn't there any other way to do this without a wrapper class ? For more info, I have:
class User {
private String firstName;
private String lastName;
private String email;
}
class SocialMedia {
private String facebook;
private String twitter;
}
You can't attach more than one object to the <form>. However, you can create an explicit model class for the form and attach it.
class UpdateUserFormModel {
private Updateuser updateuser;
private Facebook facebook;
private Twitter twitter;
}
You can access nested objects using . operator
<sf:input type="text" name="firstName" path="updateuser.firstName"/></br>
<sf:input type="text" name="facebook" path="facebook" /> </br>
No you can't have more than one model attribute for one form, but you can use a wrapper to wrap your models.
class Wrapper {
private Class1 c1;
private Class2 c2;
....
//getter&setters
}
However, you are allowed to bind several objects to the same model using
model.addObject("objectName",object);
and access the objects in your page by
${objectName.name }

How to submit values from a view?

Hi all I have a view with these controls on it
<input type="text" class="radius2" />
<input type="button" value="Place bid" class="bid-btn" />
I want to pass the value in the text box to another view (via an action method) when the button is clicked. The action method should render the view.
How can this be acheived?
Thanks,
Sachin
#using(Html.BeginForm("SubmitAction", "Home", FormMethod.Post))
{
<input id="radius2" name="radius2" type="text" class="radius2" />
<input type="submit" value="Place bid" class="bid-btn" />
}
[HttpPost]
public ActionResult SubmitAction(string radius2)
{
return AnotherView(radius2);
}
public ActionResult AnotherView(string value)
{
return View();
}
Written in notepad so might require small syntax changes
add name property to the tags so the server can access them
<input name="Name1" type="text" class="radius2" />
<input type="button" value="Place bid" class="bid-btn" />
then when submit your action should be something like this
public ActionResult SubmitAction(string name1)
{
return View("ViewName" , /* the model here */ )
}

Posting collections using partial views and microsoft ajax in ASP.Net MVC2

I am using the partial view with the ajax.beginform. In that partial view page, i have the following markup
EDIT
<%
using (Ajax.BeginForm("ManageDataSources", "DataSources", saveAjaxOptions))
{
%>....
<td>
<%: Html.Hidden("DataSource_Id", dataSource.Id, new { #class = "DataSource_Id" })%>
<%: Html.TextBox("DataSource_Name", dataSource.Name, new { #class = "DataSource_Name" })%>
</td>
<tr class="queryParameters" style="display: block">
<td colspan="2" align="center">
<input id="Text1" name="parametername" type="text" />
<input id="Text2" name="parametervalue" type="text" />
<input id="Text3" name="parametername" type="text" />
<input id="Text4" name="parametervalue" type="text" />
<input id="Text5" name="parametername" type="text" />
<input id="Text6" name="parametervalue" type="text" />
<input id="Text7" name="parametername" type="text" />
<input id="Text8" name="parametervalue" type="text" />
<input id="Text9" name="parametername" type="text" />
<input id="Text10" name="parametervalue" type="text" />
</td>
</tr>
and in the the controller, i have this model for the representation of the data
public class DataSourceViewModel
{
public string DataSource_Id { get; set; }
public string DataSource_Name { get; set; }
public List<SCParams> parameters { get; set; }
}
public class SCParams
{
public string parametername { get; set; }
public string parametervalue { get; set; }
}
EDIT
public ActionResult ManageDataSources(DataSourceViewModel dsvm)
{
return PartialView("ManageDataSources");
}
when i post the data these parametername and parameter values are not at all bound to the list of objects. How do i do this. i am using microsoft ajax and want to do this without using other plugings. Kindly suggest the right way.
EDIT
This is the data in the header taken from chrome
DataSource_Id:
DataSource_Name:Name
parametername:a
parametervalue:1
parametername:q
parametervalue:2
parametername:z
parametervalue:3
parametername:s
parametervalue:4
parametername:w
parametervalue:5
x:15
y:12
What I understand you have master detail structure and you want to receive it controller. if that is the case. then there are two possibilities either your detail portion has variable length detail portion or fixed length detail portion. You may follow the post here for variable length as well as fixed length. For Fixed length you may also follow here.
You will receive the model in following signature
public ActionResult ManageDataSources(DataSourceViewModel dsvm)
moreover you may also check formcollection parameter for actionresult
[HttpPost]
public ActionResult MyAction(FormCollection collection)

Resources