KendoUI grid for ASP.net MVC - javascript

I have a requirement of a search page in which I am using KendoUI grid to display the search result.
I have a textbox and button and if text is entered and on click event of button I hace to display the grid with the list of users matching to search result.
I am using ASP.net MVC and KENDOUI grid.
My View:
The search box and button:
<div id="SearchSection">
<input type="text" id="txtSearch" class="k-textbox"/>
<button class="k-button"id="btnSearch" style="width:150px">Search</button>
</div>
The KendoUI grid
<div id="ADUserSection">
<div id="ADSearchedUser">
List of users in Active directory:
<div id="ADuserGrid">
#(Html.Kendo().Grid<ADUser>()
.Name("kADUser")
.Columns(columns =>
{
columns.Bound(p => p.UserLoginName);
columns.Bound(p => p.UserDisplayName);
})
.AutoBind(false)
.DataSource(ds =>
{
ds.Ajax()
.Read(read =>
{
read.Action("GetADUser", "ManageUsers")
.Data("AdditionalData");
});
})
)
)
</div>
</div>
My JavaScript Function:
<script>
$(document).ready(function () {
$("#ADUserSection").fadeOut(0);
$("#AvailableUserRoleSection").fadeIn()
});
var enterTest
$("#btnSearch").click(function () {
debugger;
enterTest = $("#txtSearch").val().trim();
if (enterTest == "") {
$("#ADUserSection").fadeOut();
}
else {
AdditionalData();
$("#ADUserSection").fadeIn();
var grid = $("kADUser").data("kendoGrid").dataSource.read({ searchText: enterTest });
//**Breaks at this Point**//
}
});
function AdditionalData() {
//$("#ADuserGrid").empty();
$("#ADuserGrid").fadeIn();
return {
searchText: enterTest
}
}
My Controller Action
public JsonResult GetADUser([DataSourceRequest] DataSourceRequest request, string searchText)
{
viewmodel.searchedADUser = model.GetUserFromAD(searchText);
return Json(viewmodel.searchedADUser.ToList().ToDataSourceResult(request), JsonRequestBehavior.AllowGet);
}
On the button click event in javascript when I attach the grid to event I get the error the datasource read is not recognised.
Exact error is:
JavaScript runtime error: Unable to get property 'dataSource' of undefined or null reference
Please help me in that. any idea please share or if I am doing anything wrong in my above code please point out.
I am very new to KendoUi and MVC so please elaborate n yur explanation.
I got the above problem becosue of missing # before the grid name.
But Now I habe one more issue, even though I am follwing all the proper step.
In my above AdditionalData javascript function my parameter is not getting set set in the paaremeter
function AdditionalData() {
//$("#ADuserGrid").empty();
$("#ADuserGrid").fadeIn();
return {
searchText: enterTest
}
}
This searchText is not getting set even tough I am getting value in enterTest.
Any help will be of very great use. I am really stuck in this.

You're trying to access your grid with:
var grid = $("kADUser").data("kendoGrid");
$("kADUser") won't find any elements, because it's looking for a kADUser tag, and the .data() of an empty jQuery set is null.
As a result, when you try to access grid.dataSource, grid is "undefined or null" (which is what the error is telling you).
You should be using an id selector:
var grid = $("#kADUser").data("kendoGrid");
In general, I'd suggest to avoid compound statements and keep it at one statement per line. This will make debugging much easier.

Related

How to dynamically show a button based on conditions where the value being used to determine what button to show isn't known until it's clicked?

