MVC 3 WebGrid with a checkbox filter - asp.net-mvc-3

I'm developing a web application using MVC 3 RTM. In a view I have a WebGrid which works fine with sorting and paging. However, I also needed some filtering support on the page, so I added a textbox and a checkbox. The view code looks like this:
#using (Html.BeginForm("Index", "Customer", FormMethod.Get))
{
<fieldset>
<legend>Filter</legend>
<div class="display-label">
Search for</div>
<div class="display-field">#Html.TextBox("filter", null, new { onchange = "$('form').submit()" })</div>
<div class="display-label">
Show inactive customers?
</div>
<div class="display-field">
#Html.CheckBox("showInactive", false, new { onchange = "$('form').submit()" })
</div>
</fieldset>
var grid = new WebGrid(canPage: true, canSort: true, ajaxUpdateContainerId: "grid", rowsPerPage: 10, defaultSort: "Name");
grid.Bind(Model, rowCount: Model.Count, autoSortAndPage: false);
grid.Pager(WebGridPagerModes.All);
#grid.GetHtml(htmlAttributes: new { id = "grid" },
columns: grid.Columns(
grid.Column("Name", "Name"),
grid.Column("Address", "Address"),
grid.Column("IsActive", "Active", (item) => item.IsActive ? "Yes" : "No")));
}
This works fine except when I check the checkbox. When I load the page for the first time, the checkbox is not checked. Sorting and paging works, and I can enter some text as a filter and it filters my result, and sorting and paging still works after that. However, if I check the checkbox, it updates the result, but sorting no longer works. The filter still works though, so if I enter some text, it correctly filters the result and still respects the checkbox.
I've tried setting a breakpoint in my controller, and there's no problem there. The request is sent when I try to sort, and the controller correctly returns the view with the result as the model. But it doesn't seem like the WebGrid is updating itself.
Has anyone else experienced this, or has anyone some good advice on what to look for?
Thanks!

