MVC 6 Change where a view block renders - javascript

My goal is to create an analog to the razor #section Scripts {...} syntax that will work equally well in Views and ViewComponents.
I can do this via helper methods if I convert the JavaScript to a windows string. However, this destroys intellisense, puts you into character escaping hell and doesn't allow you to de-dup and order the scripts just prior to rendering.
I'd like to make this work in a way that allows the Visual Studio Editor to edit the JavaScript as JavaScript. It seems like I should be able to do something like this:
<div class="StatsDisplay">
label id="#labelId">#Model.DisplayFormat</label>
</div>
#using (Html.BeginNamedScript($"StatDisplay{Model.UniqueId}"))
{
<script>
$.ajax({
url: "#Model.ResultUrl",
method:"POST"
})
.done(function (value) {
var statText = "#Model.DisplayFormat".replace(/\{\s * 0\s *\}/, value);
$("##labelId").text(statText);
});
</script>
}
HtmlHelperExtension:
public static NamedScript BeginNamedScript(this IHtmlHelper htmlHelper, string name, params string[] dependancies)
{
return new NamedScript(htmlHelper.ViewContext, name, htmlHelper, dependancies);
}
And class NamedScript:
using System;
using System.Diagnostics;
using System.IO;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.ViewFeatures;
namespace WebUIB8.Helpers
{
public class NamedScript : IDisposable
{
private bool _disposed;
private readonly FormContext _originalFormContext;
private readonly ViewContext _viewContext;
private readonly TextWriter _writer;
private readonly string _name;
private readonly HtmlHelper _helper;
private readonly string[] _dependsOn;
public NamedScript(ViewContext viewContext, string name, params string[] dependsOn):this(viewContext, name, null, dependsOn)
{
}
internal NamedScript(ViewContext viewContext, string name, IHtmlHelper helper, params string[] dependsOn)
{
if (viewContext == null)
{
throw new ArgumentNullException(nameof(viewContext));
}
_name = name;
_dependsOn = dependsOn;
_helper = helper as HtmlHelper;
_viewContext = viewContext;
_writer = viewContext.Writer;
Debug.WriteLine("Beginning:\r\n" + _viewContext);
_originalFormContext = viewContext.FormContext;
viewContext.FormContext = new FormContext();
Begin();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Begin()
{
//No beginning action needed
}
private void End()
{
Debug.WriteLine("Ending:\r\n" + _writer);
//NOTE: This chunk doesn't work
//This is supposed to render the script to a string and
// pass it to the helper method that accumulates them, orders
// them, dedups them, and renders them at the proper location
// in the _Layout file so JavaScript loads last, and in dependancy order.
_helper?.AddJavaScript(_name, _writer.ToString(), _dependsOn);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
_disposed = true;
End();
if (_viewContext != null)
//NOTE: This chunk doesn't work either.
//This is supposed to prevent the code from rendering here.
_viewContext.FormContext = _originalFormContext;
}
}
public void EndForm()
{
Dispose(true);
}
}
}
I've tried the below to render the script to string, but it throws an exception inside the .RenderAsync call and aborts the page with a 503.2 error:
private async Task<string> RenderView(ViewContext viewContext)
{
using (var sw = new StringWriter())
{
var newViewContext = new ViewContext(viewContext, viewContext.View, viewContext.ViewData, sw);
var razorView = newViewContext.View as RazorView;
razorView.RenderAsync(newViewContext).Wait();
sw.Flush();
return sw.ToString();
}
}
Am I missing a simpler solution? Is there an easier way to render the result of Razor markup and pass it into an html helper method?
How can I render the ViewContext of inside the #using block into text?
How can I prevent that ViewContext from rendering with the rest of it's view? (So that I can render it later on the page)

