Multiple Html.ActionLink() within single MvcContrib.Grid column in MVC3 - asp.net-mvc-3

I'm trying to wrap my head around the proper syntax to achieve the following--or at least to find out if it's even do-able or if there is a better alternative solution.
Ok, out of the box, when you generate a strongly-typed View using the List scaffold under ASP.Net MVC3 you get a simple table with a column that has something like two or three Html.ActionLink() items representing common actions like so:
Edit | Details | Delete
I would like to use the MvcContrib grid and do the same, but I cannot figure out the correct syntax to get it to work. So far, in my Index.cshtml, I have the following snippet:
#(
Html.Grid(Model.PagedList).AutoGenerateColumns()
.Columns(column =>
{
column.For(f => Html.ActionLink("Edit", "Edit", new { id = f.itemID}))
.Named("");
})
.Sort(Model.GridSortOptions);
)
but that just gives me one column for "Edit", where as I want the column to contain three action links--Edit, Devices, Delete--with all three having the same itemID for the particular row. Is this achievable? And if so, how? If not, is there an alternative?

You could use a custom column:
columns.Custom(
#<text>
#Html.ActionLink("edit", "edit", new { id = item.Id }) |
#Html.ActionLink("details", "details", new { id = item.Id }) |
#Html.ActionLink("delete", "delete", new { id = item.Id }) |
</text>
);

