How to fill out fields in one form for multiple objects in Thymeleaf? - post

I have a following models (without getters and setters for readability):
#Entity
public class Recipe extends BaseEntity {
private String name;
private String description;
private Category category;
#OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL)
private List<Ingredient> ingredients;
#OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL)
private List<Instruction> instructions;
#ManyToMany
private List<User> administrators;
private int preparationTime;
private int cookTime;
public Recipe(){
super();
ingredients = new ArrayList<>();
instructions = new ArrayList<>();
administrators = new ArrayList<>();
}
public Recipe(String name, String description, Category category, int preparationTime, int cookTime) {
this();
this.name = name;
this.description = description;
this.category = category;
this.preparationTime = preparationTime;
this.cookTime = cookTime;
}
*
#Entity
public class Ingredient extends BaseEntity {
private String name;
private String condition;
private double quantity;
private Measurement measurement;
#ManyToOne
private Recipe recipe;
public Ingredient(){
super();
}
public Ingredient(String name, String condition, double quantity, Measurement measurement) {
this();
this.name = name;
this.condition = condition;
this.quantity = quantity;
this.measurement = measurement;
}
*
#Entity
public class Instruction extends BaseEntity {
private String name;
private String description;
#ManyToOne
private Recipe recipe;
public Instruction(){
super();
}
public Instruction(String name, String description) {
this();
this.name = name;
this.description = description;
}
What i need to do is to fill out fields for each object in one Thymeleaf form and POST it. I know how to do it with a single object. Please explain how to set up the from and controller for multiple objects, so in the end ill have recipe posted with ingredients and instructions list. Thanks!
EDITED:
Here is a controller methods:
#RequestMapping("/recipes/add")
public String formNewRecipe(Model model) {
Recipe recipe = new Recipe();
if (!model.containsAttribute("recipe")) {
model.addAttribute("recipe", recipe);
}
model.addAttribute("action", "/recipes");
model.addAttribute("heading", "New Recipe");
model.addAttribute("submit", "Save");
model.addAttribute("categories", Category.values());
model.addAttribute("measurements", Measurement.values());
return "edit";
}
#RequestMapping(value = "/recipes", method = RequestMethod.POST)
public String addRecipe(#Valid Recipe recipe,
BindingResult result,
RedirectAttributes redirectAttributes) {
if (result.hasErrors()) {
redirectAttributes
.addFlashAttribute("org.springframework.validation.BindingResult.recipe", result);
redirectAttributes.addFlashAttribute("recipe", recipe);
return "redirect:/recipes/add";
}
recipes.save(recipe);
redirectAttributes.addFlashAttribute("flash",
new FlashMessage("New Recipe Created!!!", FlashMessage.Status.SUCCESS));
return "redirect:/recipes/" + recipe.getId();
}
and Thymeleaf form:
<form th:action="#{${action}}" method="post" th:object="${recipe}">
<div class="grid-100 row controls">
<div class="grid-50">
<h2 th:text="${heading}"></h2>
</div>
<div class="grid-50">
<div class="flush-right">
<input class="button" type="submit" th:value="${submit}"/>
<a th:href="#{|/recipes|}" class="secondary">
<button class="secondary">Cancel</button>
</a>
</div>
</div>
</div>
<div class="clear"></div>
<div class="grid-100 row">
<div class="grid-20">
<p class="label-spacing">
<label> Name </label>
</p>
</div>
<div class="grid-40">
<p><input type="text" th:field="*{name}"/>
<div class="error-message"
th:if="${#fields.hasErrors('name')}"
th:errors="*{recipe.name}">
</div>
</p>
</div>
</div>
<div class="clear"></div>
<div class="grid-100 row">
<div class="grid-20">
<p class="label-spacing">
<label> Description </label>
</p>
</div>
<div class="grid-40">
<p><textarea rows="4" th:field="*{description}"></textarea>
<div class="error-message"
th:if="${#fields.hasErrors('description')}"
th:errors="*{recipe.description}">
</div>
</p>
</div>
</div>
<div class="clear"></div>
<div class="grid-100 row">
<div class="grid-20">
<p class="label-spacing">
<label> Category </label>
</p>
</div>
<div class="grid-30">
<p>
<select th:field="*{category}">
<option value="" disabled="disabled">Recipe Category</option>
<option th:each="c : ${categories}"
th:value="${c.name}"
th:text="${c.name}">All Categories</option>
</select>
</p>
</div>
</div>
<div class="clear"></div>
<div class="grid-100 row">
<div class="grid-20">
<p class="label-spacing">
<label> Prep Time </label>
</p>
</div>
<div class="grid-20">
<p>
<input type="number" th:field="*{preparationTime}"/>
<div class="error-message"
th:if="${#fields.hasErrors('preparationTime')}"
th:errors="*{preparationTime}"></div>
</p>
</div>
</div>
<div class="clear"></div>
<div class="grid-100 row">
<div class="grid-20">
<p class="label-spacing">
<label> Cook Time </label>
</p>
</div>
<div class="grid-20">
<p>
<input type="number" th:field="*{cookTime}"/>
<div class="error-message"
th:if="${#fields.hasErrors('cookTime')}"
th:errors="*{cookTime}"></div>
</p>
</div>
</div>
<div class="clear"></div>
<div class="grid-100 row">
<div class="grid-20">
<p class="label-spacing">
<label> Ingredients </label>
</p>
</div>
<div class="grid-20">
<p class="label-spacing">
<label> Item </label>
</p>
</div>
<div class="grid-20">
<p class="label-spacing">
<label> Condition </label>
</p>
</div>
<div class="grid-15">
<p class="label-spacing">
<label> Quantity </label>
</p>
</div>
<div class="grid-20">
<p class="label-spacing">
<label> Measurement </label>
</p>
</div>
<div class="ingredient-row">
<div class="prefix-20 grid-20">
<p>
<input type="text" th:field="*{ingredients[0].name}"/>
<div class="error-message"
th:if="${#fields.hasErrors('ingredients[0].name')}"
th:errors="*{ingredients[0].name}"></div>
</p>
</div>
<div class="grid-20">
<p>
<input type="text" th:field="*{ingredients[0].condition}"/>
<div class="error-message"
th:if="${#fields.hasErrors('ingredients[0].condition')}"
th:errors="*{ingredients[0].condition}"></div>
</p>
</div>
<div class="grid-15">
<p>
<input type="number" th:field="*{ingredients[0].quantity}"/>
<div class="error-message"
th:if="${#fields.hasErrors('ingredients[0].quantity')}"
th:errors="*{ingredients[0].quantity}"></div>
</p>
</div>
<div class="grid-20">
<p>
<select th:field="*{ingredients[0].measurement}">
<option value="" disabled="disabled">Measurement</option>
<option th:each="i : ${measurements}"
th:value="${i.name}"
th:text="${i.name}">Unknown
</option>
</select>
</p>
</div>
</div>
<div class="prefix-20 grid-80 add-row">
<p>
<button>+ Add Another Ingredient</button>
</p>
</div>
</div>
<div class="clear"></div>
<div class="grid-100 row">
<div class="grid-20">
<p class="label-spacing">
<label> Instructions </label>
</p>
</div>
<div class="grid-20">
<p class="label-spacing">
<label> Steps </label>
</p>
</div>
<div class="grid-60">
<p class="label-spacing">
<label> Description </label>
</p>
</div>
<div class="instruction-row">
<div class="prefix-20 grid-20">
<p>
<input type="text" th:field="*{instructions[0].name}"/>
<div class="error-message"
th:if="${#fields.hasErrors('instructions[0].name')}"
th:errors="*{instructions[0].name}"></div>
</p>
</div>
</div>
<div class="instruction-row">
<div class="grid-50">
<p>
<input type="text" th:field="*{instructions[0].description}"/>
<div class="error-message"
th:if="${#fields.hasErrors('instructions[0].description')}"
th:errors="*{instructions[0].description}"></div>
</p>
</div>
</div>
<div class="prefix-20 grid-80 add-row">
<p>
<button>+ Add Another Step</button>
</p>
</div>
</div>
<div class="clear"></div>
<div class="row"> </div>
</form>

It looks something like this:
<form th:object="${recipe}">
<input type="text" th:field="*{ingredients[0].name}" />
<input type="text" th:field="*{ingredients[0].description}" />
<input type="text" th:field="*{instructions[1].name}" />
<input type="text" th:field="*{instructions[1].description}" />
</form>
If you have a dynamic amount of ingredients, the th:each might look like this:
<th:block th:each="ingredient,i : ${recipe.ingredients}">
<input type="text" th:field="*{ingredients[__${i.index}__].name}" /><br />
<input type="text" th:field="*{ingredients[__${i.index}__].condition}" /><br />
<input type="text" th:field="*{ingredients[__${i.index}__].quantity}" /><br />
<input type="text" th:field="*{ingredients[__${i.index}__].measurement.anotherField}" /><br />
</th:block>
Dynamically adding another ingredient to the form is kind of painful... you either have to submit the form and modify the Recipe object in a controller (adding an ingredient, then redirecting back to the form). Or you can use javascript to copy the fields, making sure the name/id/etc match the others, with the index incremented.

Related

Is it possible to generate new instances of the same form by clicking a button?

I'm working on a use case in my animal shelter web application where customers are able to register one or more animals at the same time. Ideally I'd like a button on the bottom left that generates another instance of the same form when clicked, so that multiple animal registrations can be saved to the database at once.
NewAnimalRegistration.cshtml:
#model NewAnimalRegistrationViewModel
<html>
<head>
<title>Register an animal</title>
<link rel="stylesheet" href="~/css/style.css"/>
</head>
<body>
<div class="container py-5">
<div class=" row">
<div class="col-md-10" mx-auto>
<div asp-validation-summary="All"></div>
<h1>Animal registration</h1>
<p>
We are happy to hear that you are interested in placing your animal in our shelter. Please fill in the fields below and our system will
check if there is room for your animal.
</p>
<form asp-action="RegistrationForm" method="post">
<div class="form-group row mt-5">
<div class="col-sm-6">
<label asp-for="Name">Name</label>
<input asp-for="Name" class="form-control"/>
</div>
</div>
<div class="form-group row mt-5">
<div class="col-sm-4">
<label asp-for="Gender" class="mr-3">Gender</label>
<select class="form-group" asp-for="Gender" asp-items="#ViewBag.Genders"></select>
</div>
<div class="col-sm-4">
<label asp-for="Type" class="mr-3">Animal type</label>
<select class="form-group" asp-for="Type" asp-items="#ViewBag.AnimalTypes"></select>
</div>
<div class="col-sm-4">
<label>Neutered</label>
<div class="form-check">
<input asp-for="IsNeutered" class="form-check-input" type="radio" value="true">
<label class="form-check-label" asp-for="IsNeutered">
Yes
</label>
</div>
<div class="form-check">
<input asp-for="IsNeutered" class="form-check-input" type="radio" value="false">
<label class="form-check-label" asp-for="IsNeutered">
No
</label>
</div>
</div>
</div>
<div class="form-group mt-5">
<label asp-for="Reason">Why are you deciding to put this animal up for adoption?</label>
<textarea class="form-control" asp-for="Reason" rows="6"></textarea>
</div>
<div class="float-right">
<a asp-controller="Home" asp-action="Index" class="btn btn-primary px-4">Cancel</a>
<button class="btn btn-primary px-4">Save</button>
</div>
</form>
</div>
</div>
</div>
</body>
</html>
Is there a way to do this in .NET Core MVC? If yes, will I simply receive a list of all animal registrations through which I can simply loop and add them all to the database?
I made a demo based on your description, you can refer to it:
Model:
public class NewAnimalRegistrationViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
public string Type { get; set; }
public bool IsNeutered { get; set; }
public string Reason { get; set; }
}
Index.cshtml:
#model NewAnimalRegistrationViewModel
<html>
<head>
<title>Register an animal</title>
</head>
<body>
<div class="container py-5">
<div class=" row">
<div class="col-md-10" mx-auto>
<div asp-validation-summary="All"></div>
<h1>Animal registration</h1>
<p>
We are happy to hear that you are interested in placing your animal in our shelter. Please fill in the fields below and our system will
check if there is room for your animal.
</p>
<form asp-action="RegistrationForm" method="post">
<div class="float-right">
<a asp-controller="Home" asp-action="Index" class="btn btn-primary px-4">Cancel</a>
<button class="btn btn-primary px-4">Save</button>
</div>
</form>
<a id="add" href='#' class="text-danger">register another animal</a>
</div>
</div>
</div>
</body>
</html>
#section scripts{
<script>
var count = 0;
$(function () {
var actionUrl = "/Home/AddRegistrationForm?count=" + count;
$.get(actionUrl).done(function (data) {
$('body').find('.float-right').before(data);
});
})
$("#add").on("click", function (e) {
e.preventDefault();
count++;
var actionUrl = "/Home/AddRegistrationForm?count=" + count;
$.get(actionUrl).done(function (data) {
$('body').find('.float-right').before(data);
});
})
</script>
}
_RegisterPartial.cshtml:
#model NewAnimalRegistrationViewModel
#{
int i = ViewBag.Count;
}
<h3>Anaimal #i</h3>
<div class="form-group row mt-5">
<div class="col-sm-6">
<label asp-for="Name">Name</label>
<input asp-for="Name" name="[#i].Name" class="form-control" />
</div>
</div>
<div class="form-group row mt-5">
<div class="col-sm-4">
<label asp-for="Gender" class="mr-3">Gender</label>
<select class="form-group" asp-for="Gender" name="[#i].Gender" asp-items="#ViewBag.Genders"></select>
</div>
<div class="col-sm-4">
<label asp-for="Type" class="mr-3">Animal type</label>
<select class="form-group" asp-for="Type" name="[#i].Type" asp-items="#ViewBag.AnimalTypes"></select>
</div>
<div class="col-sm-4">
<label>Neutered</label>
<div class="form-check">
<input asp-for="IsNeutered" name="[#i].IsNeutered" class="form-check-input" type="radio" value="true">
<label class="form-check-label" asp-for="IsNeutered">
Yes
</label>
</div>
<div class="form-check">
<input asp-for="IsNeutered" name="[#i].IsNeutered" class="form-check-input" type="radio" value="false">
<label class="form-check-label" asp-for="IsNeutered">
No
</label>
</div>
</div>
</div>
<div class="form-group mt-5">
<label asp-for="Reason">Why are you deciding to put this animal up for adoption?</label>
<textarea class="form-control" asp-for="Reason" name="[#i].Reason" rows="6"></textarea>
</div>
Controller:
public IActionResult Index()
{
return View();
}
[HttpGet]
public IActionResult AddRegistrationForm(int count)
{
ViewBag.Count = count;
ViewBag.Genders = new List<SelectListItem>
{
new SelectListItem{ Text = "Female", Value="Female"},
new SelectListItem{ Text = "Male", Value="Male"}
};
ViewBag.AnimalTypes = new List<SelectListItem>
{
new SelectListItem{ Text = "Cat", Value="Cat"},
new SelectListItem{ Text = "Dog", Value="Dog"}
};
return PartialView("_RegisterPartial");
}
[HttpPost]
public IActionResult RegistrationForm(List<NewAnimalRegistrationViewModel> model)
{
return View();
}
Result:

