Passing parameter from view to controller when button is clicked - asp.net-mvc-3

I have created my own extension as:
public static MvcHtmlString hSearch(this HtmlHelper helper, string labelName, string labelCaption, string textName, string textValue, string tableName, string buttonId,
string actionName, string controllerName, object routeValues, object htmlAttributes)
{
var textbuilder = new TagBuilder("input");
textbuilder.MergeAttribute("id", textName);
textbuilder.MergeAttribute("name", textName);
textbuilder.MergeAttribute("value", textValue);
textbuilder.MergeAttributes(new RouteValueDictionary(htmlAttributes));
ModelMetadata metadata = ModelMetadata.FromStringExpression(labelName, helper.ViewData);
String innerText = labelCaption ?? (metadata.DisplayName ?? (metadata.PropertyName ?? labelName.Split('.').Last()));
if (String.IsNullOrEmpty(innerText))
{
return MvcHtmlString.Empty;
}
TagBuilder labelbuilder = new TagBuilder("label");
labelbuilder.Attributes.Add("for", TagBuilder.CreateSanitizedId(helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(labelName)));
labelbuilder.MergeAttributes(new RouteValueDictionary(htmlAttributes));
labelbuilder.SetInnerText(innerText);
//return new MvcHtmlString(textbuilder.ToString());
var buttonBuilder = new TagBuilder("button");
buttonBuilder.MergeAttribute("id", buttonId);
buttonBuilder.SetInnerText(buttonId);
var formBuilder = new TagBuilder("form");
var urlHelper = new UrlHelper(helper.ViewContext.RequestContext);
formBuilder.Attributes.Add("action", urlHelper.Action(actionName, controllerName, routeValues));
formBuilder.Attributes.Add("method", "Post");
formBuilder.MergeAttributes(new RouteValueDictionary(htmlAttributes));
formBuilder.InnerHtml = labelbuilder.ToString() + textbuilder.ToString() + buttonBuilder.ToString();
return new MvcHtmlString(formBuilder.ToString());
}
I used the extensions in view as:
#Html.hSearch("lblSrch", "Company", "companyName", (string)TempData["cName"], "CHComp", "Search", "Fetch", "Home", null, null)
Now I want to pass tableName when I click the button to the controller.. my controller looks like this:
public ActionResult Fetch(string search, string tablename)
{
var c = cbo.fetchData(search, tablename);
return PartialView(c.ToList());
}
Waiting for reply.. Thanks..

You haven't given us the code for your helper, but at a guess it writes out a label, a text field (textName), and a button. If this is the case, it will post / get companyName=someValue via HTTP back to your controller.
You would typically need to add a FormCollection to your controller if the fields are dynamically sent from the view. Alternatively, why not keep the name of the text search input static, e.g. name="search", which will bind to your controller's parameter of the same name.
Edit
You can pass tableName back to the controller in a hidden field (<input type='hidden' name='tableName' value='{tableNameGoesHere}')
But as per above, your search string will have different names- the model binder isn't going to recognise it as string search.

Related

Regarding writing custom html helper for hidden field with few good feature ASP.Net MVC

