Kendo UI Grid - ClientTemplate calling MVC Url.Action calls (incorrectly) two different actions - json

I have some data that loads into a Kendo grid via the Ajax binding.
Within one of the columns there's a ClientTemplate that calls a javascript method (showAll).
This method will call an action and get the details of the data, putting it into a json response, and then open a jquery-ui dialog to show the details.
When the user clicks on the link in the grid the HttpGet is triggered for the GetDetails action BUT, the problem is, it is also triggered for the entire page's action (Index).
The question, I guess, is what is causing the Index action to be triggered? Because, the dialog will show, the detailed data will populate, but once I close the dialog all the filter textboxes will be reset and the grid will reload and the data within it.
Shouldn't the only action called be the GetDetails?
Any hints will be greatly appreciated!
Code:
#(Html.Kendo().Grid<LogViewModel>()
.Name("LogGrid")
.Columns(column =>
{
column.Bound(x => x.StuffCount).Title("Stuff").Width(70)
.ClientTemplate("<a onclick=\"showAll('" + "#= Id #')\"" + " href=''>#= StuffCount #</a>");
})
.DataSource(dataBinding => dataBinding
.Ajax()
.PageSize(50)
.Read(read => read.Action("GetData", "Summary")
.Data("getSearchFilters"))
.Model(model => model.Id(o => o.Id)))
.Events(e => e
.DataBound("onGridItemsDatabound"))
.Pageable(paging => paging.Refresh(true))
)}
<div id="dialog-message" title="" style="display: none">
<p id="msg"></p>
</div>
<script type="text/javascript">
var showAll= function (id) {
var url = '#Url.Action("GetDetails", "Summary")' + "/" + id;
var sTitle = 'title text';
$.getJSON(url, null,
function (data) {
$("#dialog-message").dialog({ title: sTitle });
$("#msg").text(data.details);
showMessage();
});
};
var showMessage = function () {
$("#dialog-message").dialog({
modal: true,
draggable: false,
resizable: false,
buttons: {
Ok: function() {
$(this).dialog("close");
}
}
});
};
</script>
The controller methods (content removed for brevity
public ActionResult Index(...)
{
...
}
public ActionResult GetDetails(Guid id)
{
... (get data from repository)
return Json(data, JsonRequestBehavior.AllowGet);
}

I posted the same question on the Telerik forum. Their admin pointed me in the right direction:
http://www.kendoui.com/forums/mvc/grid/kendo-ui-grid---clienttemplate-calling-mvc-url-action-calls-(incorrectly)-two-different-actions.aspx
Which "href" value should I use for JavaScript links, "#" or "javascript:void(0)"?
Turns out I had to add the void to the href to call the javascript and stay on the page.
href="javascript:void(0)"

Related

Symfony form & Ajax

I am working on Symfony 4.4.
To refresh a table, users select three options with an input:
InProgress
Finished
All
Then they must press a validate button.
I want to improve the use of this page by automating the refresh of the table.
Currently on my model I have AJX which allows me to retrieve the value of my entry:
<script>
$(document).on('change', '#campagnes_tel_isAttending', function () {
$('#flash').remove();
let $field = $(this)
let $preselect = $('#campagnes_tel_isAttending')
let $form = $field.closest('form')
let data = {}
data[$field.attr('name')] = $field.val()
console.log(data)
// On soumet les données
// $.post($form.attr('action'), data).then(function (data) {
// // On récupère le nouveau <select>
// $('#planningsms_client_label').val($(data).find('#planningsms_client option:selected').text());
// let $input = $(data).find(target)
// // On remplace notre <select> actuel
// $(target).replaceWith($input)
// })
});
</script>
I am now stuck because I cannot figure out how to get information back to my Controller, allowing me to modify a PreSelect variable with the value of the input and change the structure of the SQL query.
Create a route? Call a route in an Ajax POST?
Use this route in my Controller?
I think it's more or less that, but on the other hand I have no idea how to implement it.
EDIT :
It has moved forward a bit.
I manage to recover the data of the change of the input in my controller.
On the other hand I try to recall the function which will allow me to make a new SQL query with the selected filter, but that does not seem to work.
Ajax :
<script>
$(document).on('change', '#campagnes_tel_isAttending', function () {
$('#flash').remove();
let $field = $(this)
let $preselect = $('#campagnes_tel_isAttending')
let $form = $field.closest('form')
let data = {}
data['isAttending'] = $field.val()
console.log(data)
$.ajax({
type: "POST",
url: "/campagnestel/ajax",
data: data,
dataType: "json",
success: function(response) {
console.log(response);
}
});
});
</script>
And function in my controller :
/**
* #Route("/ajax", methods={"GET", "POST"})
*/
public function testAjax(Request $request)
{
if (!$request->isXmlHttpRequest()) {
return new JsonResponse(array(
'status' => 'Error',
'message' => 'Error'),
400);
}
if(isset($request->request)) {
$preSelect = $request->request->get('isAttending');
return $this->queryFollowingFilter($preSelect);
}
// return $this->queryFollowingFilter($preSelect);
return new JsonResponse(array(
'status' => 'OK'),
200);
}
Error :
The controller must return a "Symfony\Component\HttpFoundation\Response" object but it returned an array
As the error message states:
The controller must return a "Symfony\Component\HttpFoundation\Response" object
A JsonResponse meets that requirement and suits your needs. Try this:
if($request->request->has('isAttending')) {
$preSelect = $request->request->get('isAttending');
return new JsonResponse(
$this->queryFollowingFilter($preSelect),
200
);
}

Call an action from a Html.Dropdown onchange event in ASP.Net MVC 5

I'm trying to call the action in the controller when the dropdown selected item changes. Here is the code I am using but it is not working.
#Html.DropDownList("UserID", null, new { #onchange = "location.href='#Url.Action("Action", "Controller")'" })
What is the correct syntax? I tried to call a javascript function and it works.
#Html.DropDownList("UserID", null, new { #onchange = "leaveChange(this);" })
leaveChange(control) is my javascript function.
However, I am unable to invoke the action of the controller. Also, How do I then pass the value of the selected item to the action?
You cannot call a C# method inside the htmlAttributes object where it expects a key value pair. Instead you could execute the Url.Action method and set the result(the url) on a parent element's data attribute and read from that in your javascript code
<div data-submit-url="#Url.Action("ApplyVacation","Jobs")">
#Html.DropDownList("UserID",)
</div>
and in the change event, read it
$(document).ready(function ()
{
$("#UserID").change(function ()
{
var $this = $(this);
var url = $this.parent().data("submit-url") + "?userId=" + $this.val();
window.location.href = url;
});
});
When user make a selection on the SELECT, this code will navigate to the /Jobs/ApplyVacation url with querystring key userId and the selected option value as the value of that. Update the names as needed based on your code.
Try removing # before onchange
#Html.DropDownList("UserID", null, new { onchange = "location.href='#Url.Action("Action", "Controller")'" })
If you want to pass parameters to action you can use
#Html.DropDownList("UserID", null, new { onchange = "location.href='#Url.Action("Action", "Controller",
new {name = "Test param", category = "Test1"})'" })
Here is what I did
#Html.DropDownList("UserID", null,new {#id = "leve" })
Jquery code as below:
<script type="text/javascript">
$('#leve').change(function () {
var url = "/UserRoles/TellMeDate";
var name = $('#leve').val();
$.get(url, { parameter: name }, function (data) {
alert(data);
});
})
});
</script>
Controller:
public string TellMeDate(string parameter)
{
return DateTime.Today.ToString() +"-->>>"+ parameter + "!!!!";
}
This code worked for me:
onchange = "location.href='" + #Url.Action("AdmissionRequirement", "Profile") + "'"