ASP.NET Core MVC validate not required fields

My page is validating a field that is not required when I submit, even though there is no validation configured for this field.
Create.cshtml
#model Lawtech.App.ViewModels.ProcessoViewModel
#{
ViewData["Title"] = "Novo processo";
}
<h3 style="padding-top: 10px">#ViewData["Title"] </h3>
<hr />
<div class="row">
<div class="col-md-12">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="row">
<div class="form-group col-md-4">
<label asp-for="Numero" class="control-label"></label>
<input asp-for="Numero" class="form-control" />
<span asp-validation-for="Numero" class="text-danger"></span>
</div>
<div class="form-group col-sm-4">
<label asp-for="IdArea" class="control-label"></label>
<div class="input-group">
<select id="slcArea" asp-for="IdArea" class="form-control select2"></select>
<div class="input-group-btn">
<a asp-action="CreateArea" class="btn btn-info" style="border-radius:0 0.25rem 0.25rem 0" data-modal="">
<span class="fa fa-plus"></span>
</a>
</div>
</div>
</div>
</div>
<div class="row">
<div class="form-group col-md-6 mt-4">
<input type="submit" value="Cadastrar" class="btn btn-sm btn-primary" />
<a class="btn btn-sm btn-info" asp-action="Index">Voltar</a>
</div>
</div>
</form>
</div>
</div>
<div id="myModal" class="modal fade in">
<div class="modal-dialog">
<div class="modal-content">
<div id="myModalContent"></div>
</div>
</div>
</div>
ViewModel
public class ProcessoViewModel
{
[Key]
public int Id { get; set; }
[DisplayName("Número")]
[Required(ErrorMessage = "O campo número é obrigatório")]
public string Numero { get; set; }
[DisplayName("Área")]
public int IdArea { get; set; }
}
Controller
In Controller Create method, nothing happens, because all validation takes place on the client side.
[Route("novo-processo")]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(ProcessoViewModel processoViewModel)
{
try
{
if (!ModelState.IsValid) return View(processoViewModel);
await _processoBLL.Insert(_mapper.Map<ProcessoDTO>(processoViewModel));
if (!ValidOperation()) return View(processoViewModel);
return RedirectToAction(nameof(Index));
}
catch
{
return View();
}
}
Inspecting in Chrome I see this generated html for the field that I didn't require validation, I don't know if it could be something related to Jquery.Unobtrusive but I can't remove it either because other fields will be validated.
<select id="slcArea" class="form-control select2 select2-hidden-accessible input-validation-error" data-val="true" data-val-required="The Área field is required." name="IdArea" data-select2-id="slcArea" tabindex="-1" aria-hidden="true" aria-describedby="slcArea-error" aria-invalid="true"></select>
Why is this validation taking place that I have not defined the field as required?
Not nullable properties (that is properties with value types) are always required. Use nullable types (reference types) for properties if they should not be required - eg. int?.
You can always use formnovalidate on any input you don't want validated.
<input asp-for="Numero" formnovalidate="formnovalidate" class="form-control" />
This way there is no need to change your model. This is demonstrated at W3Schools

