Razor Nested WebGrid - asp.net-mvc

How do I have nested WebGrid with lot of formatting for each column. I can do a nested for-loop, but I need it basically for paging. Or is there any other better option?

Excuse the verbose data setup but this works...
#{
var data = Enumerable.Range(0, 10).Select(i => new { Index = i, SubItems = new object[] { new { A = "A" + i, B = "B" + (i * i) } } }).ToArray();
WebGrid topGrid = new WebGrid(data);
}
#topGrid.GetHtml(columns:
topGrid.Columns(
topGrid.Column("Index"),
topGrid.Column("SubItems", format: (item) =>
{
WebGrid subGrid = subGrid = new WebGrid(item.SubItems);
return subGrid.GetHtml(
columns: subGrid.Columns(
subGrid.Column("A"),
subGrid.Column("B")
)
);
})
)
)
Renders:
Of course you'll have to make sure in the GetHtml() method calls you give each grid (both top and sub) unique parameter names for paging/sorting or you'll end up with conflicts.

Related

IQueryable changed in foreach loop not displaying in view

I been trying to change the value of RouteAttr.RoutedForRole if it is equal to
SHead but I checked in run time and the query was not changed even though it went to the foreach loop and there were valid entries. I also tried adding the foreach loop in the view but it didn't change anything.
public ViewResult Index()
{
IQueryable<ServiceRequestViewModel> query;
query = from c in context.ServiceRequests
select new ServiceRequestViewModel
{
ServiceRequestId = c.ServiceRequestId,
ServiceDescription = c.ServiceDescription,
RequestNumber = c.RequestNumber,
Title = c.Title,
RouteAttr = c.RouteAttr,
LogAttr = c.LogAttr
};
foreach (var item in query)
{
if (item.RouteAttr.RoutedForRole == WorkflowRole.SHead)
{
item.RouteAttr.RoutedForRole = WorkflowRole.HRManager;
}
}
return View(query);
}
Below is my gridview.
#Html.Grid(Model).Columns(col =>
{
col.Add(o => o.ServiceRequestId)
.Encoded(false)
.Sanitized(false)
.Filterable(true)
.Titled("SRF No.")
.SetWidth(150)
.RenderValueAs(o => Html.ActionLink(o.RequestNumber, "Details", new { id = o.ServiceRequestId }));
col.Add(o => o.Title)
.Filterable(true)
.SetWidth(400)
.Titled("Title");
col.Add(o => o.LogAttr.CreatedBy)
.Filterable(true)
.Titled("Requestor");
col.Add(o => o.RouteAttr.RoutedForRole)
.Filterable(true)
.Titled("Status");
}).WithPaging(10).Sortable(true)
I've been told in the comments why it's not returning so now I want to know how to update an item in iqueryable and return it in view.
Just like what #Enigmativity said in the comments, I changed the query into an array.
var data = query.ToArray();
He suggested I return data but it didn't work since ServiceRequestViewModel requires an IQueryable type. So I changed it back to IQueryable type so I can return it to view.
var queryable = data.AsQueryable();

mvc controlling two dropdownlist to filter data