If someone has a better title feel free to edit. I inherited a project from a developer who is leaving the company and I'm scratching my head trying to find a solution to a problem the existing code provides.
Code from the view:
<div>
<table class="table">
<tr>
<th class="border-bottom border-top-0">Action</th>
</tr>
#foreach (Step actionItem in Model.Steps)
{
#if (actionItem.HasRun == false)
{
<tr class="border-top-0">
<td>
#if (actionItem.ReturnsInfo == true)
{
<input type="button" value="Run Check" onclick="loadProcessingFeedbackPartial('#actionItem.StepID', '#Model.Client.DatabaseConnectionString' )" />
}
else
{
<input type="submit" value="Run Check" name="btnRunStoredProcedure" asp-action="CallStepStoredProcedure" asp-route-StepID="#actionItem.StepID" asp-route-StepCompleted="#actionItem.HasRun" />
}
</td>
</tr>
break;
}
}
</table>
</div>
Javascript being called from the button click:
<script type="text/javascript">
function loadProcessingFeedbackPartial(x, y) {
var url = '#Url.Action("ViewProcessingFeedBackPartial", "Client")';
var stepId = x;
var databaseConnectionString = y;
$("#processingFeedbackPartialDiv").load(url, { stepId, databaseConnectionString },
function () {
$("#confirmButton").removeAttr("style");
});
}
</script>
Controller action:
public IActionResult ViewProcessingFeedBackPartial(int StepId, string DatabaseConnectionString)
{
FeedbackDetails feedbackDetails = new FeedbackDetails();
feedbackDetails.Data = _clientProcessingService.GetProcessingFeedbackDetails(StepId, DatabaseConnectionString);
return PartialView("_ViewFeedback", feedbackDetails);
}
The button in the view has an Onclick event that goes to the Javascript function, which loads a partial view with the data from the controller calling a service method. Here's where the problem is. If no rows are returned, I want to bypass the partial being drawn entirely.
So I changed the controller action around a bit to include a condition where if the feedbackDetails.Data has 0 rows to just call a different method from the service, process as normal, but return the View instead of a partial.
public IActionResult ViewProcessingFeedBackPartial(int StepId, string DatabaseConnectionString, int ClientId)
{
FeedbackDetails feedbackDetails = new FeedbackDetails();
feedbackDetails.Data = _clientProcessingService.GetProcessingFeedbackDetails(StepId, DatabaseConnectionString);
if(feedbackDetails.Data.Rows.Count == 0)
{
_clientProcessingService.RunProcessStepConfirmation(DatabaseConnectionString, StepId, ClientId, "No information returned, automatically proceeding to next step.");
return RedirectToAction("Processing", new { Id = ClientId });
}
return PartialView("_ViewFeedback", feedbackDetails);
}
This "worked", except since in the view it's being called in a Javascript function that loads a partial regardless, the view is returned inside that partial instead of the view being returned.
But I'm unsure how to fix this because without first clicking the button and attempting to populate that collection with data, I don't know if it's empty (and skip the partial) or it has rows (and draw the partial).
I attempted creating an intermediary controller action that returns a boolean and attempted to use the result of that inside the javascript function to either draw the partial or skip it based on the bool, but I'm not really the greatest at Javascript so I wasn't able to get it to work.
I'm unsure if the way to solve this involves creating logic that displays multiple buttons that route to different controller actions or javascript functions or just handling it all via Javascript somehow.
What would be a good way to go about solving this?
#Mkalafut, your jQuery function is loading the controller result directly into "#processingFeedbackPartialDiv" regardless of the result received. Better to pull this initially into a variable, then add some simple logic to decide what to do next. Potentially the controller can help by returning a null result that is easy to identify.
e.g.
$.get("url", { stepId, databaseConnectionString }, function (data) {
var result = data;
// Some example conditional logic - adjust as required
if (result != null){
$("#processingFeedbackPartialDiv").html(result);
$("#confirmButton").removeAttr("style");
}
});
Remember, jQuery load & get are both just shorthand functions for ajax, so if needs be you can customise the code further to get the flexibility you need.
https://api.jquery.com/jQuery.get/
https://api.jquery.com/load/

Populate data from groovy controller in pop up