Get request from RazorPage with ViewComponent

I tray use ViewComponent in Razor Page with condition,
and each Viewcomponents are separate,
My razorpage is "/Subfolder/Index.cshtml"
<div class="row">
<div class="col-md-4">
#await Component.InvokeAsync("RightMenu")
</div>
<div class="col-md-8">
#if (Model.SIndex != 0)
{
#await Component.InvokeAsync("SubContent", new { id = Model.SIndex })
}
#if (Model.SIndex == 15)
{
<div class="row">
<div class="col-md-12">
<form method="post">
#await Component.InvokeAsync("QuestionUs", new { askLibrarian = new Lib.Model.AskLibrarian() })
<div class="form-group">
<input type="submit" value="ask Question" class="btn btn-default" asp-page-handler="question" />
</div>
</form>
</div>
</div>
}
</div>
and code behind of this is "/subfolder/index.cshtml.cs"
public class IndexModel : PageModel
{
private readonly Lib.Model.LibContext _context;
[BindProperty]
public int SIndex { get; set; }
public async Task OnGet(int Id)
{
SIndex = Id;
}
[BindProperty]
public AskLibrarian AskLibrarian { get; set; }
public async Task<IActionResult> OnPostquestionAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.AskLibrarians.Add(AskLibrarian);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
Now "questionViewcomponent" is a simple form that show many input elements
in "/subfolder/component/questionus/default.cshtml"
#model Lib.Model.AskLibrarian
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-row">
<div class="form-group col-md-6">
<label asp-for="FullName" class="control-label"></label>
<div class="input-group mb-2 mr-sm-2">
<div class="input-group-prepend">
<div class="input-group-text"><i class="fas fa-user"></i></div>
</div>
<input asp-for="FullName" class="form-control" />
<span asp-validation-for="FullName" class="text-danger"></span>
</div>
</div>
<div class="form-group col-md-6">
<label asp-for="Email" class="control-label"></label>
<div class="input-group mb-2 mr-sm-2">
<div class="input-group-prepend">
<div class="input-group-text"><i class="fas fa-envelope"></i></div>
</div>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<label asp-for="LibraryNameId" class="control-label"></label>
<div class="input-group mb-2 mr-sm-2">
<div class="input-group-prepend">
<div class="input-group-text"><i class="fas fa-book"></i></div>
</div>
<select asp-for="LibraryNameId" class="form-control" asp-items="ViewBag.LibraryNameId"></select>
</div>
</div>
<div class="form-group col-md-8">
<label asp-for="Subject" class="control-label"></label>
<div class="input-group mb-2 mr-sm-2">
<div class="input-group-prepend">
<div class="input-group-text"><i class="fas fa-book-reader"></i></div>
</div>
<input asp-for="Subject" class="form-control" />
<span asp-validation-for="Subject" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-group">
<label asp-for="Text" class="control-label"></label>
<textarea asp-for="Text" class="form-control" style="min-height:250px;"></textarea>
<span asp-validation-for="Text" class="text-danger"></span>
</div>
set breakpoint on "OnpostQuestionAsync", When I click on submit button with "question" handler do nothing and show me a blank page instead question form.
how can I resolve That
After a long Time my problem resolved by remove
<input type="submit" value="ask Question" class="btn btn-default" asp-page-handler="question" />
and add in form tag
<form method="post" sp-page-handler="question">

FormCollection inputs on action method have null values

I am trying to get values of form inputs on action method.
MVC View
#using (Html.BeginForm("Report", "Home"))
{
<div class="common_input Volunteer_input">
<div class="row">
<div class="col-sm-12">
<label for="firstName">Reporter:</label>
<div class="select_option_one">
<input type="text" id="ReporterCountries" class="ct-select2 ReporterCountriesList" />
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<label for="firstName">Partner:</label>
<div class="select_option_one">
<input type="text" name="PartnersCountries" id="PartnersCountries" class="ct-select2 CountryList" />
</div>
</div>
</div>
<h3>Comparators</h3>
<div class="row">
<div class="col-sm-12">
<label for="firstName">Partner:</label>
<div class="select_option_one">
<input type="text" name="CompareCountryList" id="CompareCountryList" class="ct-select2 CountryList" />
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<label for="firstName">Rank Growth:</label>
<div class="select_option_one">
<input type="text" name="RankGrowth" id="RankGrowth" class="ct-select2 RankGrowth" />
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div class="sub_submit">
<button type="submit" class="common_btn hvr-sweep-to-bottom">View<i class="fa fa-long-arrow-right"></i></button>
</div>
</div>
</div>
</div>
}
Action Method for Home Controller
public ActionResult Report(FormCollection form)
{
ViewData["ReporterCountries"] = Request.Form["ReporterCountries"];;
return RedirectToAction("Index","data");
}
When I try to retrieve the values of input on action controller I am receving all the control on the view but values are coming as null.

Html helper within an html helper (rendering razor correctly from string literal)

I made a custom HTML helper for the contact modal so that anyone on our app can use it with #Html.ContactModal(model and options go here), but the issue is that on the modal there's another html helper for the sales code drop down, and the razor isn't rendering correctly. I tried to do everything with a partial view only to learn you can't (or shouldn't) use partials with html helpers, only with controllers (I'm new to all of this...), a co-worker suggested I use a tag builder, but I wanted to ask here first before writing the entire form with a tag builder.
public static IHtmlString ContactModal(this HtmlHelper htmlHelper, ContactModalOptions options)
{
//required scripts
htmlHelper.AddScriptFile("~/Areas/Customers/Scripts/ContactModal/tmi.customers.contactModal.js");
htmlHelper.AddScriptLine("$('#" + options.controlId + "').contactModal(" + options.ToJson() + ");", true);
string htmlContents = string.Empty;
htmlContents = $#"<div id='contactModalContainer' style='display: none'>
<div class='panel panel-default panel-details' id='' style='display: none'>
<div class='panel-heading' id='divContactModalHeading'>
<h3 id='h3contactModalHeading'></h3>
</div>
<div class='panel-body' id='divContactDetBody'>
<form id='custContactDetailsForm'>
<div class='row'>
<div class='col-sm-6'>
<div class='form-group' style=''>
<label for='txtFirstName' id='lblFirstName' class='control-label'>First Name: </label>
<input data-helper='FirstName' id='contactFirstName' type='text' class='form-control' disabled='disabled' data-editable='true' value='' />
</div>
</div>
<div class='col-sm-6'>
<div class='form-group' style=''>
<label for='txtLastName' class='control-label'>Last Name: </label>
<input data-helper='LastName' id='contactLastName' type='text' class='form-control' disabled='disabled' data-editable='true' value='' />
</div>
</div>
<div class='col-md-12'>
<div class='form-group' style=''>
<label for='txtPositionTitle'>Position/Title: </label>
<input id='contactPosition' data-helper='Position' type='text' class='form-control' disabled='disabled' data-editable='true' value='' />
</div>
</div>
<div class='col-md-12'>
<div class='form-group' style=''>
<label for='txtSalesOrders'>E-mail: </label>
<input id='contactEmail' data-helper='Email' type='text' class='form-control' disabled='disabled' data-editable='true' value='' />
</div>
</div>
<div class='col-sm-4'>
<div class='form-group' style=''>
<label for='txtSalesOrders'>Work Phone: </label>
<input id='contactWorkPhone' data-helper='DirectPhone' type='text' class='form-control' disabled='disabled' data-editable='true' value='' />
</div>
</div>
<div class='col-sm-4'>
<div class='form-group' style=''>
<label for='txtMobilePhone'>Mobile Phone: </label>
<input id='contactMobile' data-helper='MobilePhone' type='text' class='form-control' disabled='disabled' data-editable='true' value='' />
</div>
</div>
<div class='col-sm-4'>
<div class='form-group' style=''>
<label for='txtFax'>Fax: </label>
<input id='contactFax' data-helper='Fax' type='text' class='form-control' disabled='disabled' data-editable='true' value='' />
</div>
</div>
<div class='col-sm-4'>
<div class='form-group' style=''>
<label for='txtHomePhone'>Home Phone: </label>
<input id='contactHomePh' data-helper='HomePhone' type='text' class='form-control' disabled='disabled' data-editable='true' value='' />
</div>
</div>
<div class='col-sm-4'>
<div class='form-group' style=''>
<label for='txtPager'>Other Phone: </label>
<input id='contactPager' data-helper='PagerPhone' type='text' class='form-control' disabled='disabled' data-editable='true' value='' />
</div>
</div>
<div class='col-sm-12'>
<div class='form-group'>
<label for='txtNotes'>Notes/Comments: </label>
<textarea id='contactNotes' data-helper='ContactNotes' type='text' class='form-control' disabled='disabled' rows='3' data-editable='true' value=''></textarea>
</div>
</div>
<div class='col-sm-6'>
<div class='form-group' style=''>
<label for='txtDateAdded'>Date Added: </label>
<label id='contactDateAdded'></label>
</div>
</div>
<div class='col-sm-6' style='clear: both'>
<div class='form-group'>
<label class='control-label'>Referred By:</label>
<div class=''>
##Html.SalesCodeDropDown(new SelectControlOptionsBase('contactReferredBy')
{{
includeNull = true,isEditable = editableOptions.yes,
disabled = true,
}})
</ div >
</ div >
</ div >
< div class='col-md-6' style='clear: both'>
<div class='form-group' style=''>
<label for='txtEnteredBy'>Entered By: </label>
<label id = 'contactEnteredBy' ></ label >
</ div >
</ div >
< div class='col-md-6' style='clear: both'>
<div class='checkbox'>
<label>
<input id = 'contactIsInactiveChkBx' data-helper='Inactive' type='checkbox' disabled data-editable='true' value='0'>Inactive
</label>
</div>
</div>
</div>
</form>
</div>
</div>
<div id = 'contactModalFooter' class='form-group' style=''>
<button type = 'button' id='btnContactModalClose' style='margin-right: 5px;' class='pull-right btn btn-form-state'>Close</button>
<button type = 'button' id='btnContactModalCancel' style='margin-right: 5px;' class='pull-right btn btn-form-state'>Cancel</button>
<button type = 'button' id='btnContactModalSave' style='background-color: #EE6723; margin-right: 5px;' class='pull-right btn btn-form-state'>Save</button>
</div>
</div> ";
return MvcHtmlString.Create(htmlContents);
This ended up working:
public static IHtmlString ContactModal(this HtmlHelper htmlHelper, ContactModalOptions options)
{
StringBuilder result = new StringBuilder();
//required scripts
htmlHelper.AddScriptFile("~/Areas/Customers/Scripts/ContactModal/tmi.customers.contactModal.js");
htmlHelper.AddScriptLine("$('#" + options.controlId + "').contactModal(" + options.ToJson() + ");", true);
var body = htmlHelper.Partial("~/Areas/Customers/Views/Contacts/Shared/ContactModalPartial.cshtml").ToHtmlString();
result.AppendLine(body);
return MvcHtmlString.Create(result.ToString());
}
If your #Html.SalesCodeDropDown(new SelectControlOptionsBase('contactReferredBy') returns html, then just call that before you set the variable htmlContents and then concat it where you need to.

Resources