this way i can write custom helper for hidden field
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Linq.Expressions;
namespace CustomHtmlHelpers.CustomHelpers
{
public static class CustomHiddenHelperModelBinding
{
//This overload accepts single expression as parameter.
public static MvcHtmlString Custom_HiddenFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression)
{
return Custom_HiddenFor(helper, expression, null);
}
//This overload accepts expression and htmlAttributes object as parameter.
public static MvcHtmlString Custom_HiddenFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression, object htmlAttributes)
{
//Fetching the metadata related to expression. This includes name of the property, model value of the property as well.
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
//Fetching the property name.
string propertyName = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
//Creating a textarea tag using TagBuilder class.
TagBuilder hidden = new TagBuilder("input");
//Setting the type attribute to hidden to render hidden input field.
hidden.Attributes.Add("type", "hidden");
//Setting the name and id attribute.
hidden.Attributes.Add("name", propertyName);
hidden.Attributes.Add("id", propertyName);
//Setting the value attribute of textbox with model value if present.
if (metadata.Model != null)
{
hidden.Attributes.Add("value", metadata.Model.ToString());
}
//merging any htmlAttributes passed.
hidden.MergeAttributes(new RouteValueDictionary(htmlAttributes));
return MvcHtmlString.Create(hidden.ToString(TagRenderMode.Normal));
}
}
}
later we can access it like
#Html.Custom_HiddenFor(Model => Model.hidden)
#Html.Custom_HiddenFor(Model => Model.hidden, new { #class = "hiddenClass" })
my objective to rewrite my own html helper for hidden field is to render value at client side as encrypted text and as well as tamper proof.
if anyone tamper data then i want to check at server side and if tamper then i will show friendly error message to user.
this is another sampe code for encryption with machine key but i am not sure the that code works fine in partial trust environment or not?
here is the sample code
string Protect(byte[] data)
{
if (data == null || data.Length == 0) return null;
return MachineKey.Encode(data, MachineKeyProtection.All);
}
byte[] Unprotect(string value)
{
if (String.IsNullOrWhiteSpace(value)) return null;
return MachineKey.Decode(value, MachineKeyProtection.All);
}
here’s the 4.5 usage (it supports a slightly more sophisticated usage)
-------------------------------------------------------------------------
const string MachineKeyPurpose = "MyApp:Username:{0}";
const string Anonymous = "<anonymous>";
string GetMachineKeyPurpose(IPrincipal user)
{
return String.Format(MachineKeyPurpose,
user.Identity.IsAuthenticated ? user.Identity.Name : Anonymous);
}
string Protect(byte[] data)
{
if (data == null || data.Length == 0) return null;
var purpose = GetMachineKeyPurpose(Thread.CurrentPrincipal);
var value = MachineKey.Protect(data, purpose);
return Convert.ToBase64String(value);
}
byte[] Unprotect(string value)
{
if (String.IsNullOrWhiteSpace(value)) return null;
var purpose = GetMachineKeyPurpose(Thread.CurrentPrincipal);
var bytes = Convert.FromBase64String(value);
return MachineKey.Unprotect(bytes, purpose);
}
another way to encrypt
To generate a random string, use the RNGCryptoServiceProvider.
public string GenerateSalt(int length)
{
var rng = new RNGCryptoServiceProvider();
var buffer = new byte[length];
rng.GetBytes(buffer);
return Convert.ToBase64String(buffer);
}
Now we can generate a hashed password using the function below
public virtual string CreatePasswordHash(string password, string saltkey, string passwordFormat = "SHA1")
{
if (String.IsNullOrEmpty(passwordFormat))
passwordFormat = "SHA1";
string saltAndPassword = String.Concat(password, saltkey);
string hashedPassword =
FormsAuthentication.HashPasswordForStoringInConfigFile(
saltAndPassword, passwordFormat);
return hashedPassword;
}
so guide me how can i rewrite my own custom html helper which will encrypt data in most secure way and later which can be check to make sure the
value is tamper or not at client side.
i have another requirement to easily decrypt the value when form will post and action method will be called.
i want to decrypt the encrypted value using a attribute on action method. i want to fire a function will fire before action method and decrypt
value before data de-serialize to model or to action method argument.
is it possible ?
i want that my action method would look like
[HttpPost]
[Decrypt]
public ActionResult Save(string personname, string email)
{
return View();
}
or
[HttpPost]
[Decrypt]
public ActionResult Save(Person oPerson)
{
return View();
}
i want that my [Decrypt] attribute will fire a method call decypt and pass all value to decypt() function just before action method Save would invoke.
if decypt() function found any encrypted value then it will decrypt and de-serialize decypted value to model or to action method argument.
so i have three requirements
1) i want to write a custom html helper which will render hidden field with encrypted value.
2) when data post to server there i can detect that value has been tamper or not.
if tamper then a error page with proper message will be shown to user.
3)
action method will have custom attribute which will invoke a function before action method execution.
that function will decrypt value if any encrypted value found. after decryption the decrypted value will be properly De-serialize to model
or other action method argument properly.
i am new in MVC. so guide me how should i move forward with sample code. thanks
What about #Html.AntiForgeryToken()?
"Generates a hidden form field (anti-forgery token) that is validated when the form is submitted." Developer Network
Example:
1. In your markup add #Html.AntiForgeryToken() to the form.
2. Add [ValidateAntiForgeryToken] attribute your controller action.
Developer Network

ASP.NET MVC3 Dropdownlist never selects value in Edit view