hello everyone I want to ask a question abuout mvc dropdownlist. I am trying to filter the datas to their places or their codes while dropdowns selected index changed. I can do it when I use one dropdown but when I use more than one dropdown I cannot get the results separately.
As you see in the picture I have two dropdownlist.
public ActionResult Index(int? id,int? ddwList)
{
Repository<Order> _ro = new Repository<Order>();
IEnumerable<SelectListItem> _orderSelectListItem = _ro.All().AsEnumerable().Select(s => new SelectListItem
{
Text = s.code,
Value = s.id.ToString()
});
ViewData["ddOrder"] = _orderSelectListItem;
Repository<Workshop> _rw = new Repository<Workshop>();
IEnumerable<SelectListItem> _workshopSelectListItem = _rw.All().AsEnumerable().Select(s => new SelectListItem
{
Text = s.name,
Value = s.id.ToString()
});
ViewData["ddwList"] = _workshopSelectListItem;
Repository<ClothShipment> _rcs = new Repository<ClothShipment>();
IEnumerable<MyClothShipment> _myClothShipment = null;
if (id != null)
{
int? idOrd = _rcs.Find(w => w.orderId == id).orderId;
//int? idWork = _rcs.Find(w => w.workshopId == id).workshopId;
if (idOrd != null)
{
_myClothShipment = _rcs.All().Where(w => w.orderId == id).Select(s => new MyClothShipment
{
id = s.id,
amount = s.amount,
orderName = s.order.code,
clothName = s.clothList.name,
workshopName = s.workshop.name,
shipDate = s.shipDate
});
}
//else if(idWork != null){
// _myClothShipment = _rcs.All().Where(w => w.workshopId == id).Select(s => new MyClothShipment
// {
// id = s.id,
// amount = s.amount,
// orderName = s.order.code,
// clothName = s.clothList.name,
// workshopName = s.workshop.name,
// shipDate = s.shipDate
// });
//}
}
else {
_myClothShipment = _rcs.All().Select(s => new MyClothShipment
{
id = s.id,
amount = s.amount,
orderName = s.order.code,
clothName = s.clothList.name,
workshopName = s.workshop.name,
shipDate = s.shipDate
});
}
return View(_myClothShipment);
}
my view is here
<div id="sample_editable_2_length" class="dataTables_length">
<label>
#Html.DropDownList("ddwList",(IEnumerable<SelectListItem>)ViewData["ddwList"],"Atölye Seçiniz",new {#id="StateDropDown1", #class = "span15 chosen"})
</label>
</div>
my view is here
<div id="sample_editable_2_length" class="dataTables_length">
<label>
#Html.DropDownList("ddwList",(IEnumerable<SelectListItem>)ViewData["ddwList"],"Atölye Seçiniz",new {#id="StateDropDown1", #class = "span15 chosen"})
</label>
</div>
<div id="sample_editable_1_length" class="dataTables_length">
<label>
#*<select class="m-wrap small" name="sample_editable_1_length" size="1" aria-controls="sample_editable_1">
</select>*#
#Html.DropDownList("ddOrder",(IEnumerable<SelectListItem>)ViewData["ddOrder"],"Sipariş Kodu Seçiniz",new {#id="StateDropDown", #class = "span15 chosen"})
</label>
</div>
and here is my script code
$("#StateDropDown").change(function (e) {
var controllerName = '#ViewContext.RouteData.Values["Controller"].ToString()';
var actionName = '#ViewContext.RouteData.Values["Action"].ToString()';
var _id = $("#StateDropDown").val();
var _url = "/" + controllerName + "/" + actionName + "/" + _id;
window.location.href =_url
});
$("#StateDropDown1").change(function (e) {
var controllerName = '#ViewContext.RouteData.Values["Controller"].ToString()';
var actionName = '#ViewContext.RouteData.Values["Action"].ToString()';
var _id = $("#StateDropDown1").val();
var _url = "/" + controllerName + "/" + actionName + "/" + _id;
window.location.href = _url
});
I am filling the dropdowns when on page load from database and getting all the data to show with dropdowns I want to filter the data that shown... And with this code one of my dropdown works I am taking the id of selected item (Index(int? id)) in here but when I try to use both of them separately it doesnt work how can I make both of them work. What should I do ? The second parameter always comes null or if I use different parameter except "id" it is again coming null ? and also I tried to take parameter as string but it also came null... Thank you for your helps.
To explain what your code is doing:
When your select a value from your first select, you are passing its value to the Index method (e.g. /Index/1) so the value of parameter id is 1 but no value has been passed to parameter ddwList so it is null. When you select a value from the second select you are passing its value to the index method (e.d. /Index/5) so the value of parameter id is 5 but no value has been passed to parameter ddwList so again it is null.
Assuming you want to display a table based on the selections of both selects, then you need to construct the url as /Index?id=1&ddwList=5. Therefore, remove the change events from your selects and add a button that constructs the query in its click event. However, the way you are doing this is reloading the whole page each time. I suggest you consider loading the table from a partial view using a jQuery.get() method to avoid a complete page load each time. For example
public ActionResult Index()
{
// Build the 2 select lists only
return View();
}
Index view
// Add the two #Html.DropdownFor()...
<button id="LoadTable">Load Table</button>
<div id="TablePlaceholder"></div>
and the script
$('#LoadTable').click(function() {
var id1 = // the value of the first select
var id2 = // the value of your second select
$.get('#Url.Action("Table")', { id: id1, ddwList: id2 }, function(data) {
$('#TablePlaceHolder').html(data);
});
}
and the partial result
public ActionResult Table(int? id, int? ddwList)
{
// Build your table view based on values of id and ddwList
return PartialView();
}
If I understand this, you are able to use one of the drop downs at a time and that drop down successfully sends it's value to your controller while the other drop down always sends null. If that's the case, that is working correctly with the way you made your dropdownlists with an option label.
As explained in the msdn documentation:http://msdn.microsoft.com/en-us/library/dd492256(v=vs.118).aspx
optionLabel Type: System.String The text for a default empty item.
This parameter can be null.
So if you want both of the drop down lists to be usable at the same time, you'll need to remove the events for .change and add a form with those dropdowns AND a submit button within to use both values at the same time.
OR
Do not use an option label meaning the first option of the dropDownList will be used as it's initial/default value. Here is a link to the msdn docs showing the different ways to format the Html.DropDownList helper: http://msdn.microsoft.com/en-us/library/system.web.mvc.html.selectextensions.dropdownlist(v=vs.118).aspx
I hope I understood you correctly!

Webgrid not refreshing after delete MVC

I am deleting a row in my webgrid , and redirecting to my GET index as normal after delete.
However as expected my view is showing the old deleted row still in the grid. I know this is by design and i should be able to set the Modelstate to clear , or remove the item individually to achieve this :
ModelState.Remove("id");
Or
foreach (var key in ModelState.Keys.Where(m => m.StartsWith("id")).ToList())
ModelState.Remove(key);
Or
ModelState.Remove("Applicant.ApplicantId");
Or even :
ModelState.Clear()
However none have working for me.
Here is my delete method (simplified -all error handling and non necessary code removed )
public ActionResult DeleteApplicant(int id)
{
var q =
(from a in context.Applicants
where a.ApplicantId == id
select a).Single<Applicant>();
q.ApplicantDocuments.ToList().ForEach(r => context.ApplicantDocuments.Remove(r));
context.Applicants.Remove(q);
context.SaveChanges();
foreach (var key in ModelState.Keys.Where(m => m.StartsWith("id")).ToList())
ModelState.Remove(key);
TempData["SuccessMessage"] = "Successfully deleted row.";
return RedirectToAction("Index");
}
And here is my call to my delete method from the view :
, grid.Column(format: (item) => #Html.ActionLink("Delete", "DeleteApplicant",
new { id = item.ApplicantId }, new { #class = "delete-link" }), style: "DeleteButton")
I have looked at various posts on stackoverflow etc but none seem to solve my issue : MVC 3 The View is not being refreshed after model submit
I have tried also tried re-directing to the Index action via jquery and calling the delete controller action with ajaxOptions , but this hasn't worked either.
, grid.Column(format: (item) => #Ajax.ActionLink("Delete", "DeleteApplicant",
new { id = item.ApplicantId },
new AjaxOptions { HttpMethod = "GET" , OnSuccess= "reloadGrid" }))
And adding a little script to call the home index:
function reloadGrid() {
var pathArray = window.location.pathname.split('/');
var segment_1 = pathArray[1];
var newURL = window.location.protocol + "//" + window.location.host + "/" + segment_1 + "/Home/Index";
$.ajax(
{
type: "GET",
url: newURL,
data: "{}",
cache: false,
dataType: "html",
success: function (data)
{ $().html(data); }
})
}
I am obviously doing something wrong. Is there any other way i can force a refresh of the page in a different way or can anyone spot anything obviously wrong in my current approach. Thanks in advance.
Note: I do have my webgrid markup on my Index view and not in a separate partial viewbut i don't think this should be causing this?
Update :
Here is up GET method as requested: (for context, the filters object is used when search button is clicked and page is posted back ,no filters applied in initial page load)
public ActionResult Index(string page)
{
/////Peform paging initiation
int pageIndex = 0;
if (!string.IsNullOrEmpty(page))
{
pageIndex = int.Parse(page) - 1;
}
////initialize total record count and filter results object
int totalRecordCount = 0;
var filters = new ApplicantSearchFilter();
////Perform search with paging
var applicants = this.GetApplicantsPaging(filters ,pageIndex, out totalRecordCount);
////Return view type
var data = new ApplicantViewModel()
{
Filters =filters ,
TotalRecordCount = totalRecordCount,
ApplicantReportLists = applicants,
PageIndex = pageIndex,
};
////return Index view passing data result to build it
return this.View("Index", data);
}
}
I have finally managed to get this working.
If it helps anyone else here is what i did in the end. I used a solution posted by Dror here : How to achieve edit and delete on Webgrid of MVC3 Razor? . Where he turned a GET delete call from the webgrid into a post. Not exactly what i was looking for but it works.
Does anyone have a better , alternative solution ?
View Code :
added Function :
#functions{
string Delete(dynamic p)
{
string actionController = Url.Action("Delete", "Admin", new {id=p.AccountId});
return "<form style='display:inline;' method='post' action='" + actionController + "'><input type='submit' value='Delete' onclick=\"return confirm('Are you sure?')\"/></form>";
}
}
and changed my delete call in my webgrid to :
grid.Column(header: "", format: p => Html.Raw(Delete(p)))
In the Controller:
[HttpPost]
public ActionResult Delete(int id)
{
PerformDelete(id);
return RedirectToAction("Index");
}

