Asp.Net MVC Core built-in tag helpers won't process attributes provided from custom taghelpers - asp.net-mvc

Out of boredom of writing the same boilerplate forms, I thought I'd rather write a tag helper that would produce attributes that can be processed from stock tag helpers. But, even though I managed to get my tag helper before work before form tag helpers, form tag helpers won't process the ones that I produce.
Here is the cshtml:
#model City
#{
Layout = "_Layout";
ViewData["Title"] = "Create City";
}
<form method="post" asp-action="Create">
#foreach (string propName in BaseModel.GetProperties<City>()) {
<formgroup for="#propName" />
}
<div class="form-group">
<label asp-for="Name">Name:</label>
<input class="form-control" asp-for="Name" />
</div>
<div class="form-group">
<label asp-for="Country">Country:</label>
<input class="form-control" asp-for="Country" />
</div>
<div class="form-group">
<label asp-for="Population">Population:</label>
<input class="form-control" asp-for="Population" />
</div>
<button type="submit" class="btn btn-primary">Create</button>
<a class="btn btn-secondary" asp-controller="Home" asp-action="Index">Cancel</a>
</form>
Here is the output:
<form method="post" action="/City/Create">
<div class="form-group"><label asp-for="Name"></label><input asp-for="Name" class="form-control"></div>
<div class="form-group"><label asp-for="Country"></label><input asp-for="Country" class="form-control"></div>
<div class="form-group"><label asp-for="Population"></label><input asp-for="Population" class="form-control"></div>
<div class="form-group">
<label for="Name">Name:</label>
<input class="form-control" type="text" id="Name" name="Name" value="">
</div>
<div class="form-group">
<label for="Country">Country:</label>
<input class="form-control" type="text" id="Country" name="Country" value="">
</div>
<div class="form-group">
<label for="Population">Population:</label>
<input class="form-control" type="number" data-val="true" data-val-required="The Population field is required." id="Population" name="Population" value="">
</div>
<button type="submit" class="btn btn-primary">Create</button>
<a class="btn btn-secondary" href="/">Cancel</a>
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8M_6usK6CRRNkwluTiTW8uaAAfhMcU9tAxyCT7z55zQKmpUwpi_lfSDIN4FrlMo9cE3Ka9zgX4WdpXHUdlBFVGsLIw7h_cR3FjJb6Vjqnjm8mQmtKTey_9l188p9E2sKgiksO_OB6K9-F1D7SP2lX0g"></form>
Here is my tag helper:
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
namespace ViewComponents.Infrastructure.TagHelpers {
[HtmlTargetElement("formgroup", Attributes = "for", TagStructure = TagStructure.NormalOrSelfClosing)]
public class FormGroupTagHelper : TagHelper {
/// <inheritdoc />
public override int Order => -2000;
[HtmlAttributeNotBound]
[ViewContext]
public ViewContext ViewContext { get; set; }
protected IHtmlGenerator Generator { get; }
public string For { get; set; }
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) {
if (context == null) {
throw new ArgumentNullException(nameof(context));
}
if (output == null) {
throw new ArgumentNullException(nameof(output));
}
// So that other tag helpers are processed after me...
var childContent = await output.GetChildContentAsync();
// Starting up...
// Replace the tag name, include the form-group bootstrap class.
output.TagName = "div";
output.TagMode = TagMode.StartTagAndEndTag;
output.Attributes.Add("class", "form-group");
PropertyInfo propInfo = ViewContext.ViewData.ModelMetadata.ModelType.GetTypeInfo().GetDeclaredProperty(For);
output.Content.AppendHtml(GenerateLabel(new Dictionary<string, string> { ["asp-for"] = propInfo.Name }));
output.Content.AppendHtml(GenerateInput(new Dictionary<string, string> { ["asp-for"] = propInfo.Name, ["class"] = "form-control" }));
}
public static IHtmlContent GenerateLabel(IReadOnlyDictionary<string, string> attrDict) {
TagBuilder tBuilder = new TagBuilder("label");
foreach (var kvp in attrDict)
tBuilder.Attributes.Add(kvp);
return tBuilder;
}
public static IHtmlContent GenerateInput(IReadOnlyDictionary<string, string> attrDict) {
TagBuilder tBuilder = new TagBuilder("input");
foreach (var kvp in attrDict)
tBuilder.Attributes.Add(kvp);
return tBuilder;
}
}
}
Any help will be appreciated.
Have a nice one!

Tag Helpers aren't processed in a specific order, so you cannot count on one being able to do something based on the output of the other. Each tag helper needs to be designed to work independently of any other, so if you need to render HTML, you'll need to handle that yourself in your tag helper. However, since you're trying to have one tag generate any kind of form field, that's going to be a heavy lift. This is basically beyond the scope of what a tag helper is intended for.

Related

Couldn't Add two numbers and get the Total using Asp.net MVC