I have a gsp page with a delete button for each row of a table. On the button click I want a pop up which tells the consequences of the delete. These consequences depends on the data present in the row and a few other constraints known to the grails service which is called from the grails controller associated to the gsp page. If the user confirms these consequences the row should be deleted from the table, else the table remains unchanged.
How should i go about to achieve this behavior?
Currently, I have in my gsp
<tr>
<td>name</td>
<td>parentName</td>
<td>data</td>
<td>
<g:link action="deleteRow" params="${[name: row.name, parentName: row.parentName]}">
<button class="deleteSnapshot">Delete</button>
</g:link>
</td>
</tr>
and in my .js file
$(document).on('click', ':button', function (e) {
var btn = $(e.target);
btn.attr("disabled", "disabled"); // disable button
alert('getting deletion details');
//var deletionDetails -->not sure how to get these
//var deletionDetails will get data from controller action:"getDetails"
if (confirm('/*print deletion details and ask*/ Do you want to proceed?')) {
alert('will delete')
return true
}
else {
btn.removeAttr("disabled"); // enable button
return false
}
});
and in my controller
class DeleteController{
DeleteService deleteService
def index() {
[list:deleteService.getTableList()]
}
def getDeletionDetails(string name, String parentName){
return deleteService.getDetails(name,parentName)
}
def deleteRow(String name, String parentName){
service.deleteRow(name, parentName)
redirect controller:"DeleteController", action:"index"
}
}
I know the deletion works fine, because it works even with in the current state. Just that the confirmation box asks Do you want to proceed, without displaying the details.
Any help on how i could achieve what I am looking for will be appreciated.
P.S. I am new to stackoverflow, so if i missed out on certain convention do let me know.
Thanks in advance.
I can think of two ways of doing it:
The first one is using ajax to both get deletion details and delete the row
Assuming that deleteService.getDetails(name, parentName) returns a String,
first you need to change an getDeletionDetails action so it renders the response:
def getDeletionDetails(String name, String parentName){
render deleteService.getDetails(name, parentName)
}
and change g:link-s to buttons in gsp:
<button data-name="${row.name}" data-parent-name="${row.parentName}">
Delete
</button>
In your .js then put:
$(document).on('click', ':button', function (e) {
var btn = $(e.target);
btn.attr("disabled", "disabled"); // disable button
var name = btn.data('name');
var parentName = btn.data('parentName');
$.ajax({
url: "/delete/getDeletionDetails",
data: {
name: name,
parentName: parentName
},
success: function (data) {
if (confirm(data + '\nDo you want to proceed?')) {
$.ajax({
url: '/delete/deleteRow',
data: {
name: name,
parentName: parentName
},
success: function (d) {
console.log("Success: " + d);
}
});
} else {
btn.removeAttr("disabled"); // enable button
}
}
});
});
What this code does is it sends an ajax call to /delete/getDeletionDetails, then uses its response (rendered by getDeletionDetails action in DeleteController) to show a confirmation alert. If user confirms the question, another ajax call is sent - now to deleteRow action of DeleteController - with parameters taken from data attributes of clicked button. If user cancels - nothing happens, except for reenabling a button.
Your deleteRow should only change the return statement - it also must render the response:
def deleteRow(String name, String parentName){
service.deleteRow(name, parentName)
render "You deleted an item $name - $parentName."
}
You don't need redirect here, because - thanks to using ajax - user will never leave delete/index. You can just display some kind of confirmation on page after successful ajax call.
The second option is to put deletion details in hidden fields or data- attributes in each row and then just retrieve them in js:
You can create a method getDeletionDetails() in row's domain class (presumably Row) that returns the details (using services in domain classes is not perfect, but is should work ok if the service is not very complex). Then, in your .gsp place:
<td>
<g:link action="deleteRow" params="${[name: row.name, parentName: row.parentName]}">
<button class="deleteSnapshot" data-details="${row.deletionDetails}">Delete</button>
</g:link>
</td>
You should then be able to get details in .js like this:
var deletionDetails = btn.data('details');

asp.net - Manipulate Page Content