Webgrid and formatting DateTime value when grid columns are built in model

I am creating a collection of WebGridColumns, adding them to my model and passing that to my razor page. I am doing this because there are a variable number of columns that are determined on the fly.
This works well, but now I need to format datetime values to short date and am not sure how this can be accomplished when creating the collection of webgridcolumns.
foreach (var datetimeitem in cols)
{
columns.Add(new WebGridColumn
{
ColumnName = datetimeitem,
Header = "MyHeader",
Format = **format item here**;
});
}
Any ideas?
J
Does your setup look something like this?
#{
var cols = new[] { "FirstDate", "SecondDate" };
var columns = new List<WebGridColumn>();
var grid = new WebGrid(new[]
{
new Entity { FirstDate = DateTime.Now, SecondDate = DateTime.Now },
new Entity { FirstDate = DateTime.MinValue, SecondDate = DateTime.MinValue }
});
}
If so, you can try
#foreach (var datetimeitem in cols)
{
columns.Add(new WebGridColumn
{
ColumnName = datetimeitem,
Header = "MyHeader",
// If item is already a date time
Format = m => m[datetimeitem].ToShortDateString()
// If you need to parse item as date time first
// Format = m => DateTime.Parse(m[datetimeitem]).ToShortDateString()
});
}
#grid.GetHtml(columns: columns)