How do I use MVC Validation to display a warning message without making the field required?

I have a asp.net MVC website where the user enters a social security number (SSN). The client wants to display a warning message if the SSN is already used, but doesn't want to force them to change the SSN (there are conditions under which multiple records can have the same SSN).
The MVC code I inherited has a validation to check if the SSN has already been used. The code works great. If the user enters a SSN that is already used, a message appears saying "SSN already exists" and prevents the form from being submitted. How can I change this so that the message is displayed, but so it doesn't prevent the form from being submitted?
ModelApplication.cs
[StringLength(9, MinimumLength = 9, ErrorMessage = "Please Enter 9 Digit SSN No")]
[Remote("IsSSNExist", "Admin", HttpMethod = "GET")]
public string ApplicantSSN { get; set; }
AdminController.cs
[HttpGet]
public JsonResult IsSSNExist(string ApplicantSSN)
{
if (Session["viewapp"] == null)
{
if (obj_BllApp.IsSSNExist(ApplicantSSN))
return Json("SSN already exists.", JsonRequestBehavior.AllowGet);
else
return Json(true, JsonRequestBehavior.AllowGet);
}
else
{
return Json(true, JsonRequestBehavior.AllowGet);
}
}
Application.cshtml
<label>
SSN
</label>
#Html.TextBoxFor(m => m.ApplicantSSN, new { #class = "input-small", #maxlength = "9", #onkeypress = "return ValidateNumberKeyPress(this, event);" })<br />
#Html.HiddenFor(m => m.ApplicantSSNID, new { id = "hdnApplicantSSN" })
span id="spAppSSn" class="SSNmsg">#Html.ValidationMessageFor(m => m.ApplicantSSN)</span>
UPDATE
I also tried using the response header like another poster suggested, but I couldn't get it to work.
This code didn't return anything and broke other javascript I had:
$(function () {
$("#txtApplicantSSN").change(function (xhr) {
alert("Hello");
var req = new XMLHttpRequest();
req.open('GET', document.location, false);
req.send(null);
var headers = req.getResponseHeader("SSN-DUPLICATED").toLowerCase();
alert(headers);
alert("Goodbye");
});
});
Using the same concept, I tried another way of getting the request header, but I never got a value. It looked like the validation that set the header value was being called after the javascript.
$(function () {
$("#txtApplicantSSN").change(function () {
var req = new XMLHttpRequest();
req.open('GET', document.location, false);
req.send(null);
var headers = req.getResponseHeader('SSN-DUPLICATED');
$("#remoteMessage").text(headers);
});
});
I tried something similar using session variables, but again the session variable seem to be getting set after the javascript code.
$(function () {
$("#txtApplicantSSN").change(function () {
var someSessionVariable = '#Request.RequestContext.HttpContext.Session["SSNExists"]';
alert(someSessionVariable);
$("#remoteMessage").text(someSessionVariable);
});
});
My current thought is to try to disable the validation when the submit button is clicked, but I haven't found a way to do it. I tried this
HtmlHelper.ClientValidationEnabled = false;
in the controller but it never hits the server side code. I get the validation error before it hits the controller.
Update #2
I disabled the validation when the submit button is clicked using the cancel class:
<input id="Submit1" type="submit" class="btn btn-primary cancel" value="Save" onclick="javascript: return ValidatonCoApplication();" />
This fixes the problem for this field, but disables validation for all other fields. Can someone suggest another way to do what I want without turning off validation?
In summary, this is asp.net MVC with Razor. After the user enters a SSN in a text box, I need a message to appear on the screen saying whether or not the SSN is valid. Currently I have a validation attribute in the model, but this is not only showing the message, it is declaring the model invalid and therefore not letting the user proceed to the next page. I want the validation message to appear, but do not want the model invalid. I'd appreciate any help you could give me. Thank you.
Since you only want to display a message based on the value of ApplicantSSN (not invalidate the model), remove the [Remote] attribute, and instead handle the .change() event of the textbox to call a conroller method and return an appropriate message.
Controller
public JsonResult IsSSNExist(string ApplicantSSN)
{
bool isValid = // your logic
if (isValid)
{
return Json(null, JsonRequestBehavior.AllowGet);
}
else
{
return Json(true, JsonRequestBehavior.AllowGet);
}
}
View
#Html.TextBoxFor(m => m.ApplicantSSN) // remove the onkeypress attribute
// add a placeholder for the message
<span class="field-validation-error"><span id="ssn-message">The SSN already exists.</span></span>
css
#ssn-message {
display: none;
}
Script
var url = '#Url.Action("IsSSNExist")';
var ssnMessage = $('#ssn-message');
$('#ApplicantSSN').change(function() {
$.getJSON(url, { ApplicantSSN: $(this).val() }, function(response) {
if(response) {
ssnMessage.show();
} else {
ssnMessage.hide();
}
});
});
Note: If the user enters an invalid value and tabs out of the control, the message will be displayed. You may want additional logic to hide the message if the user then starts typing in the textbox again in whichcase you would alo need to handle the keyup event
You can put validation in response header and then show it using jQuery as follows
[HttpGet]
public JsonResult IsSSNExist(string ApplicantSSN)
{
if (Session["viewapp"] == null)
{
if (obj_BllApp.IsSSNExist(ApplicantSSN)){
Response.AddHeader("SSN-DUPLICATED", "SSN already exists. ");
return Json(true, JsonRequestBehavior.AllowGet);
}
else
return Json(true, JsonRequestBehavior.AllowGet);
}
else
{
return Json(true, JsonRequestBehavior.AllowGet);
}
}
And add a span to display the remote message, and some jQuery in your view like following
<label>
SSN
</label>
#Html.TextBoxFor(m => m.ApplicantSSN,
new { #class = "input-small", #maxlength = "9", #onkeypress = "return ValidateNumberKeyPress(this, event);" })
<br />
#Html.HiddenFor(m => m.ApplicantSSNID, new { id = "hdnApplicantSSN" })
<span id="spAppSSn" class="SSNmsg">
#Html.ValidationMessageFor(m =>m.ApplicantSSN)</span>
<span id="remoteMessage" class="SSNmsg">
#Html.ValidationMessageFor(m =>m.ApplicantSSN)</span>
$(function () {
$('#ApplicantSSNID').rules()
.remote.complete = function (xhr) {
var responseMessage=xhr.getResponseHeader('SSN-DUPLICATED');
if (xhr.status == 200 && xhr.responseText === 'true') {
jQuery('#remoteMessage')[0].innerHTML =
(responseMessage||'');
}
};
});
Note: please don't use Session in MVC. It's not a good practice.
You cannot really do what you want in the way you want. Validation will pass or fail! What you'll have to do is remove the [Remote] validation attribute and do the check in the action you are submitting the form to. If the SSN exists then you'll have to pass this message out once you have completed the processing of the form.
Or use javascript and Ajax to check the SSN when the SSN textbox loses focus, then you can display the warning before the user submits the form

Pass dropbox value to a HttpGet action submit button click?

Basically I'm trying to pass the value of my dropbox to a get action.
The submit-button re-directs to the correct action , but what is the correct way to add the value of the dropbox with the re-direction?
My view:
#model TrackerModel
#using (Html.BeginForm("MyAction", "MyController", FormMethod.Get, new { ???}))
{
<div>
<strong>#Html.LabelFor(m => m.CustomerName)</strong>
#Html.TextBoxFor(m => m.CustomerName, new { type = "hidden", #class = "customer-picker" })
</div>
<button class="styledbutton" onclick="window.location.href='/Tracker/Index'">Cancel</button>
<button type="submit" value="submit" id="selectCustomer-button" class="styledbutton">Submit</button>
}
[HttpGet]
public ActionResult MyAction(IPrincipal user, Tracker model)
Customer-picker
$(document).ready(function () {
CustomerPicker();
});
function CustomerPicker() {
$('.customer-picker').select2({
placeholder: 'Select customer',
minimumInputLength: 1,
ajax: { // instead of writing the function to execute the request we use Select2's convenient helper
url: '/JsonData/GetCustomers',
type: 'POST',
dataType: 'json',
data: function (term) {
return {
query: term // search term
};
},
results: function (data) { // parse the results into the format expected by Select2.
// since we are using custom formatting functions we do not need to alter remote JSON data
return { results: data };
}
},
formatResult: function (data) {
return data;
},
formatSelection: function (data) {
return data;
}
});
}
I was expecting the value to be within my Tracker model parameter in the action, but this returns nulls. Also I'm not sure what to place in the "new" parameter in the form tag?
I also tried the following but all I get returning to the controller is text:"".
#Html.TextBoxFor(m => m.CustomerName, new { type = "hidden", #id = "selectedCustomer", #class = "customer-picker" })
<script type="text/javascript">
$(function () {
$("#Form1").submit(function (e) {
alert("boo");
e.preventDefault();
var selectCustValue = $("#selectedCustomer").val();
$.ajax({
url: '/CalibrationViewer/SentMultipleCalsToCustomer',
data: { text: selectCustValue }
});
});
});
OK got it,
var selectCustValue = $("#s2id_CustomerName span").text();
Found another piece of code the used the customer-picker and the javascript associated with view used the above.
I viewed the page source and it still show's both id and name as CustomerName, it has something to do with the "Select 2" helper.
I may get slated for marking this as the answer, considering I should have figured it out earlier, but there you have it !

FineUploader OnComplete method not firing

So, I'm using FineUploader 3.3 within a MVC 4 application, and this is a very cool plugin, well worth the nominal cost. Now, I just need to get it working correctly.
I'm pretty new to MVC and absolutely new to passing back JSON, so I need some help getting this to work. Here's what I'm using, all within doc.ready.
var manualuploader = $('#files-upload').fineUploader({
request:
{
endpoint: '#Url.Action("UploadFile", "Survey")',
customHeaders: { Accept: 'application/json' },
params: {
//variables are populated outside of this code snippet
surveyInstanceId: (function () { return instance; }),
surveyItemResultId: (function () { return surveyItemResultId; }),
itemId: (function () { return itemId; }),
imageLoopCounter: (function () { return counter++; })
},
validation: {
allowedExtensions: ['jpeg', 'jpg', 'gif', 'png', 'bmp']
},
multiple: true,
text: {
uploadButton: '<i class="icon-plus icon-white"></i>Drop or Select Files'
},
callbacks: {
onComplete: function(id, fileName, responseJSON) {
alert("Success: " + responseJSON.success);
if (responseJSON.success) {
$('#files-upload').append('<img src="img/success.jpg" alt="' + fileName + '">');
}
}
}
}
EDIT: I had been using Internet Explorer 9, then switched to Chrome, Firefox and I can upload just fine. What's required for IE9? Validation doesn't work, regardless of browser.
Endpoint fires, and file/parameters are populated, so this is all good! Validation doesn't stop a user from selecting something outside of this list, but I can work with this for the time being. I can successfully save and do what I need to do with my upload, minus getting the OnComplete to fire. Actually, in IE, I get an OPEN/SAVE dialog with what I have currently.
Question: Are the function parameters in onComplete (id, filename, responseJSON) getting populated by the return or on the way out? I'm just confused about this. Does my JSON have to have these parameters in it, and populated?
I don't do this (populate those parameters), and my output method in C# returns JsonResult looking like this, just returning 'success' (if appropriate):
return Json(new { success = true });
Do I need to add more? This line is after the saving takes place, and all I want to do is tell the user all is good or not. Does the success property in my JSON match up with the responseJSON.success?
What am I missing, or have wrong?
Addressing the items in your question:
Regarding restrictions inside of the "select files" dialog, you must also set the acceptFiles validation option. See the validation option section in the readme for more details.
Your validation option property in the wrong place. It should not be under the request property/option. The same is true for your text, multiple, and callbacks options/properties. Also, you are not setting your callbacks correctly for the jQuery plug-in.
The open/save dialog in IE is caused by your server not returning a response with the correct "Content-Type" header. Your response's Content-Type should be "text/plain". See the server-side readme for more details.
Anything your server returns in it's response will be parsed by Fine Uploader using JSON.parse when handling the response client-side. The result of invoking JSON.parse on your server's response will be passed as the responseJSON parameter to your onComplete callback handler. If you want to pass specific information from your server to your client-side code, such as some text you may want to display client-side, the new name of the uploaded file, etc, you can do so by adding appropriate properties to your server response. This data will then be made available to you in your onComplete handler. If you don't have any need for this, you can simply return the "success" response you are currently returning. The server-side readme, which I have linked to, provides more information about all of this.
To clarify what I have said in #2, your code should look like this:
$('#files-upload').fineUploader({
request: {
endpoint: '#Url.Action("UploadFile", "Survey")',
customHeaders: { Accept: 'application/json' },
params: {
//variables are populated outside of this code snippet
surveyInstanceId: (function () { return instance; }),
surveyItemResultId: (function () { return surveyItemResultId; }),
itemId: (function () { return itemId; }),
imageLoopCounter: (function () { return counter++; })
}
},
validation: {
allowedExtensions: ['jpeg', 'jpg', 'gif', 'png', 'bmp']
},
text: {
uploadButton: '<i class="icon-plus icon-white"></i>Drop or Select Files'
}
})
.on('complete', function(event, id, fileName, responseJSON) {
alert("Success: " + responseJSON.success);
if (responseJSON.success) {
$('#files-upload').append('<img src="img/success.jpg" alt="' + fileName + '">');
}
});