You can implement this behavior using tag helpers.
Let's say you create a tag helper InlineScriptConcatenatorTagHelper targetting the <script> tag, where you basically remove its contents from the output but keep them in memory for later use:
[HtmlTargetElement("script", Attributes = "inline-bundle-add")]
public class InlineScriptConcatenatorTagHelper: TagHelper
{
private IHttpContextAccessor httpContextAccessor;
public InlineScriptConcatenatorTagHelper(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
[HtmlAttributeName("inline-bundle-add")]
public string BundleName { get; set; }
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
//Get the script contents
var contents = await context.GetChildContentAsync();
var scriptContent = contents.GetContent();
//Save them into the http Context
if (httpContextAccessor.HttpContext.Items.ContainsKey(BundleName))
{
var scripts = httpContextAccessor.HttpContext.Items[BundleName] as ICollection<string>;
scripts.Add(scriptContent);
}
else
{
httpContextAccessor.HttpContext.Items[BundleName] = new List<string> { scriptContent };
}
//suppress any output
output.SuppressOutput();
}
}
You can then create a similar tag helper InlineScriptTagHelper where you will basically concatenate and render all the contents you collected from the previous helper:
[HtmlTargetElement("script", Attributes = "inline-bundle-render")]
public class InlineScriptTagHelper : TagHelper
{
private IHttpContextAccessor httpContextAccessor;
public InlineScriptTagHelper(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
[HtmlAttributeName("inline-bundle-render")]
public string BundleName { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
//if no scripts were added, suppress the contents
if (!httpContextAccessor.HttpContext.Items.ContainsKey(BundleName))
{
output.SuppressOutput();
return;
}
//Otherwise get all the scripts for the bundle
var scripts = httpContextAccessor.HttpContext.Items[BundleName] as ICollection<string>;
//Concatenate all of them as set them as the contents of this tag
output.Content.SetContentEncoded(String.Join("", scripts));
}
}
With this in place, you could add as many script blocks in your views and assign them an inline bundle name:
<script inline-bundle-add="myInlineBundle">
var title = '#ViewData["Title"]';
var greet = function (message) {
console.log(message);
}
</script>
...
<script inline-bundle-add="myInlineBundle">
greet(title);
</script>
Then add a single script element in your _Layout.cshtml that will render the concatenated output of all the inline scripts with the same bundle name:
...
<script inline-bundle-render="myInlineBundle"></script>
</body>
The rendered output will contain a single script element concatenating all the scripts you included in the inline bundle:
...
<script>
var title = 'Home Page';
var greet = function (message) {
console.log(message);
}
greet(title);
</script>
</body>
DonĀ“t forget to register the tag helpers in your assembly by adding a #addTagHelper directive to the _ViewImports.cshtml file
EDIT
Check out the github project created by #SvdSinner. It has taken the approach described here and created a tag helper that supports deduplication and dependency ordering. (With the aim of supporting minification and provide a nuget package)

Related

Why is axios response in camel case when sending a request in asp.net core [duplicate]

I'm running through a WintellectNOW course on ASP.NET Core/Web API/Angular 2. I have the API portion implemented, but for whatever reason, the JSON that is being returned has the variable names being lowercased.
The returned JSON is formatted like...
[
{"id":1,"name":"Bowler","color":"black","count":1},
{"id":2,"name":"Fedora","color":"red","count":1},
{"id":3,"name":"Baseball Cap","color":"blue","count":3}
]
I'm expecting...
[
{"Id":1,"Name":"Bowler","Color":"black","Count":1},
{"Id":2,"Name":"Fedora","Color":"red","Count":1},
{"Id":3,"Name":"Baseball Cap","Color":"blue","Count":3}
]
Based on the C# model of...
namespace HatCollection.Models
{
public class Hat
{
public int Id { get; set; }
public string Name { get; set; }
public string Color { get; set; }
public int Count { get; set; }
}
}
I even went as far as decorating the properties with [DataMember(Name = "Id")] just to make sure and it still didn't matter.
On the off chance, it's relevant the Action and instance variable in the controller...
private static readonly List<Hat> MyHats = new List<Hat>
{
new Hat {Id = 1, Name = "Bowler", Color = "black", Count = 1 },
new Hat {Id = 2, Name = "Fedora", Color = "red", Count = 1 },
new Hat {Id = 3, Name = "Baseball Cap", Color = "blue", Count = 3 }
};
[HttpGet]
public IEnumerable<Hat> Get()
{
return MyHats;
}
How do I turn off the camelCase functionality, so that ASP.NET Core returns the property names without changing them?
In Asp.Net Core 3.0 some things have changed. For camelCase do nothing that is out of the box. For PascalCase or another set style use.
services.AddMvc(setupAction=> {
setupAction.EnableEndpointRouting = false;
}).AddJsonOptions(jsonOptions =>
{
jsonOptions.JsonSerializerOptions.PropertyNamingPolicy = null;
})
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
In Startup.cs ConfigureServices section
For those who needs a solution about a PascalCase within Api Project that has not the Mvc services you should add this after AddControllers services
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers().AddJsonOptions(jsonOptions =>
{
jsonOptions.JsonSerializerOptions.PropertyNamingPolicy = null;
} ;
}
For Asp.Net Core 3.1 using the NewtonSoft.Json
services.AddControllers()
.AddNewtonsoftJson(options =>
{
options.UseMemberCasing();
});
In ASP.NET Core <3.0, JSON properties are camelCased by default (per this announcement).
You can disable this by replacing
services.AddMvc();
with
services
.AddMvc()
.AddJsonOptions(opt => opt.SerializerSettings.ContractResolver
= new DefaultContractResolver());
in your Startup.cs file. You'll have to add using Newtonsoft.Json.Serialization; to the top of the file.
With the DefaultContractResolver in place, the property names will be represented verbatim in the JSON output. No need for DataMember attributes.
Here is the answer for .net 5 :
https://learn.microsoft.com/en-us/aspnet/core/web-api/advanced/formatting?view=aspnetcore-5.0
Configure System.Text.Json based formatters Features for the
System.Text.Json based formatters can be configured using
Microsoft.AspNetCore.Mvc.JsonOptions.JsonSerializerOptions.
The
default formatting is camelCase. The following highlighted code sets
PascalCase formatting:
C#
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddJsonOptions(options =>
options.JsonSerializerOptions.PropertyNamingPolicy = null);
}
Another solution in Asp.Net.Core 2.2 as following:
services.AddMvc()
.AddJsonOptions(jsonOptions => jsonOptions.UseMemberCasing())
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
In ASP.Net Core you can use two way:
First way: UseMemberCasing()
In StartUp.cs :
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews().AddNewtonsoftJson(opt =>
{
opt.UseMemberCasing(); // <-- add this
});
}
Second way: ContractResolver
In StartUp.cs :
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews().AddNewtonsoftJson(opt =>
{
opt.SerializerSettings.ContractResolver = new DefaultContractResolver(); // <-- add this
});
}
depends on your project maybe you used AddMvc() or AddControllers() insted of AddControllersWithViews().
If AddNewtonsoftJson not found, you should install Nuget pacage : Microsoft.AspNetCore.Mvc.NewtonsoftJson (link).
You have to change the DefaultContractResolver which uses camelCase by default. Just set the NamingStatergy as null.
This should be done in the StartUp.ConfirgureService as follows.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddMvcOptions(o => o.OutputFormatters.Add(
new XmlDataContractSerializerOutputFormatter()));
.AddJsonOptions(o => {
if (o.SerializerSettings.ContractResolver != null)
{
var castedResolver = o.SerializerSettings.ContractResolver
as DefaultContractResolver;
castedResolver.NamingStrategy = null;
}
});
}
Option 2
Use JSonProperty as follows.
public class Hat
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("color")]
public string Color { get; set; }
[JsonProperty("count")]
public int Count { get; set; }
}
I am using the following solution because
a) I prefer using the .Net Core built in System.Text.Json serializer and
b) I do not want to rely on the not documented internal behaviour of
jsonOptions.JsonSerializerOptions.PropertyNamingPolicy = null;.
.
services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = new MyTransparentJsonNamingPolicy();
});
where:
public class MyTransparentJsonNamingPolicy : JsonNamingPolicy
{
// You can came up any custom transformation here, so instead just transparently
// pass through the original C# class property name, it is possible to explicit
// convert to PascalCase, etc:
public override string ConvertName(string name)
{
return name;
}
}
In .NET 6 I used:
builder.Services.AddControllersWithViews().AddJsonOptions(opt => opt.JsonSerializerOptions.PropertyNamingPolicy = null);

