Passing list/model as parameter with #url.action - c#

Is there a way to pass list as parameter for #url.action or is there a better solution for this?
Pseudo code:
This doesn't pass anything, unless I define index for my model or list.
#Url.Action("DeleteConfirmed", "Home", new { selectedids = /*model or list?*/ }, null)
I need to pass list or model to this:
public ActionResult DeleteConfirmed(List<int> selectedids) {
//Code
return View("Index");
}

I join them as a string and then convert that back on the server; this means selectedids would need to be a string on the server, which you would string split on comma and convert each value to long.
#Url.Action("DeleteConfirmed", "Home", new { selectedids = String.Join(",", Model.IDS) })

Related

FormCollection MVC5 not passing decimal to controller

i keep getting errors when trying to post an <input type="number"> from my view to my controller using FormCollection. the database type for expenseBackup is a decimal(8,2). I keep getting "Cannot impliticity convert string to decimal?". Then when i try expenseBackup = Int32.Parse(formValues["expenseBackup"]), i get "Input string was not in correct format". i don't want to have to convert anything in the controller i don't understand why it won't just pass as a decimal through the FormCollection.
Controller
[HttpPost]
public ActionResult Create(FormCollection formValues)
{
var data = new usr_ebillingClientDatabase()
{
client = formValues["client"], //is a string from form
expenseBackup = formValues["expenseBackup"] //is a decimal from form
};
dataContext.table1.InsertOnSubmit(data);
try
{
dataContext.SubmitChanges();
return RedirectToAction("Index");
}
catch (Exception e)
{
return RedirectToAction("Index");
}
}
View
<div class="form-group">
<div class="col-md-10">
#Html.EditorFor(model => model.expenseBackup, new { htmlAttributes = new { #class = "form-control" , , #type="number", #step=".01" } })
</div>
</div>
when you're reading the field from formValues["expenseBackup"] it's being read as a string. Convert it to a decimal using Convert.ToDecimal().
expenseBackup = Convert.ToDecimal(formValues["expenseBackup"] ?? 0m);
FormCollection is a key-value pair collection (NameValueCollection) which returns values as string based from provided key, which is also a string. If you're in doubt which number format applied by numeric input during submit, use combination of decimal.TryParse() and if-condition with string.IsNullOrEmpty() to check null/empty string value:
decimal expense;
if (!string.IsNullOrEmpty(formValues["expenseBackup"])
&& decimal.TryParse(formValues["expenseBackup"], out expense))
{
var data = new usr_ebillingClientDatabase()
{
client = formValues["client"],
expenseBackup = expense
};
// insert to database
}
else
{
// unable to parse numeric value, do something else
}
If you're sure that numeric representation passed in FormCollection uses certain decimal separator other than default, use NumberFormatInfo when parsing with decimal.Parse()/decimal.TryParse():
var numberFormat = new System.Globalization.NumberFormatInfo() { NumberDecimalSeparator = "," };
var data = new usr_ebillingClientDatabase()
{
client = formValues["client"],
expenseBackup = decimal.Parse(formValues["expenseBackup"], numberFormat);
};
However, I recommend using strongly-typed viewmodel over FormCollection because you're using EditorFor, and it will pass property values directly to controller when viewmodel name is included as action parameter.

Access Array declared in C# Controller in Razor

I have a 2D Array declared in my MVC Controller, and I need to access this via Razor so I can loop through each value.
I create a session and set it as the array, but I can't figure out how to access the array through razor.
Controller:
string[,] Things = new string[,] {
{ "thing1", "pie" },
{ "thing1", "cake" },
{ "thing1", "potato" }
};
public void GetThings()
{
Session["Things"] = Things;
}
public ActionResult Index()
{
GetThings();
return View();
}
Razor:
#{
for (int i = 0; i < Session["Things"].GetLength(0); i++)
{
#i
}
}
I get the error "'object' does not contain a definition for Getlength, the only suggested actions are .Equals, .GetHashCode, .GetType, and .ToString.
The above c# in the razor works if I declare the array within the razor, replacing "Session..." with the array variable name.
I can't read any values from the array session to display on the HTML front end, doing #Session["Things"] displays System.String[,] in browser (but then this is the same as if I tried to call the array declared in razor), #Session["Things"][1,1] gives browser error
Cannot apply indexing with [] to an expression of type 'object'
Cast to array:
((string[,])Session["Things"]).GetLength(0)

