How to bind OrderedDictionary? - c#

I try to bind an OrderedDictionary to a view but when the post method gets invoked the Dictionary is always empty.
Here is my code:
[HttpGet]
public ViewResult Edit(string username, string password)
{
Xml test = new Xml(#"c:\Users\pc\Desktop\xml - Copy.xml");
XmlNode userNode = test.GetUserNodeByUsernameAndPassword(username, password);
User user = new User();
user.BindData(userNode);
return View(user.user);
}
[HttpPost]
public ViewResult Edit(OrderedDictionary attributes)
{
return View(attributes);
}
And here is the view:
#using (Html.BeginForm("Edit", "Users")) {
#Html.ValidationSummary(true)
<fieldset>
<legend>User</legend>
<p>
<input type="submit" value="Save" />
</p>
#{int counter = 0;}
#{string name = "";}
#foreach (DictionaryEntry attribute in Model)
{
{ name = "[" + counter + "].key"; }
<input type="hidden" name=#name value=#attribute.Key />
#attribute.Key #Html.TextBoxFor(m => attribute.Value)
counter++;
<br />
}
</fieldset>
}
And the result Html looks like this is:
<input type="hidden" value="Username" name="[0].key">
Username
<input id="attribute_Value" type="text" value="Anamana" name="attribute.Value">
So the content of the OrderedDictionary appears fine in the view but when I make a post back the binding isn't working and the directory remains empty.

Concept
To bind a dictionary you have to change the name attribute in the html input tag. Something like this:
In your controller:
[HttpPost]
public ActionResult Edit(IDictionary<string, string> attributes)
{
}
In your HTML:
<input type="text" name="attributes[0].Key" value="A Key" />
<input type="text" name="attributes[0].Value" value="A Value" />
<input type="text" name="attributes[1].Key" value="B Key" />
<input type="text" name="attributes[1].Value" value="B Value" />
The attributes name should be before the index [0] on ther name attribute, because your action expect it.
Tips
I would use the HiddenFor and TextBoxFor HTML Helper of the Asp.Net MVC.
#Html.HiddenFor(model => model[i].Key)
#Html.TextBoxFor(model => model[i].Value)
And it will render in the format that the asp.net mvc will understand and get it working.
For more samples about databind take a look at this link.

Meantime I have found the solution.
I can pass an OrderedDictionary to the view page.
It process it by the following Razor code:
#model System.Collections.Specialized.OrderedDictionary
(...)
#{int counter = 0;}
#{string name = "";}
#foreach (DictionaryEntry attribute in Model)
{
{ name = "[" + counter + "].key"; }
#Html.Hidden(name, attribute.Key)
{name = "[" + counter + "].value";}
#attribute.Key #Html.TextBox(name, attribute.Value)
counter++;
<br />
}
The result HTML's structure fits to the samples which is found in a book, the values from the dictionary appears fine on the page.
After POST was invoked the POST handler function gets the modified values in a Dictionary.
[HttpPost]
public ViewResult Edit(Dictionary<string, string> attributes)
{}
I don't know why but I can't use OrderedDictionary here.

Related

How to overwrite one model property in ASP.Net core

I have a model with say 10 properties. A, B, C and so on...
Property A is an array.
For each value in array I generate one tag like this:
<div class="col-sm-10 row">
#foreach (var item in Model.A)
{
<div class="col-sm-1 right-buffer">
<i class="" aria-hidden="true">#item.Text</i>
</div>
}
</div>
When user clicks on some link I should redirect it to the same page, but with Some model property changed. For example:
Current url: my/controller/someaction?name=Alex&age=20&from=fr&CurrentA=
with model ?name=Alex&age=20&from=fr&CurrentA=
If user clicks on <a> with text foo it should be redirected on my/controller/someaction?name=Alex&age=20&from=fr&CurrentA=foo
then is clicks on <a> with text bar and it should be now redirected on my/controller/someaction?name=Alex&age=20&from=fr&CurrentA=bar
So entire query string (except one parameter) should be preserved to send current model state to server while I want to set one value and redirect it to the same page but with new value.
Eventually, it should acts like postback with one extra value setted to model
Is it possible or I should use JS and perform everything myself?
Manually i solved it like this:
First, create hidden fields for every property in model:
<form asp-controller="search" asp-action="index" method="get" role="form" id="searchForm" asp-antiforgery="false">
<input asp-for="SessionId" type="hidden" name="sessionId" value="#Model.SessionId" />
<input asp-for="Quantity" type="hidden" name="quantity" value="#Model.Quantity"/>
<input asp-for="SortField" type="hidden" name="sortField" value="#Model.SortField"/>
<input asp-for="IsAscending" type="hidden" name="IsAscending" value="#Model.IsAscending" />
<input asp-for="Offset" type="hidden" name="offset" value="0" />
...
</form>
Then, use JS to replace value in hidden field and then submit form. Values from inputs will be autimatically converter in query string, so everything works fine:
function sortDocuments(sortField) {
var sField = document.getElementById('SortField');
var isDescending = document.getElementById('IsAscending');
if (sField.value === sortField) {
if (isDescending.value.toUpperCase() === 'FALSE') {
isDescending.value = 'TRUE';
} else {
sField.value = 'rank';
isDescending.value = 'FALSE';
}
} else {
sField.value = sortField;
isDescending.value = 'FALSE';
}
document.getElementById('searchForm').submit();
}
Not very elegant, but it does its job.