Maybe it helps somebody:
#Html.Grid(Model).Columns(column =>
{
column.For( c=> Html.Raw(Html.ActionLink(...).ToString() +
" " + Html.ActionLink(...).ToString())).Named("Actions").Encode(false);
....

You should pass an inline helper:
column.For(#<text>
#Html.ActionLink("Edit", "Edit", new { id = item.itemID })
#Html.ActionLink(...)
#Html.ActionLink(...)
</text>)

Related

MVC3 #Html.ActionLink displays Text together with the address

I do not know what I have done wrong but all my #Html.ActionLinks are displaying the expected text together with hyperlink instead of the text alone.
#Html.ActionLink("About", "About", "Home")
displays as
About(Home/About)
instead of
About
In the views,
#Html.ActionLink("Edit", "Edit", new { id = item.Id })
displays
Edit(Home/Edit/4)
instead of the text
Edit
Any clues would be appreciated.
You might have an extension or DisplayTemplate that would change the default behaviour.
It looks like your second example is missing a parameter (the last null for html attributes, I also included the Controller here)
#Html.ActionLink("Edit", "Edit", "Home", new { id = item.Id }, null)

ASP.NET MVC DropDownListFor not selecting value from model

I'm using ASP.NET MVC 3, and just ran into a 'gotcha' using the DropDownListFor HTML Helper.
I do this in my Controller:
ViewBag.ShippingTypes = this.SelectListDataRepository.GetShippingTypes();
And the GetShippingTypes method:
public SelectList GetShippingTypes()
{
List<ShippingTypeDto> shippingTypes = this._orderService.GetShippingTypes();
return new SelectList(shippingTypes, "Id", "Name");
}
The reason I put it in the ViewBag and not in the model (I have strongly typed models for each view), is that I have a collection of items that renders using an EditorTemplate, which also needs to access the ShippingTypes select list.
Otherwise I need to loop through the entire collection, and assign a ShippingTypes property then.
So far so good.
In my view, I do this:
#Html.DropDownListFor(m => m.RequiredShippingTypeId, ViewBag.ShippingTypes as SelectList)
(RequiredShippingTypeId is of type Int32)
What happens is, that the value of RequiredShippingTypeId is not selected in the drop down.
I came across this: http://web.archive.org/web/20090628135923/http://blog.benhartonline.com/post/2008/11/24/ASPNET-MVC-SelectList-selectedValue-Gotcha.aspx
He suggests that MVC will lookup the selected value from ViewData, when the select list is from ViewData. I'm not sure this is the case anymore, since the blog post is old and he's talking about MVC 1 beta.
A workaround that solves this issue is this:
#Html.DropDownListFor(m => m.RequiredShippingTypeId, new SelectList(ViewBag.ShippingTypes as IEnumerable<SelectListItem>, "Value", "Text", Model.RequiredShippingTypeId.ToString()))
I tried not to ToString on RequiredShippingTypeId at the end, which gives me the same behavior as before: No item selected.
I'm thinking this is a datatype issue. Ultimately, the HTML helper is comparing strings (in the Select List) with the Int32 (from the RequiredShippingTypeId).
But why does it not work when putting the SelectList in the ViewBag -- when it works perfectly when adding it to a model, and doing this inside the view:
#Html.DropDownListFor(m => m.Product.RequiredShippingTypeId, Model.ShippingTypes)
The reason why this doesn't work is because of a limitation of the DropDownListFor helper: it is able to infer the selected value using the lambda expression passed as first argument only if this lambda expression is a simple property access expression. For example this doesn't work with array indexer access expressions which is your case because of the editor template.
You basically have (excluding the editor template):
#Html.DropDownListFor(
m => m.ShippingTypes[i].RequiredShippingTypeId,
ViewBag.ShippingTypes as IEnumerable<SelectListItem>
)
The following is not supported: m => m.ShippingTypes[i].RequiredShippingTypeId. It works only with simple property access expressions but not with indexed collection access.
The workaround you have found is the correct way to solve this problem, by explicitly passing the selected value when building the SelectList.
This might be silly, but does adding it to a variable in your view do anything?
var shippingTypes = ViewBag.ShippingTypes;
#Html.DropDownListFor(m => m.Product.RequiredShippingTypeId, shippingTypes)
you can create dynamic viewdata instead of viewbag for each dropdownlist field for complex type.
hope this will give you hint how to do that
#if (Model.Exchange != null)
{
for (int i = 0; i < Model.Exchange.Count; i++)
{
<tr>
#Html.HiddenFor(model => model.Exchange[i].companyExchangeDtlsId)
<td>
#Html.DropDownListFor(model => model.Exchange[i].categoryDetailsId, ViewData["Exchange" + i] as SelectList, " Select category", new { #id = "ddlexchange", #class = "form-control custom-form-control required" })
#Html.ValidationMessageFor(model => model.Exchange[i].categoryDetailsId, "", new { #class = "text-danger" })
</td>
<td>
#Html.TextAreaFor(model => model.Exchange[i].Address, new { #class = "form-control custom-form-control", #style = "margin:5px;display:inline" })
#Html.ValidationMessageFor(model => model.Exchange[i].Address, "", new { #class = "text-danger" })
</td>
</tr>
}
}
ViewModel CompanyDetail = companyDetailService.GetCompanyDetails(id);
if (CompanyDetail.Exchange != null)
for (int i = 0; i < CompanyDetail.Exchange.Count; i++)
{
ViewData["Exchange" + i]= new SelectList(companyDetailService.GetComapnyExchange(), "categoryDetailsId", "LOV", CompanyDetail.Exchange[i].categoryDetailsId);
}
I was just hit by this limitation and figured out a simple workaround. Just defined extension method that internally generates SelectList with correct selected item.
public static class HtmlHelperExtensions
{
public static MvcHtmlString DropDownListForEx<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression,
IEnumerable<SelectListItem> selectList,
object htmlAttributes = null)
{
var selectedValue = expression.Compile().Invoke(htmlHelper.ViewData.Model);
var selectListCopy = new SelectList(selectList.ToList(), nameof(SelectListItem.Value), nameof(SelectListItem.Text), selectedValue);
return htmlHelper.DropDownListFor(expression, selectListCopy, htmlAttributes);
}
}
The best thing is that this extension can be used the same way as original DropDownListFor:
#for(var i = 0; i < Model.Items.Count(); i++)
{
#Html.DropDownListForEx(x => x.Items[i].CountryId, Model.AllCountries)
}
There is an overloaded method for #html.DropdownList for to handle this.
There is an alternative to set the selected value on the HTML Dropdown List.
#Html.DropDownListFor(m => m.Section[b].State,
new SelectList(Model.StatesDropdown, "value", "text", Model.Section[b].State))
I was able to get the selected value from the model.
"value", "text", Model.Section[b].State this section the above syntax adds the selected attribute to the value loaded from the Controller

Unexpected HTML from Razor

I have some Razor code in a view that is supposed to route through to a different part of my model:
#Html.ActionLink("Edit", "Edit", "Journal", new { id = item.JOURNAL.REF_ID })
but when I look at the HTML that is emmitted, it is does not relect what I have written:
Edit
How can I stop this from happening?
That's because you are using the wrong overload. It should be like this:
#Html.ActionLink("Edit", "Edit", "Journal", new { id = item.JOURNAL.REF_ID }, null)
Let's see why you are using the wrong overload. Let's break down what you wrote:
#Html.ActionLink(
"Edit", // linkText
"Edit", // actionName
"Journal", // routeValues
new { id = item.JOURNAL.REF_ID } // htmlAttributes
)
See the problem?
And now let's break down the correct way:
#Html.ActionLink(
"Edit", // linkText
"Edit", // actionName
"Journal", // controllerName
new { id = item.JOURNAL.REF_ID }, // routeValues
null // htmlAttributes
)
See the difference?
I would recommend you reading very carefully the documentation and the different available overloads of the ActionLink helper as well as the exact significance of their parameters.