Unable to pass a native value in ActionLink under Razor while POCO instances work great

First I thought that I'm using the wrong overload again (a very common gotcha in the API - everybody trips over that one). But wouldn't you know? That's not it. I actually had the HTML attributes parameter too and I verified with intellisense that it's the route values I'm entering.
#Html.ActionLink("Poof", "Action", "Home", 10, new { #class = "nav-link" })
Nevertheless, it seem that the receiving method below only sees null and crashes as it can't make an integer out of it.
public ActionResult Record(int count) { ... }
I've tried a few things: changed parameter type to int? and string (the program stops crashing but the value is still null). I've tested to package the passed value as an object (with/without #).
#Html.ActionLink("Poof", "Record", "Home",
new { count = "bamse" },
new { #class = "nav-link" })
I can see that the anchor produced has my value as a query string, so the changes are there. However, I still get null only in the method.
What am I missing?
The weird thing is that the following works fine.
#Html.ActionLink("Poof", "Record", "Home",
new Thing(),
new { #class = "nav-link" })
public ActionResult Record(Thing count) { ... }
Your using the overload of #Html.ActionLink() that expects the 4th parameter to be typeof object. Internally the method builds a RouteValueDictionary by using the .ToString() value of each property in the object.
In your case your 'object' (an int) has no properties, so no route values are generated and the url will be just /Home/Action(and you program crashes because your method expects a non null parameter).
If for example you changed it to
#Html.ActionLink("Poof", "Action", "Home", "10", new { #class = "nav-link" })
i.e. quoting the 4th parameter, the url would now be /Home/Action?length=2 because typeof string has a property length and there a 2 characters in the value.
In order to pass a native value you need to use the format
#Html.ActionLink("Poof", "Action", "Home", new { count = 10 }, new { #class = "nav-link" })
Which will generate /Home/Action?count=10 (or /Home/Action/10 if you create a specific route definition with Home/Action/{count})
Note also that passing a POCO in your case only works correctly because your POCO contains only value type properties. If for example, it also contained a property which was (say) public List<int> Numbers { get; set; } then the url created would include ?Numbers=System.Collections.Generic.List[int] (and binding would fail) so be careful passing complex objects in an action link
Hard to say what might be wrong with your code from the information provided in your question but assuming total defaults (a newly created ASP.NET MVC application in Visual Studio), if you add the following markup in your ~/Views/Home/Index.cshtml:
Html.ActionLink(
"Poof",
"Record",
"Home",
new { count = "bamse" },
new { #class = "nav-link" }
)
and the following action in your HomeController:
public ActionResult Record(string count)
{
return Content(count);
}
upon clicking on the generated anchor, the correct action will be invoked and the correct parameter passed to it.
The generated markup will look like this:
<a class="nav-link" href="/Home/Record?count=bamse">Poof</a>
So I guess that now the question that you should be asking yourself is: how does my setup differs with what Darin has outlined here? Answering this question might hold the key to your problem.
UPDATE:
OK, now you seem to have changed your question. You seem to be trying to pass complex objects to your controller action:
public ActionResult Record(Thing count) { ... }
Of course this doesn't work as you might expect. So make sure that you pass every single property that you want to be available when constructing your anchor:
Html.ActionLink(
"Poof",
"Record",
"Home",
new { ThingProp1 = "prop1", ThingProp2 = "prop2" },
new { #class = "nav-link" }
)
Alternatively, and of course a much better approach to handle this situation is to attribute an unique identifier to your models, so that all its needed in order to retrieve this model from your backend is this identifier:
Html.ActionLink(
"Poof",
"Record",
"Home",
new { id = "123" },
new { #class = "nav-link" }
)
and then in your controller action simply use this identifier to retrieve your Thing:
public ActionResult Record(int id)
{
Thing model = ... fetch the Thing using its identifier
}

Is it possible to pass an empty string to an action method with a routed URL segment rather than a query string?

I have a controller class Movie. One of its action method is given as follows:
public string ActionMethod(string id)
{
if (id == null)
{
return "null";
}
if (id == string.Empty)
{
return "empty";
}
return "neither null nor empaty";
}
Is it possible to pass an empty string to an action method with a routed URL segment rather than a query string?
Note that http://localhost:21068/Movies/ActionMethod?id= can do that but it uses a query string. In addition, it is not allowed to modify the action method. :-)
I'm not clear about what you're trying to achieve, but you can probably do it by specifying routes appropriately in Global.asax.cs. As a contrived example, the following:
routes.MapRoute(
"",
"{controller}/{action}/id/{id}",
new { controller = "Home", action = "Index", id = "" }
);
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
lets you use:
http://localhost:21068/Movies/ActionMethod - id is null
http://localhost:21068/Movies/ActionMethod/id/ - id is an empty string
http://localhost:21068/Movies/ActionMethod/id/123 - id is a nonempty string
If you state what URLs you want to allow and how to map them, someone will no doubt suggest how you can set up your routes.
Dont pass any querystring if ur ID is empty. like
http://localhost:21068/Movies/ActionMethod
If you can't use a query string, have you considered using a form? I haven't tested this, but give it a go:
#using (Html.BeginForm("ActionMethod", "YourControllerName", "GET")) {
<input type="text" name="id">
<input type="submit" name="submitForm" value="submitForm" />
}
Reference: http://msdn.microsoft.com/en-us/library/dd460344(v=vs.108).aspx
I think you can pass nullable parameter like:
public string ActionMethod(string? id)
and than don't pass any query string as:
http://localhost:21068/Movies/ActionMethod

How can I make Html.DropDownList encode its values?

In my viewData I have an IList mls.
I want to use this to show in a dropdown. Like so:
<%= Html.DropDownList("ml3Code",
new SelectList(Model.Mls, "Code", "Description", Model.Ml3.Code ?? ""),
Model.T9n.TranslateById("Labels.All"),
new { #class = "searchInput" })%>
This works fine, until there's a myObject.Code == VOC<420 g/l.
I would have expected that an HTML helper would encode its values, but it doesn't.
How should I approach this problem? The only thing I can come up with is first making a dupe list of the objects with encoded values and then feeding it to the selectlist. This would be really bothersome.
P.S. I hope Phill H. and his team will have a long and thorough look at the encoding for asp.net-mvc 2.0...
I'm puzzled. The question "Do ASP.NET MVC helper methods like Html.DropDownList() encode the output HTML?" was asked on SO before, and the answer was "Yes" - and the source-code from the MVC framework was cited to back this assertion up.
Well, you can roll your own Html helper, but if you're like me you won't want to do that.
To me, I see two options here:
Write your select element in plain view without the helper. I've never felt the helpers provide you much save for highlighting an element when an error occurs.
Patch the select box on the client when the page loads, as in:
function encodeHtml(str)
{
var encodedHtml = escape(str);
encodedHtml = encodedHtml.replace(///g,"%2F");
encodedHtml = encodedHtml.replace(/\?/g,"%3F");
encodedHtml = encodedHtml.replace(/=/g,"%3D");
encodedHtml = encodedHtml.replace(/&/g,"%26");
encodedHtml = encodedHtml.replace(/#/g,"%40");
return encodedHtml;
}
window.onload = function()
{
var ml3Code = document.getElementById("ml3Code");
for(var i = 0; i < ml3Code.options.length; ++i)
{
ml3Code.options[i].value = encodeHtml(ml3Code.options[i].value);
}
};
It's a hack, I know. I strongly prefer the first choice.
This is encoded. But dont check with firebug - It shows values decoded.
Check in ViewSource of the Browser and things are encoded.
Controller
public List<CategoryInfo> GetCategoryList()
{
List<CategoryInfo> categories = new List<CategoryInfo>();
categories.Add(new CategoryInfo { Name = "Food<äü", Key = "VOC<420 g/l", ID = 2, Uid = new Guid("C0FD4706-4D06-4A0F-BC69-1FD0FA743B07") });
}
public ActionResult Category(ProductViewModel model )
{
IEnumerable<SelectListItem> categoryList =
from category in GetCategoryList()
select new SelectListItem
{
Text = category.Name,
Value = category.Key
};
model.CategoryList = categoryList;
return View(model);
}
View
<%= Html.DropDownList("Category" , Model.CategoryList) %>
Model
public class ProductViewModel
{
public IEnumerable<SelectListItem> CategoryList { get; set; }
public List<CategoryInfo> Categories { get; set; }
}
HTML
<select id="Category" name="Category"><option value="VOC<420 g/l">Food<äü</option>
</select>

Categories