I have a function that fills creates dropdownlist in ASP.NET MVC.
public static MvcHtmlString countryDropDown(this HtmlHelper helper, string name, string optionLabel, object selectedValue)
{
XmlDocument doc = new XmlDocument();
doc.Load(HttpContext.Current.Server.MapPath("~/App_Data/countries.xml"));
StringBuilder b = new StringBuilder();
b.Append(string.Format("<select name=\"{0}\" id=\"{0}\">", name));
if (!string.IsNullOrEmpty(optionLabel))
b.Append(string.Format("<option value=\"\">{0}</option>", optionLabel));
foreach (XmlNode node in doc.SelectNodes("//country"))
{
string selected = string.Empty;
if (node.Attributes["name"].Value == selectedValue as string)
{
selected = "selected=\"selected\"";
}
b.Append(string.Format("<option value=\"{0}\" {2}>{1}</option>", node.Attributes["name"].Value, node.Attributes["name"].Value, selected));
}
b.Append("</select>");
return MvcHtmlString.Create( b.ToString());
}
I use this function in Create and Edit views as:
#Html.countryDropDown("Country"," ", ViewData["Country"])
It shows list of countries perfectly but the problem is that I never selects the saved value in Edit page.
How the code can be modified so that it can select the value in edit page.
Solved using
#Html.countryDropDown("Country"," ", ViewData.Eval("Country"))
instead of
#Html.countryDropDown("Country"," ", ViewData["Country"]

How do I call the placeholder watermark in a class?

