I have a View which calls the function 'Versions' on click of a href, which normally returns a 'Version.cshtml'.
I modified that Controllerfunction:
If i click on that link and JavaScript is enabled, it starts an ajax call which returns a JSON-result. It modifies the DOM so the page won't reload. Now my problem is to differentiate when JavaScript is enabled or not - If enabled, it should return the JSON result; if disabled it should return the 'Version.cshtml'. When I inspect the element, we have already a class called 'nojs-noborder' when JS is disabled. But how can i get that value through the controller without JS so I can fill the bool 'isJavaScriptenabled'?
Controller function looks like this:
if (isJavaScriptEnabled)
{
var tocstr = ControllerContext.RenderView(PartialView("Versiontoc", model));
var middlestr = ControllerContext.RenderView(PartialView("Versionmiddle", model));
var result = new JsonResult
{
Data = new { tocstr, middlestr },
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
MaxJsonLength = MaxValue
};
return result;
}
return View("Version", model);
The class in the inspected element:
<div class="nojs-noborder" id="contentwrapper">
It's unclear from your answer whether you have done this but you need to update your view to make an AJAX request or follow the anchor tag depending on whether JavaScript is enabled or not.
Then, in your controller, you don't need to determine if JavaScript is enabled in the browser, but rather whether the request is an AJAX request. If it is, return JSON else return the view.
Most JavaScript libraries will append the X-Requested-With header with a value of XMLHttpRequest. You can check for the existence of this header.
I'm unsure what version of MVC you are using but you could use the built in Request.IsAjaxRequest() to determine this.
Your ActionMethod then becomes:
if (Request.IsAjaxRequest())
{
var tocstr = ControllerContext.RenderView(PartialView("Versiontoc", model));
var middlestr = ControllerContext.RenderView(PartialView("Versionmiddle", model));
var result = new JsonResult
{
Data = new { tocstr, middlestr },
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
MaxJsonLength = MaxValue
};
return result;
}
return View("Version", model);
If this method isn't available you could roll your own based on this answer
Related
In my ASP.NET Core web application, one of my pages has a sequence of steps that it performs to call stored procedures. Depending on whether or not stored procedures return rows, I route to one of two controller actions (either rendering a partial including an additional input, or overriding what that input would do and just coming back to the page on the next step).
Right now I've got code that is nearly there. My controller actions navigate and process correctly and my Ajax works... Sort of.
Button in the Razor view that calls the Ajax function
<input type="button" value="Run Check" onclick="runCheck('#actionItem.StepID', '#Model.Client.DatabaseConnectionString', '#Model.Client.ClientID')" />
Ajax
<script type="text/javascript">
function runCheck(x, y, z) {
$.ajax({
url: '#Url.Action("ProcessFeedbackHasRows", "Client")',
type: 'POST',
data: { stepId: x, databaseConnectionString: y, clientId: z },
success: function (result) {
if (result) {
alert('true');
var stepId = x;
var databaseConnectionString = y;
var clientId = z;
var url = '#Url.Action("ViewProcessingFeedBackPartial", "Client")';
$("#processingFeedbackPartialDiv").load(url, { stepId, databaseConnectionString, clientId },
function () {
$("#confirmButton").removeAttr("style");
});
} else {
alert('false');
var newUrl = '#Url.Action("Processing", "Client")';
window.location = newUrl;
}
}
});
};
</script>
Controller Action
public JsonResult ProcessFeedbackHasRows(int StepId, string DatabaseConnectionString, int ClientID)
{
bool hasRows = true;
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.");
hasRows = false;
}
return new JsonResult (new { HasRows = hasRows });
}
The alerts are there to just prove that the right condition was in fact met and that the right things are happening. And this is where my problems lie. When I had the Network traffic tab of the F12 tools open, I noticed that whatever json object is created first determines all future runs of the code.
For example: let's say I forced the first item to come through with at least 1 row returned, I'd see the alert true, see the JSON object in the Network tab contain true and see my partial view, as expected.
The next several steps would produce a a false result because no rows were returned from the SP in the controller. The bool in the controller would be set to false, the JSON object in the Network tab would say HasRows = false, but my alert would show true and the partial still renders asking me for confirmation. So despite not returning any rows and producing a false result, I see the alert true and the partial is rendered even though in my Network tab I see
The opposite is true as well. If I had the first item through create an object where HasRows = false, and the next several would have been true, subsequent steps return true in the Network tab, but alert false and go through the false logic in the Ajax.
What is the best way to handle this? Is there a way to clear the JSON or something? I figured by creating a new JsonResult at the end of every method call, it would produce a new result to inspect, but it seems to continue using the first one sent in despite being able to see the others in the Network tab.
What I've tried
Disabling cache in Ajax by adding cache: false, right above the URL in the $.ajax setup.
Resetting the json object within the function after my else braces
result = []; and delete result.hasRows;
Despite these attempts, the ajax will always alert and go through whatever logic flow was sent first while the actual object contains the correct variable.
I actually solved this because in my inexperience with javascript and jQuery, I didn't understand exactly how the logic was being handled since I can't very easily debug the javascript the way I can the C#.
Through trial and error and alerts, I found that essentially, the in my Ajax I had to change the if condition to inspect result.hasRows rather than just the result.
<script type="text/javascript">
function runCheck(x, y, z) {
$.ajax({
cache: false,
url: '#Url.Action("ProcessFeedbackHasRows", "Client")',
type: 'POST',
data: { stepId: x, databaseConnectionString: y, clientId: z },
success: function (result) {
if (result.hasRows) {
alert('result = true');
alert('result.hasRows = ' + result.hasRows);
var stepId = x;
var databaseConnectionString = y;
var clientId = z;
var url = '#Url.Action("ViewProcessingFeedBackPartial", "Client")';
$("#processingFeedbackPartialDiv").load(url, { stepId, databaseConnectionString, clientId },
function () {
$("#confirmButton").removeAttr("style");
});
} else {
alert('result = false');
alert('result.hasRows = ' + result.hasRows);
var newUrl = '#Url.Action("Processing", "Client")';
window.location = newUrl;
}
}
});
};
</script>
The initial question still stands though. Assuming I wanted to delete the entire JSON object and use my initial logic present in the question, how can I clear or delete the entire object so that every time I hit that Ajax call, a new object would be inspected? Or is that now how this works and I solved the problem correctly?
In my form I have a textbox as below
#Html.TextBox("first_name")
I need to pass the value of this textbox to controller through a actionlink.
I tried the below
#Html.ActionLink("View", "view_Details", new { name = first_name})
but this is giving error
"first_name" does not exist in the current context
Is this possible using a Actionlink?
My controller signature is
public ActionResult view_Details(string name)
{
return View();
}
Edited
#Html.ActionLink("View", "view_Details", new { name = getname()})
<script type="text/javascript">
function getname() {
return $("#first_name").val();
}
</script>
I tried above code. Its also giving error
getname() does not exist in the current context
You need javascript/jquery to get the value of the textbox and then update the url you want to redirect to
Html
#Html.ActionLink("View", "view_Details", new { id = "myLink" }) // add id attribute
Script
$('#myLink').click(function() {
var firstname = $('#first_name').val(); // get the textbox value
var url = $(this).attr('href') + '?name=' + firstname; // build new url
location.href = url; // redirect
return false; // cancel default redirect
});
Side note: further to you edit, the reason you receive that error is that razor code (the #Html.ActionLink() is parsed on the server before its sent to the view but getname() is a client side method which does not exist at that point - i.e it does not exist in the current context
I am trying to construct a URL using a HTML helper extension method while trying pass in parameters to the extension method. For example
public static MvcHtmlString GenerateActionLink(this HtmlHelper html,string displayText,string id,int logicstatusId)
{
var actionName = string.Empty;
var controllerName = string.Empty;
if (logicstatusid == 5)
{
actionName = "Basic";
controllerName = "HighBasic";
}
else
{
action = "Advanced";
controllerName = "HighAdvanced";
}
var targetURL = UrlHelper.GenerateUrl("Default", action, controller, new RouteValueDictionary(new { id = id}), RouteTable.Routes, html.ViewContext.RequestContext, false);
//Create the hyper link tag
var anchorLinkBuilder = new TagBuilder("a");
//Merge the target URL with the href attribute
anchorLinkBuilder.MergeAttribute("href", targetURL);
return MvcHtmlString.Create(anchorLinkBuilder.ToString(TagRenderMode.Normal));
}
While this helper method is working, the problem I am facing is on the client side.
var cellHtml = '<div class="action-column">';
var id= row.encryptedId;
cellHtml += '#Html.GenerateHtmlLink("Blip","'+ id+'" , 4)';
cellHtml += "</div>";
return cellHtml;
In this case the URL is getting constructed but the id parameter is not passing on to the helper method. I am not sure if I have done the passing of the parameter the right way. I'd appreciate if anybody help out.
Your C# code (call to the GenerateActionLink helper method) gets executed in server when razor tries to render the view. At that time the js variable value will not be there. The output of razor executing all the C# code view file is just the html markup which the browser will render. Only after that your javascript will be executed and the js variable value will be avaialble.
If you absolutely need to generate the dynamic url (for each id/logicstatusId value) in your client side javascript code using the UrlHelper method, you might consider exposing that C# code via an action method. Whenever you need the link url in your javascript code, make an ajax call to the action method, pass the parameter value and get the url.
public string GenerateActionLink(string id, int logicstatusId)
{
var actionName = "Advanced";
var controllerName = "HighAdvanced";
if (logicstatusId == 5)
{
actionName = "Basic";
controllerName = "HighBasic";
}
var targetUrl = UrlHelper.GenerateUrl("Default", actionName, controllerName, new RouteValueDictionary(new { id = id }), RouteTable.Routes, Request.RequestContext, false);
return targetUrl;
}
And in client side
var id = 1;
$.get('/Home/GenerateActionLink?logicstatusId=5&id=' + id,function(res) {
var htmlMarkup = 'Blip';
// do something with htmlMarkup
// Ex : $('#SomeDivId').append(htmlMarkup);
});
But if you want to do this for many items, you might not want to make a call for each items, In that case,I would generate the base links and conditionally append the querystring values in javascript
var baseUrlBasic = "#Url.Action("Basic","HighBasic");
// Now later
var id = 1;
var newUrl = baseUrl+'?logicstatusId=5&id='+id;
// Use this to build the anchor tag
When Method of the senderform is POST, everything works fine. However, as soon as I change the method to GET, I don't receive anything on the server.
function ajaxSubmit(destinationElement, senderform) {
var xmlreq = new XMLHttpRequest();
var params = new FormData(senderform);
xmlreq.open(senderform.method, senderform.action, true);
if (/\/content\.php$/.test(senderform.action))
xmlreq.onreadystatechange = receiveTable;
else xmlreq.onreadystatechange = receiveText;
xmlreq.send(params);
}
I know that I could manually append key-value pairs at the end of Action address, but the problem is that I don't know which form is going to be passed with what fields.
I would prefer native javaScript if possible.
How can I send a GET request using XMLHttpRequest with key-value pairs from senderform which points to form Element (the same way as it already works for POST requests)?
First parameter is a reference to submit button, or form element itself. Second is callback function for XMLHttpRequest.
var ajaxSubmit = function(sender, callback) {
var xmlreq = new XMLHttpRequest(), params;
// look around for the sender form and key-value params
if (sender.form !== undefined)
{
params = new FormData(sender.form);
params.append(sender.name, sender.value);
sender = sender.form;
}
else params = new FormData(sender);
var actAddress = sender.action;
// append the params to the address in action attribute
if (sender.method == 'get')
{
var firstRun = true;
for (var key of params.keys())
{
if (firstRun)
{
actAddress += '?';
firstRun = false;
}
else actAddress += '&';
actAddress += key + "=" + params.get(key);
}
}
xmlreq.open(sender.method, actAddress, true);
xmlreq.onreadystatechange = callback;
if (sender.method == 'get')
xmlreq.send();
else xmlreq.send(params);
}
Therefore you can use it as
<form onsubmit="ajaxSubmit(this,callbackFx)" >
<!-- or -->
<input onclick="ajaxSubmit(this,callbackFx)" type="submit" name="" value=""/>
</form>
Are you sure the problem is not the PHP script? I see no reference that https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#send() with FormData needs POST to work, but if the PHP script takes the info from $POST or something (My PHP is rusty), the behavior would be different.
Since you can't create a useable body in a GET request (see below), then the other option is to use params in the url.
function buildGetUrlParams(baseUrl, paramsObj) {
var builtUrl = baseUrl + "?";
Object.keys(paramsObj).forEach(function(key) {
builtUrl += key + "=" + paramsObj[key] + "&";
});
return builtUrl.substr(0, builtUrl.length - 1);
}
document.getElementById('finalUrl').innerText = buildGetUrlParams('http://test.url.com', { name:'James', occupation:'web design' });
<div id="finalUrl"></div>
An HTTP GET request can contain a body, but there is no semantic meaning to that body. Which means, in simple terms, that a server doesn't have any reason to, nor have any knowledge of how, to process the body of a GET request. If it's possible to write a server that could do this, it would be bad practice as per the HTTP/1.1 specs:
if the request method does not include defined semantics for an entity-body, then the message-body SHOULD be ignored when handling the request.
And that's basically why it's not working. If you want to send any sort of data that the server is able to respond to, then you'll need to use a different HTTP method.
This answer also explains this issue.
This is my C# WebAPI2 controller, which gets hit:
[HttpGet, Route("bycaseidlist/{idArray}")]
public async Task<IHttpActionResult> GetByCaseIdList([FromUri] List<int> idArray)
This is the call:
var idArray = [4,4,2,4];
var url = baseUrl + 'api/cases/bycaseidlist/' + idArray ;
$http.get(url)
The problem is that the API doesn't get the array, it gets ...this:
In other words an array with one value: 0. Why is this happening? How do I fix it? It seems to be in-line with this answer, but it doesn't work. Should I pass it in the body? I feel like I am missing something obvious.
Get ActionMethods can take objects as arguments. However, the default behavior is to look at the body when the parameter is not a .net primitive. In order to force the action method to use a model binder to read the object data from the request, the parameter can be decorated with the [FromUri] or [ModelBinder] attributes. (Note there are other ways to do this that include doing parameter binding rules but that is probably overkill for what you are trying to accomplish here). Here is an implementation that solves the original problem that you were posing.
<script type="text/javascript">
var ajaxCall = function (myArry) {
var ajaxProperties = {};
ajaxProperties.url = "/api/Mul/Mutiply";
ajaxProperties.type = "Get";
ajaxProperties.data = {};
ajaxProperties.data.numbers = myArry;
ajaxProperties.contentType = "application/json";
console.log(ajaxProperties);
ajaxProperties.success = function (data) {
console.log(data);
}
ajaxProperties.error = function (jqXHR) {
console.log(jqXHR);
};
$.ajax(ajaxProperties);
};
var getData = function (e) {
var myArry = new Array();
myArry.push($('input[name=num1').val());
myArry.push($('input[name=num2').val());
ajaxCall(myArry);
return false;
};
</script>
Controller
[HttpGet]
public IHttpActionResult Multiply([FromUri] int[] numbers)
{
int result = 0;
if(numbers.Length > 0)
{
result = 1;
foreach (int i in numbers)
{
result = result * i;
}
}
return Ok(result);
}
}
I think my mistake was using Get. I might be remembering incorrectly (someone confirm if you know offhand), but Get might not be able to take objects as arguments. Anyway, I changed the method to POST and then changed the param to be sent in the request body, rather than the url. It now works. Here is the working code:
[HttpPost, Route("bycaseidlist")]
public async Task<IHttpActionResult> PostByCaseIdList([FromBody] int[] sqlCaseIdArray)
and the call itself:
function runDbCall(url, sqlCaseIdArray){
return $http({
method: 'POST',
url: url,
data: sqlCaseIdArray
});
}
runDbCall(url, sqlCaseIdArray)
I will come back to this when I figure out if the problem was Get not being able to take objects, but I thought it could in url, just not in body...need to clarify. If someone posts an answer just on that part, I will accept, since that's probably the root of the prob.