I am trying to do a simple calculator program in asp.net MVC. I like to add two number and get the total. But I couldn't do the task. I am a beginner at asp.net MVC. code which I tried so far I attached below.
Model
namespace WebApplication30.Models
{
public class cal
{
public int no1 { get; set; }
public int no2 { get; set; }
public int tot { get; set; }
}
}
Controller
public class CalController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(cal c)
{
c.tot = c.no1 + c.no2;
return View(c);
}
}
** This is a View pass the values from the controller**
#model WebApplication30.Models.cal
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<div class="row">
#using (Html.BeginForm("Index", "cal", FormMethod.Post))
{
<div class="form-group">
<label for="no">No 1</label>
<input type="text" class="form-control" id="no1" name="no1">
</div>
<div class="form-group">
<label for="pwd">No 2</label>
<input type="text" class="form-control" id="no2" name="no2">
</div>
<div class="form-group">
<label for="pwd">total</label>
<input type="text" class="form-control" id="tot" name="tot">
</div>
<button type="submit" class="btn btn-default">Submit</button>
}
</div>
enter image description here
You need to bind the input value to the cal Model. Also, need to add cal model when get index action called .
CONTROLLER
using System;
using System.Web.Mvc;
using System.Collections.Generic;
using WebApplication30.Models;
namespace WebApplication30
{
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
return View(new cal());
}
[HttpPost]
public ActionResult Index(cal c)
{
c.tot = c.no1 + c.no2;
return View(c);
}
}
}
VIEW
#model WebApplication30.Models.cal
#{
ViewBag.Title = "Index";
}
<body>
<h2>Index</h2>
<div class="row">
#using (Html.BeginForm("/Index", "Home", FormMethod.Post))
{
<div class="form-group">
<label for="no">No 1</label>
<input type="text" class="form-control" id="no1" name="no1" value="#Model.no1">
</div>
<div class="form-group">
<label for="pwd">No 2</label>
<input type="text" class="form-control" id="no2" name="no2" value="#Model.no2">
</div>
<div class="form-group">
<label for="pwd">total</label>
<input type="text" class="form-control" id="tot" name="tot" value="#Model.tot">
</div>
<button type="submit" class="btn btn-default">Submit</button>
}
</div>
</body>
NOTE: add / before Index while submitting the form it creates sometimes a problem.
DEMO

How to pass modified model back to Controller in ASP.NET Core

I have the following model:
public class Card
{
[DataType(DataType.Date)]
[BindProperty]
public DateTime Day { get; set; }
[BindProperty]
public string Field { get; set; }
}
The following Controller:
// GET: Card
public async Task<IActionResult> Index(DateTime? day)
{
return View(model);
}
public async Task<IActionResult> Refresh(DateTime? Day, string Field)
{
return RedirectToAction("Index", Day);
}
The following View:
#model Card
<h1>Cards</h1>
<div class="text-center">
<label asp-for="Day" class="control-label"></label>
<input asp-for="Day" class="form-control" />
</div>
<div class="text-center">
<label asp-for="Field" class="control-label"></label>
<select asp-for="Field" class="form-control" asp-items="ViewBag.Fields"></select>
</div>
<form asp-action="Refresh">
#Html.HiddenFor(x => x.Day)
#Html.HiddenFor(y => y.Field)
<input type="submit" value="Refresh" class="btn btn-default" />
</form>
No matter what I change, I always get the initial Day value back and null as the Field, like the Model has never been changed…
So how can I post back the modified model to my controller?
your form is submitting the values from the hidden fields which are rendered on the page when the view first loads and then they are never modified (which is why you are seeing the default initialization for Day and for Field). Your editable fields are outside of the form and are what you're editing but they never get submitted to the server. I think the main takeaway here for you is that forms only know about inputs that exist inside of them (unless you write some javascript magic to handle this but there is no reason to do so in this case)
You need to remove the hidden fields and put your editable fields inside the form as follows:
#model Card
<h1>Cards</h1>
<form method="post" asp-action="Refresh">
<div class="text-center">
<label asp-for="Day" class="control-label"></label>
<input asp-for="Day" class="form-control" />
</div>
<div class="text-center">
<label asp-for="Field" class="control-label"></label>
<select asp-for="Field" class="form-control" asp-items="ViewBag.Fields"></select>
</div>
<input type="submit" value="Refresh" class="btn btn-default" />
</form>
you can also change your controller action to:
[HttpPost]
public async Task<IActionResult> Refresh(Card card)
{
return RedirectToAction("Index", card.Day);
}

How to configure Checkboxes Materialize Css in Web application Asp.Net MVC

How do I configure Checkboxes in Asp.Net MVC Razor.
Since in the documentation we have the following configuration Materialize for checkboxes :
<p>
   <label>
     <input type = "checkbox" />
     <span> Network </span>
   </label>
</p>
And in Razor I could not perform this configuration.
<div class = "input-field col s12">
        #Html.EditorFor (model => model.AnnualDestaque)
        #Html.LabelFor (model => model.AnnualDestaque)
        #Html.ValidationMessageFor (model => model.AnnualDestaque, "", new {#class = "text-danger"})