Having trouble in accessing posted form data (MVC)

How to access posted form data in action method and display in view
I'm creating an input wizard in MVC. There are two views, one for taking input from user and another for display the input data.
Here is the model's code:
public class Info
{
public int CustomerId { set; get; }
public double Price { set; get; }
public string name { set; get; }
}
}
Controller's Code-
public ActionResult FillCustomer()
{
return View();
}
public ActionResult DisplayCustomer(FormCollection frm)
{
Info info = new Info();
info.name = Convert.ToString( Request.Form["name"]);
info.Price = Convert.ToDouble(Request.Form["price"]);
info.CustomerId = Convert.ToInt32(Request.Form["customerid"]);
return View(info);
}
FillCustomer View's Code-
<form method="post" action="DisplayCustomer">
Name: <input type="text" id="name" /><br />
Price: <input type="text" id="price" /><br />
CustomerId: <input type="text" id="customerid" />
<br />
<input type="submit" id="btn1" />
</form>
</body>
</html>
DisplayCustomer View's Code-
<body>
<div>
Name is <%=Model.name %> and ID is <%=Model.CustomerId %>
<%if (Model.Price > 200)
{%>
Greater than 200
<%}
else %>
<%{%>Lesser than 200
<%} %>
</div>
</body>
</html>
I checked with debugger, controller is not getting posted data.
Your form's input elements should have a name attribute
<form method="post" action="DisplayCustomer">
Name: <input type="text" id="name" name="name" /><br />
Price: <input type="text" id="price" name="price" /><br />
CustomerId: <input type="text" id="customerid" name="customerid" />
<br />
<input type="submit" id="btn1" />
</form>
Now you will get form data as
Info info = new Info();
info.name = Convert.ToString( Request.Form["name"]);
info.Price = Convert.ToDouble(Request.Form["price"]);
info.CustomerId = Convert.ToInt32(Request.Form["customerid"]);
In controller, form data is received as key/value pairs. And keys are generaetd from name attributes.
You are using MVC so the parameter in DisplayCustomer method is your model passed from view, which is created implicitly by model binder. Change the type from FormCollection to Info class as following:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult DisplayCustomer(Info model)
{
// you can now access to Info properties
}
This way values of Info object are populeted and I think this is what you are trying to accomplish.
I think you have missed to write a Attribute top of the DisplayCustomer function. Just write it, I hope it will work.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult DisplayCustomer(FormCollection frm)
{
Info info = new Info();
info.name = Convert.ToString( Request.Form["name"]);
info.Price = Convert.ToDouble(Request.Form["price"]);
info.CustomerId = Convert.ToInt32(Request.Form["customerid"]);
return View(info);
}

Posting a model back to controller in ASP.NET MVC3

I'm trying to post a model back to the controller but for some reason the controller always gets NULL back. I know I'm doing something really obviously wrong. What is it?
However if I post back a specific attribute from that model, it works just fine.
Controller:
[HttpPost]
public void MyAction(Company company)
{
System.Diagnostics.Debug.WriteLine("STUFF:" + company.dbName);
if(company.CompanyOptions!=null)foreach (var item in company.CompanyOptions.CompanyLicenseOptions.CompanyLicenseOptionsList) System.Diagnostics.Debug.WriteLine("STUFF:" + item);
else System.Diagnostics.Debug.WriteLine("STUFF IS NULL");
}
View:
#model Domain.Entities.Company
#using (Html.BeginForm("MyAction", "Controller", FormMethod.Post))
{
foreach (var licensedFeature in Model.CompanyOptions.CompanyLicenseOptions.CompanyLicenseOptionsList)
{
#Html.CheckBox(licensedFeature.LicenseName, licensedFeature.IsLicensed, checkboxHtmlAttributes);
#licensedFeature.LicenseName
}
<input type="hidden" name="company" value="#Model"/>
<input id="submit_licenses" type="submit" style="display:none;" />
}
#Model appears to be a complex type so you cannot assign to as the value of the input field.
The best you can do is use an #Html.EditorFor to generate the required html fields for the model and this will ensure that they are posted back as the Company object.
Replace this
<input type="hidden" name="company" value="#Model"/>
with
#Html.EditorFor(model => model)