How to sync Kendo UI mvc Grid with Kendo Mvc Autocomplete

I am working asp.net mvc witk Kendo UI MVC Tools. I am trying to display list of records in a Kendo UI Mvc Grid. and i have one kendo ui autoComplete Textbox when i type a letter it shows corresponding field record that matches the criteria will be showed like a dropdown. Now i want to sync the autocomplete textbox with kendo ui mvc grid. that means when i type a letter records that matches criteria should display in the grid. I have tried with change event but it doesnt seems work for me.
#(Html.Kendo().AutoComplete().Events(c=>c.Change("GridChange"))
.Name("txtSearchItem")
.Filter("startswith")
.DataTextField("xxx")
.Value(ViewBag.SearchValue)
.BindTo((List<MyRecords>)Model).HtmlAttributes(new { #class = "required", style = "font-size:19px;width:150px;", onkeypress = "return isNumericKey(event);" })
)
please guide me.
First, create a grid with a HeaderTemplate to make a combobox that behaves as an autocomplete as well.
#( Html.Kendo().Grid(Model)
.Name("Grid")
.ClientDetailTemplateId("inventoryTemplate")
.DataSource(ds => ds.Ajax()
.Read(r => r.Action("Read", "Home"))
)
.Columns(columns =>
{
columns.Bound(p => p.Item).Width(10)
.Filterable(false).Sortable(false).HeaderTemplate(#<text>
#(Html.Kendo().ComboBox()
.DataValueField("Items")
.Placeholder("Items")
.DataTextField("Items")
.Name("Items")
.DataSource(ds => ds.Read(rea => rea.Action("ListOfItems", "Home")))
.Events(ev => ev.Change("onComboListCodeChange"))
)
</text>);
})
)
Now create this method will get an array from Dictionary of filters, you will need it later.
function getArrayFromDic(dic) {
var arr = new Array();
arr = $.map(dic, function (n, i) {
return { field: n.field, operator: n.operator, value: n.value };
});
return arr;
}
This function will get a dictionary that represents the filters available on the grid. If there is more than one filter.
function getFilterDic() {
var grid = $('#Grid').data('kendoGrid');
var filtersDicTemp = {
};
if (grid.dataSource._filter) {
$.each(grid.dataSource._filter.filters, function (index, value) {
filtersDicTemp[value.field] =
{
field: value.field, operator: value.operator, value: value.value
}
});
}
return filtersDicTemp;
}
This will be called each time you change the value of the filter Autocomplete combobox in this case.
There is a method on the kendo.data.DataSource called filter, where you can pass an array of filters.
function onComboListCodeChange(e) {
var grid = $('#Grid').data('kendoGrid');
var filtersDic = getFilterDic();
if (this.value() && this.value() != 'All') {
if (this.value() != 'Items' && this.value() != '') {
filtersDic["Items"] =
{
field: "Items", operator: "startswith", value: this.value()
}
}
}
else {
if (filtersDic["Items"]) {
delete filtersDic["Items"];
}
}
var filters = getArrayFromDic(filtersDic);
grid.dataSource.filter(
filters
);
}
Hope it helped!
Use the approach from here http://demos.kendoui.com/web/grid/toolbar-template.html
The difference would be that you will use AutoComplete (which is almost the same) and you do not need to put it inside the Toolbar via the Toolbar template.

Resources