I was wondering if anyone could explain how to manipulate content for various sections of a page depending on if a button is clicked. I think what I am looking for is similar to an include in php. I wasnt sure if asp.net had a way to do the same (partial view?) or if bootstrap/jquery is the way to go. I've included a .png to illustrate what I am trying to do.
I would like section b's content to change based on what button is selected in section A. While not necessarily relevant to this question.. I then would like various user inputs in section B to manipulate existing content in section C.
In your controller, have an action that returns a PartialView:
public PartialViewResult MyPartial(string someText)
{
var model = new MyPartialModel {SomeStuff = someText};
return PartialView(model);
}
Create the model and partial view as you would any other:
public class MyPartialModel
{
public string SomeStuff { get; set; }
}
Partial View:
#model ExampleApp.Models.MyPartialModel
#Html.TextBoxFor(m => m.SomeStuff)
Then on your page you can load in your partial via ajax with jQuery:
<div>
<button type="button" id="load-partial">Load The Partial!</button>
</div>
<div id="section-b"></div>
#section Scripts{
<script>
$(document).ready(function () {
$('#load-partial').click(function () {
$.get('MyPartial', { sometext: "Hello!" }).done(function (data) {
$('#section-b').html(data);
});
});
});
</script>
}
Edit to answer comment:
If you don't want to instantiate a new model in the controller each time, you can pass the model (more or less) directly from the view.
In you controller, have a very simple action that accepts a model as a parameter and returns the partial view. Note the HttpPost attribute.
[HttpPost]
public PartialViewResult MyPartial(MyPartialModel model)
{
return PartialView(model);
}
The model's got more than one property this time:
public class MyPartialModel
{
public string Name { get; set; }
public int Age { get; set; }
}
The partial's pretty much the same, except it now displays the new properties of the model.
#model MVCPlayGround.Models.MyPartialModel
#Html.TextBoxFor(m => m.Name)
#Html.TextBoxFor(m => m.Age)
The jquery on the main page/view is very also similar, but uses POST instead of GET.
// these could be anything, from control on the page, or whatever
var name = "James";
var age = 30;
$(document).ready(function () {
$('#load-partial').click(function () {
// note that Name and the Age are the names of the properties in our model
$.post('MyPartial', { Name: name, Age: age }).done(function (data) {
$('#section-b').html(data);
});
});
});
This works because when data transmitted via POST, it's treated as form data, and when the controller's deciding which action to use it'll look at the parameters for the actions, and compare them to the form data available. The MyPartialModel contains properties that match the form data, so it chooses that action. There are other subtle rules, but that's basically it. Behind the scenes it'll still be instantiating a model in the controller, it's just in the framework, not in the code you've written.
Another edit
Having just re-read your comment I don't think I've answered it fully.
If you want to save the changes you've made in a partial view to the main view, have some hidden fields in the main view to hold this data.
<input type="hidden" id="name-holder" />
<input type="hidden" id="age-holder" />
And then when you want to store a value to them, just set the values with jquery:
$('#some-save-button-maybe').click(function(){
$('#name-holder').val($('id-of-name-on-partial').val());
$('#age-holder').val($('id-of-age-on-partial').val());
});
When you click on a the button to show a partial, send the appropriate data to the controller to render in the partial:
$('#load-partial').click(function () {
$.post('MyPartial', { Name: $('#name-holder').val(), Age: $('#age-holder').val() }).done(function (data) {
$('#section-b').html(data);
});
});
Hopefully that's what you need...
Yes there are partial views in MVC, and they are usually belong in the Views/Shared folder of your project and are prefixed with a _ (i.e. _MyPartial.cshtml.
As #AdamHeeg pointed out in the comments, there are many tutorials on the web about this kind of setup and many different ways to achieve what you are after.
Here is roughly how I might tackle it...
HTML
<nav>
#Html.ActionLink("Button 1", "GetSectionB")
</nav>
<section id="sectionB">
<!-- Content here -->
</section>
JavaScript
$('nav a').on('click', function (e) {
e.preventDefault();
$.get(this.href, function (html) {
$('#sectionB').html(html);
});
});
Controller
public PartialViewResult GetSectionB()
{
var vm = new MyViewModel();
//do stuff
return PartialView("_SectionB", vm);
}

Partial view with ajax and jQuery UI.Dialog

I am using a standard MVC4 EF5 setup and have a standard view which loads data from the db onto a table.
At the start of the table I have a column for each record with an Add button. The functionality I want is to click the button, popup a model dialog box with a form and add something to the item in the grid that was clicked (a 1 to many).
Lets say I have a list of vans available shown in the list. And when I click the add button beside the particular van where I want to add a passenger, I want a popup to show that allows me to type the details of the passenger so they can be assigned to that van.
I think I am over complicating this. But my brain is fried. I tried partial views with ajax. I tried jQuery UI.Dialog. Im just lost. I am trying to figure out how to find the id of the record I clicked (given the buttons are all generated by a for each loop in the view as normal and numbering them 1 to X does not tell me the id of the record I clicked). So even if I get the popup showing, I wont know which van to assign the passenger to.
If your woundering where the passenger list is coming from, its another table. And effectively any passenger can be assigned to any van. Its hypothetical.
Im actually working on a document generator and so there is a many to many relationship between document parts and documents (a given document part, can appear or belong to many documents, and a document can contain many document parts). I know its messy, this is why I did not want to use the real example.
I'm thinking its maybe an easy enough problem to solve but I have been at it since Friday and the brain left home!
Edit: Adding Code:
Here is the main view: The main problem I am having with this is the way the grid is constructed. I think its partially razor, partially html, partially html helper, and partially javascript. I don't know which part is which, but I just need to get a popup to show for each button in the table, and to have an id I can assign values to. I cant figure out how to do it here.
Html.Grid(dbaccess().Where(c => something = something
).Select(o => new
{
Name = o.Name,
Comment = o.Comment,
Status = o.Status,
}
, "grdConfiguration", 0, htmlRowClass: (p) => (row++ % 2 != 0) ? "" : "oddRow"
, columns: new[]{
//THIS IS THE PROBLEM LINE BELOW .... It shows a button in the table, but...
//how do I make it unique. Is it even necessary to do so.
// How do I get the ID of the record at this location when this button is pressed.
//This is the code as originally posted: For context
new Helpers.GridColumn(value: (a) => "<input type=\"button\" class=\"btn\" id=\"BtnHello\" value=\"Add\" />"),
//for some reason when I try the following alternative as suggest by the answers so far - it doesn't work.
new Helpers.GridColumn(value: (a) => "<input type=\"button\" class=\"btn\" data-ThisId=\"#model.SomeId\" value=\"Add\" />"),
//THIS IS THE PROBLEM LINE ABOVE....
there is more columns but this button calls the jQuery...
On this view I also have some Div tags in which to load the partial... I can actually get this to popup. But that's about all I can do. And only when I click the first button in the table.
<div id='SomePopUp' style='display: none;'>
//#using (Html.BeginForm())
//{
// <div>
// <span class="display-label">Quantity: </span>
// <span class="display-field"><input type="text" id="txtQuantity" /></span>
// </div>
// <div>
// <span class="display-label">Comments: </span>
// <span class="display-field"><textarea rows="7"></textarea></span>
// </div>
//}
</div>
I also have a script section on this view with the code for the popup:
<script type="text/javascript">
$("#BtnHello").click(function ()
{
$("#SomePopUp").dialog(
{
resizable: false,
height: 400,
width: 400,
modal: true,
title:"add to {Some ID}:", //I want the id to show here so I know I have the record I want.
buttons:
{
Submit : function ()
{
$(this).dialog('Some Text');
},
Cancel: function ()
{
$(this).dialog('close');
}
}
});
});
</script>
I have a controller:
[HttpGet]
public ActionResult AddExtra(int id)
{
//Fairly sure I should be doing something with this id, but how do I get it from the button.
return PartialView();
}
And for the partial view I have
#model CM.ViewModels.AddExtraPackagesViewModel
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h3>Add Something</h3>
</div>
<div>
//I was using ajax here...
#*#using (Ajax.BeginForm("DoSomething", "Something", FormMethod.Post,
new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
UpdateTargetId = "list-of-something"
}))
{
<div class="modal-body">
#Html.TextBoxFor(x => x.Quantity);
#Html.TextAreaFor(x => x.Comment);
</div>
<div class="modal-footer">
<button class="btn btn-success" id="submit">Save</button>
Close
</div>
}
</div>
I made a little view model too but...
public class AddExtraViewModel
{
public int Id { get; set; }
public string Quantity { get; set; }
public string Comment { get; set; }
}
I apologise if this is all over the place but I did not write the original code. There were about 7 other programmers here before me and I'm just struggling to get through it.
Any help would be appreciated.
I think you would want something like this (using jQuery and jQuery UI):
Controller:
public ActionResult SomeAction(int id) {
return View(new YourModel { Id = id });
}
Partial View:
#model YourProject.Models.YourModel
// Partial view content e.g. the form etc.
Your view:
/<!-- html etc. -->
<table>
<tr>
<td>Add</td>
</tr>
</table>
<script>
$(function(){
$(".add-button").click(function(){
var options = {
autoOpen: false
}
var dialog = $("<div>").dialog(options);
var id = $(this).data("theId");
dialog.load("the/url/to/the/controller/action", { id: id }, function(){
dialog.dialog("open");
dialog.find("form").submit(function(){
// do stuff
dialog.remove();
return false;
});
});
});
});
</script>
if you are building buttons in a forloop you don't want to define an id on the button. Duplicate id's on a view can cause lots of issues. Use a class on the buttons instead to trigger off of and use $(this) in your script to get details of the button that was clicked. To access buttons on a partial or on items that are added to your page after page load you need to tie the click event for that button to the document like this
$(document).on("click", ".btnDetails", function(){
//your script here
});
The other example uses "this" and shows how you can pass the id of the clicked button back to the controller. The controller will need to be a little different though
public PartialViewResult PopulatePartial(int ID){
var Model = //populate your model based on the passed id
return PartialView("PartialViewName", Model);
}

jquery autocomplete in variable length list

Trying to figure out how to do this, using Sanderson begincollectionitems method, and would like to use autocomplete with a field in each row.
I think I see how to add a row with an autocomplete, just not sure the approach for existing rows rendered with guid.
Each row has an of field that the user can optionally point to a record in another table. Each autocomplete would need to work on the html element idfield_guid.
I'm imagining using jquery to enumerate the elements and add the autocomplete to each one with the target being the unique of field for that row. Another thought is a regex that maybe let you enumerate the fields and add autocomplete for each in a loop where the unique field id is handled automatically.
Does that sound reasonable or can you suggest the right way? Also is there a reasonable limit to how many autocomplete on a page? Thanks for any suggestions!
Edit, here's what I have after the help. data-jsonurl is apparently not being picked up by jquery as it is doing the html request to the url of the main page.
$(document).ready(function () {
var options = {
source: function(request, response) {
$.getJSON($(this).data("jsonurl"), request, function (return_data) {
response(return_data.itemList);
});
},
minLength: 2
};
$('.ac').autocomplete(options);
});
<%= Html.TextBoxFor(
x => x.AssetId,
new {
#class = "ac",
data_jsonurl = Url.Action("AssetSerialSearch", "WoTran", new { q = Model.AssetId })
})
%>
And the emitted html look okay to me:
<input class="ac" data-jsonurl="/WoTran/AssetSerialSearch?q=2657" id="WoTransViewModel_f32dedbb-c75d-4029-a49b-253845df8541__AssetId" name="WoTransViewModel[f32dedbb-c75d-4029-a49b-253845df8541].AssetId" type="text" value="2657" />
The controller is not a factor yet, in firebug I get a request like this:
http://localhost:58182/WoReceipt/Details/undefined?term=266&_=1312892089948
What seems to be happening is that the $(this) is not returning the html element but instead the jquery autocomplete widget object. If I drill into the properties in firebug under the 'element' I eventually do see the data-jsonurl but it is not a property of $(this). Here is console.log($this):
You could use the jQuery UI Autocomplete plugin. Simply apply some know class to all fields that require an autocomplete functionality as well as an additional HTML5 data-url attribute to indicate the foreign key:
<%= Html.TextBoxFor(
x => x.Name,
new {
#class = "ac",
data_url = Url.Action("autocomplete", new { fk = Model.FK })
})
%>
and then attach the plugin:
var options = {
source: function(request, response) {
$.getJSON($(this).data('url'), request, function(return_data) {
response(return_data.suggestions);
});
},
minLength: 2
});
$('.ac').autocomplete(options);
and finally we could have a controller action taking two arguments (term and fk) which will return a JSON array of suggestions for the given term and foreign key.
public ActionResult AutoComplete(string term, string fk)
{
// TODO: based on the search term and the foreign key generate an array of suggestions
var suggestions = new[]
{
new { label = "suggestion 1", value = "suggestion 1" },
new { label = "suggestion 2", value = "suggestion 2" },
new { label = "suggestion 3", value = "suggestion 3" },
};
return Json(suggestions, JsonRequestBehavior.AllowGet);
}
You should also attach the autocomplete plugin for newly added rows.

Categories

Resources