I do this in an EditorTemplate View:
#Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue,
new { placeholder = ViewData.ModelMetadata.Watermark })
I now want to do something in a class file in which I am using TagBuilder and MergeAttribute. I use code, for example, to display an image for "required", and tried to modify it to do the same thing with placeholder. But I can't do ViewData. So this isn't working:
public static MvcHtmlString PlaceholderFor<T, TValue>
(this HtmlHelper<T> html, Expression<Func<T, TValue>> expression)
{
var placeholder = new ModelMetadata([CAN'T FIGURE OUT IN HERE]);
placeholder.MergeAttribute([ALSO CAN'T FIGURE OUT IN HERE]);
return MvcHtmlString.Create(placeholder.ToString());
}
I don't even know if the above would be appropriate so I could do something like this:
input.MergeAttribute += html.PlaceholderFor(expression);
or whether that is also the incorrect approach.
For context I am building an "input" using:
input.InnerHtml += html.EditorFor(expression);
Can any one provide a code example that would work in this context? Sorry if I am not explaining it correctly.
Your question is not very clear about what you are trying exactly to achieve. But if you want to get the contents generated by the TextBox helper inside another custom helper you could simply call this TextBox helper:
public static IHtmlString MyHelperFor<T, TValue>(
this HtmlHelper<T> html,
Expression<Func<T, TValue>> expression,
object htmlAttributes
)
{
var div = new TagBuilder("div");
var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
var attributes = new RouteValueDictionary(htmlAttributes);
attributes["placeholder"] = metadata.Watermark;
div.InnerHtml = html.TextBoxFor(expression, attributes).ToHtmlString();
return new HtmlString(div.ToString());
}
and then:
#Html.MyHelperFor(x => x.Test, new { #class = "foo" })

T4MVC creates incorrect URL for ActionLink when using Areas

In my project I'm trying to create an URL in a view inside an area.
The code is this:
#Html.ActionLink("Cancel", MVC.MyArea.MyController.Index(), new { #class = "btn" })
When I view the result in the browser, the generated code is the following:
<a class="btn" href="/MyArea/MyController/MyCurrentAction?Count=3&Keys=System.Collections.Generic.Dictionary%602%2BKeyCollection%5BSystem.String%2CSystem.Object%5D&Values=System.Collections.Generic.Dictionary%602%2BValueCollection%5BSystem.String%2CSystem.Object%5D">Cancel</a>
I went looking for the code in T4MVC and found that this was the method generating the code above (inside the T4Extensions class):
public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, ActionResult result, object htmlAttributes, string protocol = null, string hostName = null, string fragment = null) {
return htmlHelper.RouteLink(linkText, null, protocol, hostName, fragment, result.GetRouteValueDictionary(), htmlAttributes);
}
Apparently, the RouteLink method is not being able to use the result.GetRouteValueDictionary() as it should.
So, I checked the ASP.NET MVC source code and tried to replicate the same functionality. I've changed my T4MVC to this:
public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, ActionResult result, object htmlAttributes, string protocol = null, string hostName = null, string fragment = null) {
var t4mvcResult = result.GetT4MVCResult();
var actionName = t4mvcResult.Action;
var controllerName = t4mvcResult.Controller;
var routeValues = new RouteValueDictionary(t4mvcResult.RouteValueDictionary);
var htmlAttribs = new RouteValueDictionary(htmlAttributes);
return new MvcHtmlString(HtmlHelper.GenerateLink(htmlHelper.ViewContext.RequestContext, htmlHelper.RouteCollection, linkText, null /* routeName */, actionName, controllerName, protocol, hostName, fragment, routeValues, htmlAttribs));
}
Now it's working (which is great, of course), at least for the tests I've made, but I'm afraid that I'm doing something wrong in the first place and that this path can lead me into some problems ahead.
Darn, this looks like a regression in 2.6.69. Can you try 2.6.68 to verify that indeed it worked before? I have hidden 2.6.69 for now so others don't get it automatically in new projects (and when updating).
This was the bug that triggered the bad fix: http://mvccontrib.codeplex.com/workitem/7191
Also, could you try the exact fix mentioned in the last comment in that bug? Change the method to:
public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, ActionResult result, object htmlAttributes, string protocol = null, string hostName = null, string fragment = null)
{
return htmlHelper.RouteLink(linkText, null, protocol, hostName, fragment, result.GetRouteValueDictionary(), HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
Sorry about the break!

How can I add a hash fragment to T4MVC route dictionary ActionResult?

I have an extension method that returns an ActionResult (simplified for demonstration purposes):
public static ActionResult GetTestActionResult(this HtmlHelper htmlHelper, int productId)
{
return MVC.Products.Details(productId);
}
I'm using this in an Html.ActionLink:
#Html.ActionLink("Product Details", Html.GetTestActionResult(Model.ProductId), new { #class = "button blue" });
I'm using a custom jQuery plugin for tabs, that uses these hash fragments for navigation. I want to add the tab which I want to open, by tagging the hash fragment onto the end of the URL.
Html.ActionLink does have an overload for the Fragment, namely:
public static MvcHtmlString ActionLink(
this HtmlHelper htmlHelper,
string linkText,
string actionName,
string controllerName,
string protocol,
string hostName,
string fragment,
Object routeValues,
Object htmlAttributes
)
However, that is full of nasty magic strings, which T4MVC is designed to remove. Is there anyway to add the fragment to the route dictionary in my static extension method (GetTestActionResult)?
Something like:
return MVC.Products.Details(productId).AddRouteValue(String.Empty, "#tab-similar-products");
I am aware that there are two similar questions and answers on SO, but they don't quite provide me with what I am looking for. I need to wrap the fragment into the ActionResult BEFORE passing it back to the view:
Including hash values in ASP.NET MVC URL routes
Create a T4MVC ActionLink with url fragment
UPDATE:
Using David Ebbo's fix below, I made the following changes. A bit hacky, but it works:
First I altered my internal function that returns an ActionResult so that it would also add the fragment as a route value (not ideal but works):
return MVC.Products.Details(productId).AddRouteValue("tab", "#tab-similar-products");
Then in the view it copies that fragment value out of the route dictionary, then removes that route value for completeness.
// get my ActionResult with the tab fragment tagged on as a route value
var actionResult = Html.GetTestActionResult(item.Key, Model.ClaimId);
// get the tab fragment value
var tabRoute = actionResult.GetRouteValueDictionary().FirstOrDefault(r => r.Key == "tab").Value ?? "none";
// remove the route value, otherwise it will get tagged to the querystring
actionResult.GetRouteValueDictionary().Remove("tab");
// display
#Html.ActionLink("Product Details", Html.GetTestActionResult(Model.ProductId), new { #class = "button blue" }, fragment: tabRoute.ToString());
I'm sure there is a prettier way to return the fragment with the ActionResult, but for the moment this works. Thanks David.
T4MVC needs new overloads to handle this. In T4MVC.tt, try changing:
public static <#=HtmlStringType #> ActionLink(this HtmlHelper htmlHelper, string linkText, ActionResult result, object htmlAttributes) {
return ActionLink(htmlHelper, linkText, result, new RouteValueDictionary(htmlAttributes));
}
public static <#=HtmlStringType #> ActionLink(this HtmlHelper htmlHelper, string linkText, ActionResult result, IDictionary<string, object> htmlAttributes) {
return htmlHelper.RouteLink(linkText, result.GetRouteValueDictionary(), htmlAttributes);
}
to
public static <#=HtmlStringType #> ActionLink(this HtmlHelper htmlHelper, string linkText, ActionResult result, object htmlAttributes, string protocol = null, string hostName = null, string fragment = null) {
return ActionLink(htmlHelper, linkText, result, new RouteValueDictionary(htmlAttributes), protocol, hostName, fragment);
}
public static <#=HtmlStringType #> ActionLink(this HtmlHelper htmlHelper, string linkText, ActionResult result, IDictionary<string, object> htmlAttributes, string protocol = null, string hostName = null, string fragment = null) {
return htmlHelper.RouteLink(linkText, null, protocol, hostName, fragment, result.GetRouteValueDictionary(), htmlAttributes);
}
you'll then be able to write something like:
#Html.ActionLink("Product Details", Html.GetTestActionResult(Model.ProductId), new { #class = "button blue" }, fragment: "#tab-similar-products")
Let me know if that works, and I'll try to get it added to the main template.

Resources