Multiple Models in a View in ASP.NET MVC Entity FrameWork - mysql

I want two CheckBoxList an index view, those
CheckBoxList dynamically bind from two different tables. One is Sports
and another is Country.
Basically i try through EditorTemplates, but it not working with two
models. i face problem to use two model in a single view.
This is my Index Method in Controller
SampleDBContext db = new SampleDBContext();
public ActionResult Index()
{
ViewData["Sports"] = db.Sports.ToList();
ViewData["Country"] = db.Countries.ToList();
return View(ViewData["Sports"]);// I am confused and i don't know what to write there we can call both table data.
}
Template View for Sports
#model MVCDEMO.Models.Sports
#Html.HiddenFor(x => x.s_Id)
#Html.CheckBoxFor(x => (bool)x.is_selected)
#Html.DisplayFor( x => x.s_Name)
Template View for Country
#model MVCDEMO.Models.Country
#Html.HiddenFor(x => x.c_Id)
#Html.CheckBoxFor(x => (bool)x.is_selected)
#Html.DisplayFor( x => x.c_Name)
Index View
<div class="form-horizontal">
<div class="form-group">
<label class="col-sm-2 control-label">
Select Sports
</label>
<div class="col-md-3">
#Html.EditorForModel()//what to write it recognize Sports template
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">
Select Country
</label>
<div class="col-md-3">
#Html.EditorForModel()//what to write it recognize Country template
</div>
</div>
</div>
If i work for single Model it work perfectly.

I would personaly use a strongly typed view model having Sports and Countries as properties, instead of using ViewData:
public class IndexPageViewModel
{
public IEnumerable<Sports> Sports { get; set; }
public IEnumerable<Country> Countries { get; set; }
}
Creating two partial views, one for each class (Sport and Country), would allow you to use the #Html.EditorForModel() helper. Reusing the partial views would also be an added benefit, but this obviously depends on the specific application.
Please note that the 'EditorForModel' helper will render an editor for each property of the model. If you need to have a different kind of editor, for example a dropdown menu, I'm not sure EditorForModel should be used.

Related

How to correctly iterate elements?

I'm iterating my Model.Payments collection (which is an public IEnumerable<Payments> Payments { get; set; }):
#using (Html.BeginForm())
{
<div class="payments">
#foreach (var payment in Model.Payments)
{
Html.RenderPartial("_Payment", payment);
}
</div>
<input type="submit" value="Aggiorna" />
}
And this is my _Payment partial:
#model MyProject.Models.Payments
<div class="payment-row">
<span>ActivityID:</span> #Html.TextBoxFor(x => x.ActivityID)
<span>PaymentType:</span> #Html.TextBoxFor(x => x.PaymentType)
<span>Amount:</span> #Html.TextBoxFor(x => x.Amount)
</div>
But it doesn't create a proper HTML. Name/ID are the same, so once I postback, I can't retrieve data.
Where am I wrong?
The correct approach is to use the EditorFor() method, which will correctly prefix your inputs with the collection indexer
Rename you partial view to Payments.cshtml (to match the name of the class), and move it to the /Views/Shared/EditorTemplates (or /Views/YourControllerName/EditorTempatesFolder)
Then you view becomes
#using (Html.BeginForm())
{
<div class="payments">
#Html.EditorFor(m => m.Payments)
</div>
<input type="submit" value="Aggiorna" />
}
The EditorFor() method generates the correct html for each item in the collection, and will generate inputs such as <input name="Payments[0].ActivityID" ... /> rather than <input name="ActivityID" ... /> which you are currently generating.
As a side note, you should consider using #Html.LabelFor() to generate a <label> associated with your form controls, rather than using a <span>.

How to Bind Collection of Objects from UI to Backend using Thymeleaf and Spring Boot