I normally add a form (above my WebGrid) which contains a textbox called "Keywords" and a checkbox called "IsActive" and when the "Go" button is clicked it reloads the WebGrid adding the "Keywords" and "IsActive" options to the query string.
You can add more elements to your form and their values will be sent al
Use the following helper script - webgrid.helper.js:
function reloadGrid(form) {
var grid = form._grid ? form._grid.value : "grid";
var args = {};
updateQueryParams(args, grid + " th a");
args.sort = "";
args.sortdir = "";
updateQueryParams(args, grid + " tfoot a");
args.page = 1;
for (var i = 0; i < form.elements.length; i++) {
var el = form.elements[i];
if (el.type == "text" || el.type == "select" || (el.type == "radio" && el.checked))
args[el.name] = el.value;
else if (el.type == "checkbox")
args[el.name] = el.checked;
}
//get controller name
var s = $("#grid th a")[0].onclick.toString();
s = s.substring(s.indexOf("/"));
var controller = s.substring(0, s.indexOf("?"));
var queryString = "";
for (var key in args)
queryString += "&" + key + "=" + escape(args[key]);
var url = controller + "?" + queryString.substring(1);
$("#" + grid).load(url + " #" + grid);
}
function updateQueryParams(args, path) {
var links = $("#" + path);
if (links.length == 0)
return;
var s = links[0].onclick.toString();
s = s.substring(s.indexOf("?") + 1);
s = s.substring(0, s.indexOf(" #"));
var a = /\+/g; // Regex for replacing addition symbol with a space
var r = /([^&=]+)=?([^&]*)/g;
var d = function (s) { return decodeURIComponent(s.replace(a, " ")); };
var q = s;
while (e = r.exec(q))
args[d(e[1])] = d(e[2]);
}
Then just above my webgrid, I have the following partial file - *_ListFilter.cshtml*
#using (Html.BeginForm(null, null, FormMethod.Get))
{
<div id="filter">
<table width="600">
<tr>
<td>#Html.TextBoxFor(c => c.Keywords, new { size = 50, placeholder = Strings.SearchByKeywords, title = Strings.SearchByKeywords })</td>
<td>&nbsp

Related

add a variable in an id attribute in ajax's div.append

I am developing a web app with Django and i have this ajax where i'm refreshing some images from the db in order to display them in a template.
function refreshUploadedImages() {
var inputs = ['Designer Name', 'Color', 'Fabric', 'Type', 'Tag', 'Subtag'];
$.getJSON('/admin/image-uploader/images', function(data) {
$('#uploadedFiles').empty();
for (uiid in data) {
ui = data[uiid];
var div = $('<div>');
div.data('id', ui.id);
// image
var image = new Image();
image.src = ui.url
image.width = 180;
div.append($('<div>').append(image));
// list
var ul = $('<ul>')
div.append(ul)
// inputs
for (input in inputs) {
ul.append(
$('<li>').append(
$('<label>').append(
$('<span>').append(document.createTextNode(inputs[input] + ':'))
).append($('<input>'))));
}
$('#uploadedFiles').append(div);
div.append('<li><input type="button" class="delete-img-btn" id = <<ui.id>> img-id=image.id value="Delete"/></li>');
}
$(window).trigger('uploadedImagesRefresh');
});
$(function(){
//
$('.delete-img-btn').live('click', function() {
//asign the image id from the button attribute 'img-id'
var id= $(this).attr('img-id');
//The data to be send via ajax the server will recieve 2 POST variables ie. 'action' and 'id'(which is the img id)
var data={
'action':'/admin/image-uploader/',
'pk' : id,
'success':refreshUploadedImages
};
//The ajax request.
vary = $('.delete-img-btn').attr('id');
$.post("/admin/image-uploader/delete/"+vary , data);
});
});
}
My problem is, in this line
div.append('<li><input type="button" class="delete-img-btn" id = <<ui.id>> img-id=<<<image.id>>> value="Delete"/></li>');
I want to assign id a variable ui.id i.e (id = <<ui.id>> ) which is defined somewhere outside the div.append. Can you help me on how to do it please.
Is this all you're trying to do?
div.append(
'<li><input type="button" class="delete-img-btn" id="'
+ ui.id + '" img-id="'
+ image.id + '" value="Delete"/></li>');
image.id isn't defined though.

How can I access the values of my view's dropdown list in my controller?

How can I access the values of my view's dropdown list in my controller?
var typeList = from e in db.Rubrics where e.DepartmentID == 2 select e;
var selectedRubrics = typeList.Select(r => r.Category);
IList <String> rubricsList = selectedRubrics.ToList();
IList<SelectListItem> iliSLI = new List<SelectListItem>();
SelectListItem selectedrubrics = new SelectListItem();
selectedrubrics.Text = "Choose a category";
selectedrubrics.Value = "1";
selectedrubrics.Selected = true;
iliSLI.Add(selectedrubrics);
for(int i = 0;i<rubricsList.Count();++i)
{
iliSLI.Add(new SelectListItem() {
Text = rubricsList[i], Value = i.ToString(), Selected = false });
}
ViewData["categories"] = iliSLI;
In my view this works fine showing the dropdown values:
#Html.DropDownList("categories")
Then in my controller I am using FormCollection like this:
String[] AllGradeCategories = frmcol["categories"].Split(',');
When I put a breakpoint here, I get an array of 1’s in AllGradeCategories. What am I doing wrong?
MORE:
Here’s my begin form:
#using (Html.BeginForm("History", "Attendance",
new {courseID = HttpContext.Current.Session ["sCourseID"] },
FormMethod.Post, new { #id = "formName", #name = "formName" }))
{
<td>
#Html.TextBoxFor(modelItem => item.HomeworkGrade, new { Value = "7" })
#Html.ValidationMessageFor(modelItem => item.HomeworkGrade)
</td>
<td>
#Html.TextBoxFor(modelItem => item.attendanceCode, new { Value = "1" })
#Html.ValidationMessageFor(model => model.Enrollments.FirstOrDefault().attendanceCode)
</td>
<td>
#Html.EditorFor(modelItem => item.classDays)
</td>
<td>
#Html.DropDownList("categories")
</td>
}
My controller signature:
public ActionResult ClassAttendance(InstructorIndexData viewModel, int id, FormCollection frmcol, int rows, String sTeacher, DateTime? date = null)
EDITED 2
Tried this but although it seems to get posted, the I still don’t get the values of the list in the hidden field or the categories parameter.
#Html.Hidden("dropdownselected1");
#Html.DropDownList("categories",ViewBag.categories as SelectList, new { onchange = "changed" })
</td>
$(function () {
$("#dropdownselected1").val($("#categories").val());
});
If you are looking to access the selected value from the dropdown list, use a hidden field and repopulate that field on onChange() javascript event of the dropdown list.
but you can use normal
#Html.Hidden("dropdownselected1");
#Html.DropDownList("categories",new { onchange ="changed();"}
function changed()
{
$("#hiddenfieldid").val($("#dropdownlistid").val());
}
should do.
I ended up creating a ViewModel to handle just the drowdown list with an associated partial view. Then in the controller I accessed the values of the list using FormCollection().

ASP.NET MVC 3 WebGrid Sort Order Image

I am working with a WebGrid, and I would like to have an image or character like "^" "v" in the header showing the column with the sort order.
How can I do this?
This is the code of one of my Webgrids:
<div id="contenedor-gridACTIVIDAD">
#{
WebGrid gridACTIVIDAD = new WebGrid(Model.actividadDiaria, ajaxUpdateContainerId: "contenedor-gridACTIVIDAD", rowsPerPage: 20);
}
#gridACTIVIDAD.GetHtml(
fillEmptyRows: true,
alternatingRowStyle: "fila-alternativa",
headerStyle: "encabezado-grid",
footerStyle: "pie-grid",
mode: WebGridPagerModes.All,
firstText: "<<",
previousText: "<",
nextText: ">",
lastText: ">>",
columns: new[] {
gridACTIVIDAD.Column("contrato", header: "Contrato"),
gridACTIVIDAD.Column("Observacion", header: "Observación"),
gridACTIVIDAD.Column("DESCR", header: "Tarea"),
gridACTIVIDAD.Column("FECHA", header: "Fecha",
format: (item) =>
{
return item.fecha.ToString("dd/MM/yyyy");
}
),
gridACTIVIDAD.Column("",
header: "ESTADO",
format: (item) =>
{
if (item.estado == "VC")
{
return Html.Image("/Imagenes/vc.gif","Validado correcto", new { #border = "0"});
}
else if (item.estado == "VI")
{
return Html.Image("/Imagenes/vi.gif", "Validado incorrecto", new { #border = "0" });
}
else if (item.estado == "NV")
{
return Html.Image("/Imagenes/vp.gif", "No validado", new { #border = "0" });
}
else
{
return Html.Image("/Imagenes/nv.png", "Pendiente validar", new { #border = "0" });
}
}
),
gridACTIVIDAD.Column("JDP", header: "JDP")
}
)
</div>
I've solved this issue on server side using HTMLAgilityPack.
This is the code for the view:
WebGrid webGrid = new WebGrid(Model.myModel, defaultSort: "title", rowsPerPage: 20);
IHtmlString webGridHtml = webGrid.GetHtml(
columns: webGrid.Columns(
webGrid.Column("id", "ID"),
webGrid.Column("title", "Title")
)
);
#Helper.GetExtendedWebGrid(webGrid, webGridHtml);
And this is the code for the Helper class:
public static IHtmlString GetExtendedWebGrid(WebGrid webGrid, IHtmlString webGridHtml)
{
HtmlString result;
string webGridHtmlString = webGridHtml.ToString();
HtmlDocument htmlDocument = new HtmlDocument();
htmlDocument.LoadHtml(webGridHtmlString);
HtmlNode htmlNodeAnchor = htmlDocument.DocumentNode.SelectSingleNode("//a[contains(#href,'" + webGrid.SortColumn + "')]");
if (htmlNodeAnchor != null)
{
string imgSortDirection;
if (webGrid.SortDirection == SortDirection.Ascending)
imgSortDirection = "asc";
else
imgSortDirection = "desc";
HtmlNode htmlNodeIcon = HtmlNode.CreateNode("<img src=\"/images/" + imgSortDirection + ".png\" width=\"10\" height=\"10\" />");
htmlNodeAnchor.ParentNode.AppendChild(htmlNodeIcon);
webGridHtmlString = htmlDocument.DocumentNode.OuterHtml;
}
result = new HtmlString(webGridHtmlString);
return result;
}
I just did a google search on "webgrid sort indicator" and came back with a result from http://prokofyev.blogspot.com/2011/01/sort-indicator-in-systemwebhelperswebgr.html
Basically, the person uses jQuery to add in the sort direction image.
<script type="text/javascript">
$('thead > tr > th > a[href*="sort=#grid.SortColumn"]').parent()
.append('#(grid.SortDirection == SortDirection.Ascending ? "U" : "D")');
</script>
(As an added bonus, I just tested it myself, so you can copy and paste this code (the link one is a picture instead of a code sample :( ...) just replace the U and D with whatever you want to display instead.
Hope that helps.
This page has a very simple example of how to implement the sorting arrow or images, when using the WebGrid with AJAX.
http://www.mikesdotnetting.com/Article/203/Enhancing-The-WebGrid-With-Sort-Arrows

Hiding columns values for few records in the grid depending on their role type: MVC3

**I am trying to create a view which has a grid
View layout, I am using is:**
#model IEnumerable<VC.MC.ReportalWeb.UI.Users>
#using myspace
#{
ViewBag.Title = "Users";
var grid = new WebGrid(source: Model, canSort: true);
}
<h2>Users</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
#grid.GetHtml(columns: grid.Columns(
grid.Column("UserName"),
grid.Column("Email"),
grid.Column(
header: "",
style: "text-align-center",
format: (item) => new HtmlString(Html.ActionLink("Edit", "Edit", new { id = item.id ).ToString() + " | " +
Html.ActionLink("Details", "Details", new { id = item.id }).ToString() + " | " +
Html.ActionLink("Delete", "Delete", new { id = item.id }).ToString()))
)
)
#grid.GetHtml(columns: grid.Columns(Html.RoleBasedColumns(grid)))
#{
if (!string.IsNullOrWhiteSpace(grid.SortColumn))
{
<script type="text/javascript">
$('thead > tr > th > a[href*="sort=#grid.SortColumn"]').parent().append('#(grid.SortDirection == SortDirection.Ascending ? "^" : "v")');
</script>
}
}
The RoleBasedColumns(grid) is a helper method in my razor which is
public static WebGridColumn[] RoleBasedColumns(
this HtmlHelper htmlHelper,
WebGrid grid
)
{
var user = htmlHelper.ViewContext.HttpContext.User;
var columns = new List<WebGridColumn>();
var query = from p in _adminModelContainer.Users
select p;
IList<Users> userList = query.ToList();
for (int i = 0; i < userList.Count; i++)
{
// The Prop1 column would be visible to all users
columns.Add(grid.Column("UserName"));
if (userList[i].RolesId == 1)
{
// The Prop2 column would be visible only to users
// in the foo role
columns.Add(grid.Column("Email"));
}
}
return columns.ToArray();
}
I want to show Edit and delete link buttons only for those users whose RolesId is 1.
Using the above functionality the grid is just replicating itself .Columns headers are shown whose rolesid is 1.
I am in a fix.
Any help would be of great use.
Thanks
Do it the easy way and use Telerik's free open source mvc controls (the grid) and when you define your grid.. just use a dynamic column along the lines of:
#(Html.Telerik().Grid(Model)
.Name("Grid").TableHtmlAttributes(new { width="800"})
.Columns(columns =>
{
if (userIsInWhateverRole){
columns.Template(o => Html.Action(GenerateYourLinkStuffHere));
}
columns.Bound(o => o.Address).Width(150);
columns.Bound(o => o.City).Width(120);
columns.Bound(o => o.State).Width(100);
})
.Sortable()
.Scrollable()
.Groupable()
.Filterable()
.Pageable(paging =>
paging.PageSize(5)
.Style(GridPagerStyles.NextPreviousAndNumeric)
.Position(GridPagerPosition.Bottom)))
No other grid is as nice in my opinion - and its free. Why give yourself the headache : )

Inline editing with AJAX - how do I create multiple editable areas on the same page?

I found a tutorial on how to create editable regions on a page using AJAX.
This is great, except it was written for a single element with a unique ID. I'd like to be able to click on multiple elements on the same page and have them also be editable (e.g., I'd like to alter the script below so it works not with a single element, but with multiple elements of a particular class).
Here is my HTML:
<h2>Edit This</h2>
<p class="edit">This is some editable content</p>
<p class="edit">This is some more editable content</p>
<p class="edit">I could do this all day</p>
Here is the JS file I'm working with (I updated the script per Rex's answer below): This script is, unfortunately, not working - can anyone point me in the right direction?
Event.observe(window, 'load', init, false);
function init() {
makeEditable('edit');
}
function makeEditable(className) {
var editElements = document.getElementsByClassName(className);
for(var i=0;i<editElements.length;i++) {
Event.observe(editElements[i], 'click', function(){edit($(className))}, false);
Event.observe(editElements[i], 'mouseover', function(){showAsEditable($(className))}, false);
Event.observe(editElements[i], 'mouseout', function(){showAsEditable($(className), true)}, false);
}
}
function showAsEditable(obj, clear) {
if (!clear) {
Element.addClassName(obj, 'editable');
} else {
Element.removeClassName(obj, 'editable');
}
}
function edit(obj) {
Element.hide(obj);
var textarea ='<div id="' + obj.id + '_editor"><textarea cols="60" rows="4" name="' + obj.id + '" id="' + obj.id + '_edit">' + obj.innerHTML + '</textarea>';
var button = '<input type="button" value="SAVE" id="' + obj.id + '_save"/> OR <input type="button" value="CANCEL" id="' + obj.id + '_cancel"/></div>';
new Insertion.After(obj, textarea+button);
Event.observe(obj.id+'_save', 'click', function(){saveChanges(obj)}, false);
Event.observe(obj.id+'_cancel', 'click', function(){cleanUp(obj)}, false);
}
function cleanUp(obj, keepEditable) {
Element.remove(obj.id+'_editor');
Element.show(obj);
if (!keepEditable) showAsEditable(obj, true);
}
function saveChanges(obj) {
var new_content = escape($F(obj.id+'_edit'));
obj.preUpdate = obj.innerHTML // stow contents prior to saving in case of an error
obj.innerHTML = "Saving…";
cleanUp(obj, true);
var success = function(t){editComplete(t, obj);}
var failure = function(t){editFailed(t, obj);}
var url = 'http://portal.3roadsmedia.com/scripts/edit.php';
var pars = 'id=' + obj.id + '&content=' + new_content + '&pre=' + obj.preUpdate;
var myAjax = new Ajax.Request(url, {method:'post',
postBody:pars, onSuccess:success, onFailure:failure});
}
function editComplete(t, obj) {
obj.innerHTML = t.responseText;
showAsEditable(obj, true);
}
function editFailed(t, obj) {
obj.innerHTML = 'Sorry, the update failed.';
cleanUp(obj);
}
The Event.observe method currently attaches to a single element with the ID specified. You should change this to iterate over a collection of elements located by classname and attach to each of them. According to the Prototype documentation, you can provide an element object as the first parameter, instead of an ID.
Currently, id is a string:
function makeEditable(id) {
Event.observe(id, 'click', function(){edit($(id))}, false);
//...
Which means Event.observe is attaching to the click event of the element with the ID provided. You want to attach to all elements with a class. Try:
function makeEditable(className) {
var editElements = document.getElementsByClassName(className);
for(var i=0;i<editElements.length;i++) {
Event.observe(editElements[i], 'click', function()
//...
}
//...

Resources