</div>
Please include model Name in your cshtml page
#model WebApplication3.Models.Test
#{
ViewBag.Title = "Home Page";
}
#using (Html.BeginForm("Save", "Home", FormMethod.Post))
{
<div class="row">
<div class="col-md-4">
#Html.TextBoxFor(m => m.EmployeeName)
#Html.CheckBoxFor(m=>m.IsSelected)
</div>
<input type="submit" value="Save" />
</div>
}
Model
public class Test
{
public string EmployeeName { get; set; }
public bool IsSelected { get; set; }
}
If you just want to have a checkbox binded with your model like that :
public class Network
{
public bool Selected { get; set; }
public string Name { get; set; }
}
Just use :
#Html.CheckBoxFor(m=>m.Selected)
#Html.LabelFor(m=>m.Name)
I was able to solve it, and I am passing the answer.
I used Html Helper Documentation Html Helper MVC
It worked perfectly without mistakes the way it's meant to be.
<div class="input-field col s12">
<label>
<input data-val="true"
data-val-required="The Advertisement field is required."
id="Advertisement"/**** m => m.Advertisement ****/
name="Advertisement"/**** m => m.Advertisement ****/
type="checkbox"
value="true" />
<span>Anuncio Destaque</span>
<input name="Advertisement" type="hidden" value="false" />
</label>
</div>
You can make it work doing this:
<label>
<input type="checkbox" name="FIELDNAME" id="FIELDNAME" value="true" class="filled-in" #(Model.FIELDNAME? "checked" : "") />
<span>#Html.DisplayNameFor(model => model.FIELDNAME)</span>
</label>
<input name="FIELDNAME" type="hidden" value="false" />

HttpPostedFileBase is Not Being Populated

I have a model (simplified, removing extraneous properties):
public class SubmitModel
{
[Required]
[DataType("FileUpload")]
[Display(Name = "Formatted Data File")]
public HttpPostedFileBase FormattedDataFile { get; set; }
}
A controller:
[HttpPost]
public ActionResult Submit(SubmitModel model)
{
if (this.ModelState.IsValid)
{
//...
}
return this.View(model);
}
A FileUpload view:
#{
IDictionary<string, object> htmlAttributes = Html.GetUnobtrusiveValidationAttributes(string.Empty);
}
<input type="file" id="#this.ViewData.TemplateInfo.GetFullHtmlFieldId(string.Empty)" name="#this.ViewData.TemplateInfo.GetFullHtmlFieldName(string.Empty)" #(new MvcHtmlString(htmlAttributes.ToHtmlAttributesString())) />
#Html.ValidationMessage(string.Empty)
And a simple view:
#model SubmitModel
#using (Html.BeginForm())
{
<div class="Form">
#Html.EditorForModel()
<div class="Footer">
<button class="Button" data-options='{ "icons": { "primary": "ui-icon-disk" } }'>Submit</button>
</div>
</div>
}
Which renders to this HTML:
<form action="/Data/Submit" method="post">
<div class="Form">
<div class="Item">
<div class="Label Required">Formatted Data File:</div>
<div class="Input">
<input type="file" id="FormattedDataFile" name="FormattedDataFile" data-val-required="The Formatted Data File field is required." data-val="true" />
<span class="field-validation-error" data-valmsg-for="FormattedDataFile" data-valmsg-replace="true">The value 'Test.xlsx' is invalid.</span>
</div>
</div>
<div class="Footer">
<button class="Button" data-options='{ "icons": { "primary": "ui-icon-disk" } }'>Submit</button>
</div>
</div>
</form>
Upon clicking Submit, I'm brought to the proper controller/action and my model's FormattedDataFile property is null. The ModelState is invalid, saying that "The Formatted Data File field is required." This same code worked fine in some MVC-3 projects I've done - is there anything different regarding this in MVC-4?
i think you are missing enctype="multipart/form-data" in the form
http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2

modelbinding on ASP.NET MVC Razor engine failing

The model binding just isn't working for me - I always get NULL coming through to the controller! Any thoughts people?
Rob
Here is my action signature:
public ActionResult SearchForUser(SearchForUserModel m)
Here is my Razor header model declaration:
#model WebOne.Models.StatusIndexModel
StatusIndexModel is a composite model containing SearchForUserModel:
public class SearchForUserModel
{
[Required(ErrorMessage = "Search information required")]
[DisplayName("Contact Search")]
public string Search { get; set; }
}
Here is my Razor:
#using (Html.BeginForm("SearchForUser", "Status"))
{
<div>
<div class="editor-field">
#Html.TextBoxFor(m => m.searchForUserModel.Search)
<input type="submit" class="formbutton_small" value="Find" />
<br />
#Html.ValidationMessageFor(m => m.searchForUserModel.Search)
</div>
</div>
}
Here is the generated HTML:
<form action="/Status/SearchForUser" method="post">
<div>
<div class="editor-field">
<input data-val="true" data-val-required="Search information required" id="searchForUserModel_Search" name="searchForUserModel.Search" type="text" value="" />
<input type="submit" class="formbutton_small" value="Find" />
<br />
<span class="field-validation-valid" data-valmsg-for="searchForUserModel.Search" data-valmsg-replace="true"></span>
</div>
</div>
</form>
You need to specify the prefix:
public ActionResult SearchForUser(
[Bind(Prefix="searchForUserModel")]SearchForUserModel m
)

Resources