Edit Dictionary in MVC3

I have a view-model "MyClass" that contains a dictionary:
Dictionary<string, bool> columns {get; set;}
and it has keys and values
columns.Add("Name", true);
columns.Add("Type", true);
now, I want to let the user edit MyClass and its dictionary.
For every key I want to show its key and to enable to check\unchek its value (true\false), such that the input's name will be the right name for editing. since the dictionary has many keys, I need to to it with "foreach" or "for".
How can I do that?
You can start from http://www.asp.net/mvc/tutorials/mvc-music-store/mvc-music-store-part-1
You need to write something like this in your View
#using (Html.BeginForm()) {
<fieldset>
<legend>MyClassViewModel</legend>
#foreach (var item in Model.Columns){
#Html.Label(item.Key)
#Html.CheckBox(item.Key, item.Value)
}
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
and your view must be bind to model in that case
#model MvcDemoApp.Models.MyClassViewModel
or as an alternative
#foreach (var item in Model.Columns)
{
#item.Key
<input type="checkbox" id="#item.Key" checked="#item.Value" />
<input type="hidden" value="#item.Value" />
<br />
}
or try using CheckBoxList custom helper by community
http://weblogs.asp.net/gunnarpeipman/archive/2011/05/05/asp-net-mvc-implementing-checkboxlist.aspx
http://www.codeproject.com/Articles/292050/MVC3-Html-CheckBoxList-custom-extension
In your case you want something like this, for the default binder to understand the name="" format:
<input type="checkbox" name="columns[0].Value" value="Key0 string value" />
<input type="checkbox" name="columns[1].Value" value="Key1 string value" />
Unfortunately the default helpers don't fully support this so you've to write the name="" explicitly. Something like:
#Html.CheckBox("columns[" + i + "].Value", new { value = columns[i].Key });
Read more about these kind of bindings on this blog post.

Get html items from view to control

Still kind of new to MVC, so please bear with me. I'm trying to grab some dynamically generated HTML. In this case, list items in my notifyList. I plan on looping through each one in the controller and adding them as database entries. Any help is appreciated, thanks.
View
#model _BaseViewModel
// The form it's within...
#using (Html.BeginForm("Create", "Leaf", FormMethod.Post, new { id = "createForm" }))
<div class="editor-label bottom-area bottom-header">
Notification List:
</div>
<div class="editor-field bottom-area">
<ul id="notifyList"></ul>
</div>
Controller:
[HttpPost]
public ActionResult Create(_BaseViewModel model)
{
// Some loop here
// get html here
db.UserItems.AddObject(model.user);
db.SaveChanges();
//
return RedirectToAction("Index");
}
As far as I understood, you use jQuery to fetch <li/> elements into notifyList. What you need to do here is to generate a hidden input as well. Sample:
$("#btnAppend").click(function() {
for(var i = 0; i < 4; i++) {
var _val = "Foo " + i;
var $li = $("<li/>").text(_val);
var $hidden = #("<input/>").
attr("type", "hidden")
attr("name", "foo").
val(_val);
$hidden.appendTo($li);
$li.appendTo("#notifyList");
}
});
This code will generate following output inside your DOM:
<ul id="notifyList">
<li>Foo 0<input type="hidden" value="Foo 0" name="foo" /></li>
<li>Foo 1<input type="hidden" value="Foo 1" name="foo" /></li>
<li>Foo 2<input type="hidden" value="Foo 2" name="foo" /></li>
<li>Foo 3<input type="hidden" value="Foo 3" name="foo" /></li>
</ul>
When you make a http form post, you can grab the values by the below controller action implementation:
public ActionResult Index(string[] foo) {
foreach(var item in foo) {
//Work with each individual item
}
//continue your code
}
it doesn't work this way. html only exists in the view. the controller has no concept of html (not should it). data sent to the controller comes in 1 of types (GET, POST). there are others, but these are the main to.
get is typically associated with the querystring www.domain.com/mypage.aspx?key=value
where post is the input values from form
<form action="mypage.aspx" method="post">
<input name="key" value="value"/>
<input type="submit" value="click me"/>
</form>
So adding items to a html list won't provide any meaning to the controller. javascript and ajax provide more options on how the data gets sent to the server, but the data is sent, not the markup. and the data is sent as key value pairs.

Categories

Resources