ViewBag oddity in creating a MultiSelectList

I have spent a lot of time scouring the various forums for help on MultiSelectLists in asp.net MVC3. Finally figured out how to solve my issue now I'm trying to cut down on my code and I've come across something weird with ViewBags. First the code, than I'll explain the behavior.
My function that creates the MultiSelectList to used by the Controller
public MultiSelectList GetPermissionList(string[] selectedValues)
{
List<SelectListItem> permissions = new List<SelectListItem>()
{
new SelectListItem{ Value = "", Text = "None"},
new SelectListItem{ Value = "View", Text = "View"},
new SelectListItem{ Value = "Add", Text = "Add"},
new SelectListItem{ Value = "Edit", Text = "Edit"},
new SelectListItem{ Value = "Delete", Text = "Delete"}
};
return new MultiSelectList(permissions, "Value", "Text", selectedValues);
}
partial code from the edit action from the controller
public ActionResult Edit(int id)
{
ViewBag.Title = "Edit a Security Role";
SecurityRoles securityroles = Repository.Details(id);
ViewBag.Orders = securityroles.Orders.Split(',');
ViewBag.OrdersListBox = GetPermissionList(ViewBag.Orders);
return View(securityroles);
}
partial code from the View
<td class="rightAlign topAlign editor-label">
#Html.MyLabel(m => m.Orders, "lblOrders")
</td>
<td class="editor-field">
#Html.ListBoxFor(m => m.Orders, ViewBag.OrdersListBox as MultiSelectList, new { size = "5" })
</td>
Keep in mind I've cut out a large chunk of code from the edit Action, I have roughly 9 list boxes I'm creating for this security role manager.
My goal, in the edit action is to simply have 1 line of code, calling the GetPermissionList and having it return to the viewbag so I can just display that in the view, as opposed to the 2 lines per listbox that I currently have.
Just looking at the code, it seems obvious if I were to make the call this way:
ViewBag.OrdersListBox = GetPermissionList(securityroles.Orders.Split(','));
It should work, but the selected values do not come through. To compound the oddity, here is something else I tried and it worked fine, but it makes no sense why.
ViewBag.Orders = securityroles.Orders.Split(',');
ViewBag.OrdersListBox = GetPermissionList(securityroles.Orders.Split(','));
ViewBag.Orders plays no role in the ViewBag.OrdersListBox nor is it used in the view, but when I simply assign it a value than the 2nd line of code works.
Does this make sense to anyone? Any suggestions on how to create a way for the GetPermissionList to simply work correctly by sending it a string array instead of passing it the ViewBag object?
I think, You have to set Orders because that is what the selected values are being bound to when the selection happens. You could just pass a string[] if you didn't want to have anything preselected. Check out Darin's answer in this post. He is using a model but i think the same concept applies to view bag.
Multiselect with ViewModel

Telerik MVC specifying your own action router within a template column

I am using the latest version of Telerik MVC with ASP.NET MVC 3 and the Razor view engine.
I have the following column declaration:
column.Bound(x => x.Id)
.Template(x => Html.ActionLink("Edit", "Edit", new { id = x.Id }))
.Title("Action")
.Width(100);
I have created my own method that routes to this Edit action method which I would like to use but not sure how to?
public static object AdministrationCategoryEdit(this UrlHelper urlHelper, int categoryId)
{
Check.Argument.IsNotNull(urlHelper, "urlHelper");
return new { area = "Administration", controller = "Category", action = "Edit", id = categoryId };
}
How would I reference the above method in my column declaration and pass it through the category ID?
For example, if I want to use it with a button, then I would do something like:
$('#btnEdit').click(function () {
window.location = '#Url.RouteUrl(Url.AdministrationCategoryEdit(Model.Id))';
});
There is no perfect match for your requirement since all ActionLink methods have a separate parameter for the action. However, it should work with the following code even though the action is now specified twice.
column.Bound(x => x.Id)
.Template(x => Html.ActionLink("Edit", "Edit", Url.AdministrationCategoryEdit(x.Id)))
.Title("Action")
.Width(100);
An alternative would be to create an HTML helper similar to ActionLink that now only generates the route parameters but the complete code for a link.

Resources