How to return List of Objects to Backend Service? For E.g. there are list of customer getting displayed in UI, out of which only those customers which users selects (checks in checkbox) should be returned to the backend (Controller class). But i am not able to returned the selected object back.
My code:
public class CustomerType {
private String customerName;
private String customerMsg;
private Boolean selected;
// setter
// getter
}
​
public class Customers {
private ArrayList<CustomerType> customerType;
// setter
// getter
}
​
#GetMapping(value = "/")
public String index(ModelMap modelMap) {
ArrayList<CustomerType> customerType = new ArrayList<>();
customerType.add(new CustomerType("1", "c1", null));
customerType.add(new CustomerType("2", "c2", null));
customerType.add(new CustomerType("3", "c3", null));
Customers customers = new Customers();
customers.setCustomerTypes(customerType);
modelMap.put("customers", customers);
return "index";
}
#PostMapping(value = "/save")
public String save(#ModelAttribute Customers customers, BindingResult errors, Model model) {
...
...
return "hello";
}
​
========== index.html ==========
...
<form id = "form" class="col-xs-12 col-sm-4" role="form"
th:action="#{/save}" method="post" th:object="${customers}">
<div class="checkbox" th:each="customerType : ${customers.customerType}" >
<input type="checkbox" id="custType" name="custType"
th:text="${customerType.customerName}" th:value="${customerType.customerMsg}" th:checked="${customerType.selected}"></input>
</div>
<div>
<p>
<button type="submit" class="btn btn-default">Submit</button>
</p>
</div>
</form>
I am able to display lists of Customers on UI e.g there are three customer c1, c2, c3 out of which if user selects c1 and c3 so after clicking on submit button those should get mapped to #ModelAttribute Customers Object in save method and that object should contain list of two Objects c1 and c3, but instead of getting 2 Objects I am receiving Null.
I am not able to get where i am going wrong.
While sending your form back to controller, be sure that transmited data reflect desired object structure. You have to provide checkboxes that correspond the checked field of the CustomerType objects and additional hidden inputs which correspond others fields of mentioned class.
Please update your form to looks like:
<form id = "form" class="col-xs-12 col-sm-4" role="form" th:action="#{/save}" method="post" th:object="${customers}">
<div class="checkbox" th:each="customerType, iterator : ${customers.customerType}" >
<input type="hidden" th:field=*{customerType[__${iterator.index}__].customerName} />
<input type="hidden" th:field=*{customerType[__${iterator.index}__].customerMsg} />
<input type="checkbox" th:field="*{customerType[__${iterator.index}__].selected}" th:text="${customerType.customerName}" ></input>
</div>
<div>
<p>
<button type="submit" class="btn btn-default">Submit</button>
</p>
</div>
</form>
With this you'll receive the Customers object containing list of all passed CustomerType objects, with selected fields evaluated to true for checked records.
The name of the input fields in the form should match the property name in the CustomerType class i.e they should be customerName and customerMsg for the Spring to be able to create and populate the corresponding CustomerType object.

Thymeleaf and Springboot project - tag names

I have a Springboot & Thymeleaf project that is generating the same "names" on my person inputs.
The controller looks like:
#GetMapping("/newEpisode")
public String episodeForm(Model model) {
model.addAttribute("episode", new Episode());
List<Country> countries = countryRepository.findAll();
Set<String> roles = new HashSet<>();
roles.add("Admin");
model.addAttribute("primaryPerson1",new EpisodePerson());
model.addAttribute("primaryPerson2",new EpisodePerson());
model.addAttribute("roles", roles);
model.addAttribute("countries", countries);
return "episode";
}
Some of my HTML looks like:
<input type="text" class="form-control person surname" style="text-transform: uppercase" data-property="surname" placeholder="SURNAME" th:field="${primaryPerson1.person.surname}"/>
But the generated name in the HTML for this tag is not unique:
<input type="text" class="form-control person surname" style="text-transform: uppercase" data-property="surname" id="surname1" placeholder="SURNAME" name="person.surname" value="">
Why are all the person tags in the html sharing the same name for example I have two :
name="person.surname"
You've misused the th:field attribute.
Its aim is to bind your input with a property in the form-backing bean. So you should either create separate forms for each object and use it in following manner:
<!-- Irrelevant attributes omitted -->
<form th:object="${primaryPerson1}">
<input th:field="*{person.surname}"/>
</form>
...or create a form-backing bean, which would combine both of your objects, e.g.:
public class EpisodeFormBean {
private List<EpisodePerson> episodePersons;
//getter and setter omitted
}
...then add it to a model in your episodeForm method...
EpisodeFormBean episodeFormBean = new EpisodeFormBean();
episodeFormBean.setEpisodePersons(Arrays.asList(new EpisodePerson(), new EpisodePerson()));
model.addAttribute("episodeFormBean", episodeFormBean);
...and use it in your template as follow:
<!-- Irrelevant attributes omitted -->
<form th:object="${episodeFormBean}">
<input th:field="*{episodePersons[0].person.surname}"/>
<input th:field="*{episodePersons[1].person.surname}"/>
</form>
In the second solution generated names would be unique. I think it's more suitable for your needs.
You should check out Tutorial: Thymeleaf + Spring as there it is well explained. Especially you should notice a phrase:
Values for th:field attributes must be selection expressions (*{...}),
which makes sense given the fact that they will be evaluated on the
form-backing bean and not on the context variables (or model
attributes in Spring MVC jargon).

Save data in 2 tables mvc