Call Wicket 6 Code from Javascript and return value

I have managed to call my Wicket 6 Java code from Javascript using option A in this example: https://stackoverflow.com/a/42612027/1047418
However, I have not been able to find examples for returning data from the Java side back to JavaScript (the generated JavaScript callback function does not even include a return statement). How can this be achieved?
Edit: I am not trying to set an attribute in Java and as I've already explained, calling Wicket from JavaScript is not the problem here. I am trying to return a JSON object from Wicket back to the browser as a result of an Ajax request.
Edit2: Following martin-g's examples I cobbled up this working example...
Java
public class MyAjaxBehaviour extends AbstractDefaultAjaxBehavior {
#Override
protected void onComponentTag(ComponentTag tag) {
super.onComponentTag(tag);
tag.put("aprachatcallbackurl", getCallbackUrl());
}
#Override
protected void updateAjaxAttributes(AjaxRequestAttributes attributes) {
super.updateAjaxAttributes(attributes);
attributes.setDataType("json");
attributes.setWicketAjaxResponse(false);
}
#Override
protected void respond(AjaxRequestTarget target) {
getComponent().getRequestCycle().replaceAllRequestHandlers(
new TextRequestHandler("application/json", "UTF-8", "{...JSON GOES HERE...}));
}
}
JavaScript
var mySuccessCallback = function(param1, param2, data, statusText) {
// Data contains the parsed JSON object from MyAjaxBehaviour.respond(...)
...
}
var myFailureCallback = function() {
...
}
Wicket.Ajax.get({
"u": callbackUrl,
"dt": "json",
"wr": false,
"sh": [mySuccessCallback],
"fh": [myFailureCallback]
});
Main problem as that the Wicket 7 Reference incorrectly instructs to use "wr" instead of "dt" in the JavaScript call. :)
I think you can do it in a simpler way!
Wicket Ajax API is just: Wicket.Ajax.ajax({...}). All you need to prepare at the server side is to save the callback url, e.g. by saving it globally in the window object or in HTML element's attributes (data-the-url).
public class CallFromJavascriptBehavior extends AbstractDefaultAjaxBehavior {
#Override
protected void respond(AjaxRequestTarget target) {
final StringValue parameterValue = RequestCycle.get().getRequest().getQueryParameters().getParameterValue("yourName");
System.out.println(String.format("Hello %s", parameterValue.toString()));
// write anything to the WebResponse and then consume it in the JS success handler. See below
}
#Override
public void onComponenntTag(ComponenntTag tag, Component component) {
super.onComponenntTag(tag, component);
tag.put("data-the-url", getCallbackUrl());
}
}
Then in your JS code you can do:
var callbackUrl = jQuery("#theElementId").data("the-url");
Wicket.Ajax.get({"u": callbackUrl, "sh":[successHandler], "fh": [failureHandler] });
Where successHandler and failureHandler are JS functions defined inline (e.g. function(...) {}) or elsewhere.
More documentation you can find at:
https://ci.apache.org/projects/wicket/guide/7.x/single.html#_ajax_request_attributes_and_call_listeners
A blog article with an complete example at http://wicketinaction.com/2012/07/wicket-6-javascript-improvements/
You can just write a Resource and mount it, and get it with your favorite Ajax-approach.
For example:
public class MyResource extends AbstractResource
#Override
protected ResourceResponse newResourceResponse( Attributes attributes )
{
ResourceResponse resourceResponse = new ResourceResponse();
resourceResponse.setContentType( "text/json" );
resourceResponse.setTextEncoding( "utf-8" );
HttpServletRequest request = (HttpServletRequest) attributes.getRequest().getContainerRequest();
try
{
this.json = IOUtils.toString( request.getInputStream() );
}
catch ( IOException e )
{
e.printStackTrace();
}
resourceResponse.setWriteCallback( new WriteCallback()
{
#Override
public void writeData( Attributes attributes ) throws IOException
{
OutputStream outputStream = attributes.getResponse().getOutputStream();
Writer writer = new OutputStreamWriter( outputStream );
writer.write( MyResource.this.json );
writer.close();
}
} );
return resourceResponse;
}
(Copied from my other answer here https://stackoverflow.com/a/17876029/461499)
And see here for mounting it:
https://dzone.com/articles/how-implement-rss-feeds-custom

Access Java Enum fields in javascript

I'm trying to access the facility String of this enum in java script
public enum FacilityEnum {
CAR_VALET("carValet"),
INDOOR("indoorPark"),
DISABLED_ACCESS("disabledAccess"),
EV_CHARGE("evCharge"),
private String facility;
private FacilityEnum(String facility) {
this.facility = facility;
}
public String getFacility() {
return facility;
}
public void setFacility(String facility) {
this.facility = facility;
}
}
This enum is used in a Facility.class
#Entity
public class Facility {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long facilityId;
#Enumerated(EnumType.STRING)
private FacilityEnum service;
#ManyToMany(mappedBy = "facilities")
#JsonBackReference("parks-facilities-services")
private Set<Park> parks;
}
public FacilityEnum getService() {
return service;
}
public void setService(FacilityEnum service) {
this.service = service;
}
which has a ManyToMany relation with Park.class.
The problem comes when i need to use the facility String in javascript
This the javascript interested part, i'm using Spring + Thymleaf
var parcheggi = JSON.parse([[${parks}]]); //my list of Parks
parcheggi.forEach(function (arrayItem) { //it's ok
var parcheggio = arrayItem;
var services = parcheggio.facilities; //it's ok, i get Facility objects
var servicesDiv = '<div>';
services.forEach(function (service){
var s = service; //the single Facility
servicesDiv += '<img src="/images/park_icons/facilities/' + s.service + '.png" />'
});
servicesDiv += '</div>';
//rest of the code...
In this case s.service is the rough Enum (CAR_VALET, INDOOR...) if i try s.service.facility I get undefined.. I need to have carValet, indoor, disabledAccess and so on...
One way to do what you want is to configure object mapper to serialize enums using toString method. You would add the following to the object mapper configuration:
objectMapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
(Note that previous Jackson versions have equivalent to this property but it's different).
Then just add toString to your enum:
#Override
public String toString ()
{
return facility;
}
You are getting undefined because your Enum can't be deserialized in JSON, you have two options here:
Either change the Implementation of your Enum, so it contains only the Stringconstants and it will be correctly mapped by Jackson.
Your code would be:
public enum FacilityEnum {
CAR_VALET,
INDOOR,
DISABLED_ACCESS,
EV_CHARGE;
}
Or you should override the toString() method in your Enum so it can
be deserialized and returned as a String.
Your code would be:
public enum FacilityEnum {
CAR_VALET("carValet"),
INDOOR("indoorPark"),
DISABLED_ACCESS("disabledAccess"),
EV_CHARGE("evCharge"),
private String facility;
private FacilityEnum(String facility) {
this.facility = facility;
}
public String getFacility() {
return facility;
}
public void setFacility(String facility) {
this.facility = facility;
}
#Override
public String toString() {
return facility;
}
}

How can I call a server side c# method via javascript without a WebMethod or Updatepanel?

I would prefer not to use an update panel and using the common WebMethod approach leads me to this error with this code
private string currentHtml() {
StringWriter str_wrt = new StringWriter();
HtmlTextWriter html_wrt = new HtmlTextWriter(str_wrt);
Page.RenderControl(html_wrt);
return str_wrt.ToString();
}
[WebMethod]
public static void EmailPtoRequest() {
string test = currentHtml();
}
Error 8 An object reference is required for the non-static field, method, or property 'PtoRequest.cs.WebForm1.currentHtml()
Clearly the method being static is causing a bunch of headaches.
Is there a standard that I can use for this type of functionality? The goal is to allow the user to send their data to the server without causing a post and refreshing the page.
Note: I DO NOT want to use a webmethod as it is causing an error which does not let me compile.
public partial class WebForm1 : System.Web.UI.Page {
protected void Page_Load(object sender, EventArgs e) {
}
private string currentHtml() {
StringWriter str_wrt = new StringWriter();
HtmlTextWriter html_wrt = new HtmlTextWriter(str_wrt);
Page.RenderControl(html_wrt);
return str_wrt.ToString();
}
[WebMethod]
public static void EmailPtoRequest() {
WebForm1 WebForm1 = new WebForm1();
string test = WebForm1.currentHtml();
}
}
Results in 'test' being an empty string instead of the html of the page.
private static string currentHtml() {
StringWriter str_wrt = new StringWriter();
HtmlTextWriter html_wrt = new HtmlTextWriter(str_wrt);
Page.RenderControl(html_wrt);
return str_wrt.ToString();
}
[WebMethod]
public static void EmailPtoRequest() {
string test = currentHtml();
}
}
Results in the first error again, but in the currentHtml method instead.
Please remember the question is not about the error, but an alternative to webmethod or update panels. Thank you.
3 options:
Make the currentHtml method static,
Instantiate the class that contains currentHtml like this:
new MyClass().currentHtml();
Use an ajax enabled wcf service.

Dynamically creating hidden form fields for various models (MVC 4)

I'm trying to dynamically create hidden fields for a set of properties, but I'm getting a 500 server error when I submit the form. I confirmed the following:
The properties I'm iterating over in the foreach statement are correct.
property.Name is a valid property name for the type retrieved by NewItem.GetType()
Here's what I have:
View
#model PaneViewModel
using (Ajax.BeginForm("AddItem", "Action", new AjaxOptions
{
UpdateTargetId = "tool-wrapper",
HttpMethod = "POST",
}))
{
// Some standard input fields here (these are working properly).
[...]
// Here's what's broken:
#foreach (var property in Model.NewItem.GetType().GetProperties().Where(<criteria here>))
{
#Html.HiddenFor(m => m.NewItem.GetType().GetProperty(property.Name), column.GetValue(Model.NewItem, null))
}
<button type="submit">Add</button>
}
ItemViewModel
public class ItemViewModel
{
public int SomeField { get; set; }
public int AnotherField { get; set; }
}
PaneViewModel
public class PaneViewModel
{
public ItemViewModel NewItem { get; set; }
}
Controller
[HttpPost]
public ActionResult AddItem([Bind(Prefix = "NewItem")] ItemViewModel model)
{
// Stuff here.
}
It's worth noting that the following generates the hidden fields with the correct names and values in the generated HTML, but the values of the hidden field aren't posted to the controller action:
#foreach (var property in Model.NewItem.GetType().GetProperties().Where(<criteria here>))
{
#Html.Hidden(property.Name, column.GetValue(Model.NewItem, null))
}
So it seems the problem is with the m => m.NewItem.GetType().GetProperty(property.Name) component
This type of logic does not belong in a view
Html.HiddenFor() expects an expression (Expression<Func<TModel,
TProperty>>) as the first parameter, but .GetProperty() returns
typeof PropertyInfo
You should not be generating multiple hidden inputs for properties
of your model, but rather use a view model to represent only what
you need to edit (it degrades performance by sending extra data to
the client and then posting it back again unchanged, and anyone
could use FireBug or similar tools to change the values and you
might be none the wiser.
However, if you do want to do this, the you could create a html helper that generates hidden inputs for all properties marked with the [HiddenInput] attribute (or modify this example to pass in some condition that filters the required properties)
public static MvcHtmlString HiddenForModel<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression)
{
StringBuilder html = new StringBuilder();
ModelMetadata metaData = ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
var properties = metaData.Properties.Where(p => p.TemplateHint == "HiddenInput");
foreach(var property in properties)
{
html.Append(helper.Hidden(property.PropertyName));
}
return MvcHtmlString.Create(html.ToString());
}
Note this will also generate the id and data-val-* attributes which are probably unnecessary, so you could minimize the generated html by using
foreach(var property in properties)
{
TagBuilder input = new TagBuilder("input");
input.MergeAttribute("type", "hidden");
input.MergeAttribute("name", property.PropertyName);
input.MergeAttribute("value", string.Format("{0}", property.Model));
html.Append(input.ToString());
}

Categories

Resources