My project is Room-reservation service .I have View :
#model Client
#using (Html.BeginForm("SaveUserDB", "Booking", FormMethod.Post))
{
<div class="editor-label">
#Html.Label("Surname")
#Html.TextBoxFor(model => model.Surname)
</div>
<div class="editor-label">
#Html.Label("Name")
#Html.TextBoxFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.Label("Patronymic")
#Html.TextBoxFor(model => model.Patronymic)
</div>
<input type="submit" id="submit" value="Reservation" />
And I have controller for this View:
[HttpPost]
public ActionResult SaveUserDB(Client client)
{
if (ModelState.IsValid)
{
using (db)
{
db.Client.Add(client);
db.SaveChanges();
return RedirectToAction("Thankyou");
}
}
return View(client);
}
This controller save data client to database table Client. But I need also create record in second table Reservation, which takes parameters: Datetime.Now , and Client.Id. Parameter Client Id in database is autoincrement, but doesn't display in the View.
Well, if this is how you add a record to the Client table:
db.Client.Add(client);
Then why not use that same exact approach to add a record to the Reservation table? Something like this:
var reservation = new Reservation
{
ClientID = client.ID,
SomeOtherColumn = DateTime.Now
};
db.Reservation.Add(reservation);
(Note: This is based on speculation of what your Reservation object/table might look like based on your description. But the concept is the same. Create an instance of a reservation object and add it to the data context.)

How to Get Model Data from Partial View?

I am creating a site in which I utilize partial views to display various bits of data about a single Model. Here is a bit of the HTML. (Note, all of these are contained within a single form and the Index page that these partials are rendered in is strongly typed to the main model. The main model contains various lists of data.)
<div id="tab1"><% Html.RenderPartial("Tab1", Model); %></div>
<div id="tab2"><% Html.RenderPartial("Tab2", Model.AnItemList1.FirstOrDefault<AnItemList1>()); %></div>
<div id="tab3"><% Html.RenderPartial("Tab3", Model.AnItemList2.FirstOrDefault()); %></div>
Here is ONE of the partial views headers (for 'tab2'):
<%# Language="C#" Inherits="System.Web.Mvc.ViewUserControl<AnItem1>" %>
The pages display correctly. The issue is that, when I enter data into the various parts of the partial pages and then submit the entire form (via POST), the data is not making it back to my data store (MSSQL) - but this only happens for any of the list items (that are contained within the Model). The first partial page does properly have its data set within the data store.
What am I doing wrong here? Should I only be passing the model to Html.RenderPartial and then get the specific model I need on the partial page? Should I pass the entire list and then get the first (right now, I only care about the first item in the list - that will EVENTUALLY change, but not any time soon).
Suggestions or thoughts appreciated.
Update: Here is how I accessing the properties on the partial views.
<div class="data-group">
<%: Html.CheckBoxFor(model => model.Property1) %>
<%: Html.LabelFor(model => model.Property1) %>
</div>
Update 2: Per request...
Controller Action (ScenarioController):
public ActionResult Index(int id = 0)
{
if (id == 0)
{
SavedScenario scenario = new SavedScenario();
scenario.AnItemList1.Add(new AnItem1());
scenario.AnItemList2.Add(new AnItem2());
return View("Index", scenario);
}
else
{
SavedScenario scenario = repository.GetScenario(id);
if (scenario == null)
return View("NotFound");
else
return View("Index", scenario);
}
}
[HttpPost]
public ActionResult Index(SavedScenario scenario)
{
if (ModelState.IsValid && TryUpdateModel(scenario, "SaveScenario"))
{
repository.Add(scenario);
repository.Save();
}
return View(scenario);
}
Rendered HTML (I can only include parts of it - this is a small sample of what is in the form):
<form action="/Scenario" id="form0" method="post">
<!-- This is the one that works - the basic Scenario. Top level. -->
<fieldset>
<legend>Scenario Information</legend>
<div class="data-group">
<div class="editor-label">
<label for="ScenarioName">Scenario Name</label>
</div>
<div class="option1">
<input class="wide" id="ScenarioName" name="ScenarioName" type="text" value="" />
</div>
<div class="validation">
<div><span class="field-validation-valid" id="ScenarioName_validationMessage"></span></div>
</div>
</div>
</fieldset>
<!-- This does not work or get submitted (as far as I can tell). -->
<div id="tab2">
<fieldset>
<legend>Tab2</legend>
<div class="data-group">
<input id="Property1" name="Property1" type="checkbox" value="true" /><input name="Property1" type="hidden" value="false" />
<label for="Property1" />
</div>
</div>
</fieldset>
</form>
My apologies for having to keep this so generic.
Hard to guess from this much code. However you should make sure that all properties of your models have the same prefix when they are posted back to the server
Edit: form field names should match property names of your model to correctly bind all values. You have two fields with the same name that you can bind in following way
[HttpPost]
public ActionResult Index(SavedScenario scenario, List<bool> Property1)
{
// here you can do with values coming in property1
if (ModelState.IsValid && TryUpdateModel(scenario, "SaveScenario"))
{
repository.Add(scenario);
repository.Save();
}
return View(scenario);
}
It might be issue with naming the fields on your partial forms. Try naming the fields on your partial views by prefixing it with the name of the Model passed into it...like 'AnItemList1.name' instead of just 'name'..I am just guessing here though...but that's what I did sometimes to fix the problem when I was getting values as null..