I have a view with a hidden field
#Html.HiddenFor(model => model.Driver.ID, new { htmlAttributes = new { id = "driver" } })
I have a controller with a method I want to call passing that field "driver", but debugging I see this method doesn't even get called.
public string GetEditWarningMessage(int? driverID)
{
Driver driver = unitOfWork.DriverRepository.Get().Where(d => d.ID == driverID).FirstOrDefault();
string message = null;
if(!driver.Status.Equals("A"))
{
message = "This driver is assigned to current or future schedule movements";
}
return new JavaScriptSerializer().Serialize(message);
}
and finally the javascript, which I'm sure is the source of the problem
$().ready(function () {
$("#driver").change(function () {
$.get("../GetEditWarningMessage/" + $(this).val() + '?' + $.now(), function (response) {
var warning;
warning = $.parseJSON(response);
});
$("form").submit(function () {
var performEdit = false;
if (warning != null) {
performEdit = confirm(warning);
return performDelete;
}
});
});
});
basically if the message I pass from the GetEditWarningMessage() is null, I don't want the popup to appear, otherwise have it popup with the warning message passed on.
Help much appreciated, thank you all
Change the following
$.get("../GetEditWarningMessage/" + $(this).val() + '?' + $.now(), function (response) {
var warning;
warning = $.parseJSON(response);
});
To
var warning;
$.get("../GetEditWarningMessage/" + $(this).val() + '?' + $.now(), function (response) {
warning = $.parseJSON(response);
});
In your code the variable "warning" has scope inside only $.get call. So it cannot be used in form submit call..
Declaring it outside the $.get call will solve the issue.
If the hidden input value doesn't change then you must call GetEditWarningMessage inside the form submit call.. even that Synchronous ajax call so that form submittion can wait for the response of warning message.
Related
I am new to MVC, I looked at other solutions to my problem on web but still I can't find what is wrong with my code, I'm using code (as is) from umbraco website, I'm trying to call a function and pass a var from js, I don't understand what is the problem..., tried to solve it with online examples but getting the same error... help
//c#
public class MokedLoginController : SurfaceController
{
[HttpGet]
[Route("umbraco/surface/MokedLogin/DoLogin/{id}")]
public void DoLogin([FromBody]int member)
{
var _member = Services.MemberService.GetById(member);
if (_member != null)
FormsAuthentication.SetAuthCookie(_member.Username, false);
}
}
//js
angular.module('umbraco').controller('MokedLoginController', [
'$scope',
'$http',
'editorState',
'contentResource',
function ($scope, $http, editorState, contentResource) {
// Check if you are creating a new member
$scope.isNew = editorState.current.id <= 0;
// Define the login as member function
$scope.loginAsMember = function () {
// ### Setup cookie ????
var url = '/umbraco/surface/MokedLogin/DoLogin/{id}';
// Get the current member id using the editorState
var _memberId = editorState.current.id;
// Do Login
$http.post(
url, _memberId
).then(
function (response) {
// ### Redirect ????
// Get the redirect page from config
var urlPageRedirect = $scope.model.config.memberRedirectPage;
// Check if page is set in the config
if (urlPageRedirect)
{
contentResource.getNiceUrl(urlPageRedirect).then(function (data) {
window.open(data, '_blank') // Get the first url
});
} else {
// Open the root page
window.open('/', '_blank');
}
},
function (error) {
console.log(error.data);
}
);
};
}
]);
// Do Login
$http.post(
// the famouse exception that comes from ? and why ?
System.ArgumentException: The parameters dictionary contains a null entry for parameter 'member' of non-nullable type 'System.Int32' for method 'Void DoLogin(Int32)'
in 'ThePoolInsurance.Web.Controllers.MokedLoginController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
VS2013, WebForms, .NET 4.51
I want to use a hidden field to maintain the contents of my Knock Out view model across postbacks. So I took the KO code from http://knockoutjs.com/examples/cartEditor.html and then read http://www.codeproject.com/Articles/153735/Using-KnockoutJS-in-your-ASP-NET-applications for some ideas.
The end result is the following:
<asp:HiddenField ID="HiddenField1" runat="server" />
<script type='text/javascript' src="http://knockoutjs.com/examples/resources/sampleProductCategories.js"></script>
<script type="text/javascript">
function formatCurrency(value) {
return "$" + value.toFixed(2);
}
var CartLine = function () {
var self = this;
self.category = ko.observable();
self.product = ko.observable();
self.quantity = ko.observable(1);
self.subtotal = ko.computed(function () {
return self.product() ? self.product().price * parseInt("0" + self.quantity(), 10) : 0;
});
// Whenever the category changes, reset the product selection
self.category.subscribe(function () {
self.product(undefined);
});
};
var Cart = function () {
// Stores an array of lines, and from these, can work out the grandTotal
var self = this;
self.lines = ko.observableArray([new CartLine()]); // Put one line in by default
self.grandTotal = ko.computed(function () {
var total = 0;
$.each(self.lines(), function () { total += this.subtotal() })
return total;
});
// Operations
self.addLine = function() {
self.lines.push(new CartLine());
SaveList();
};
self.removeLine = function(line) {
self.lines.remove(line);
SaveList();
};
self.save = function () {
var dataToSave = $.map(self.lines(), function (line) {
return line.product() ? {
productName: line.product().name,
quantity: line.quantity()
} : undefined
});
alert("Could now send this to server: " + JSON.stringify(dataToSave));
};
self.SaveList = function () {
var myHidden = document.getElementById('<%= HiddenField1.ClientID %>');
if (myHidden)//checking whether it is found on DOM, but not necessary
{
var dataToSave = $.map(self.lines(), function (line) {
return line.product() ? {
productName: line.product().name,
quantity: line.quantity()
} : undefined;
});
alert("Saving - " + JSON.stringify(dataToSave));
myHidden.value = JSON.stringify(dataToSave);
}
};
};
var stringViewModel = document.getElementById('<%=HiddenField1.ClientID %>').value;
var viewModel;
if (document.getElementById('<%=HiddenField1.ClientID %>').value == '') {
alert('Nothing In Hidden Field');
viewModel = new Cart();
} else {
viewModel = ko.utils.parseJson(stringViewModel);
for (var propertyName in viewModel) {
viewModel[propertyName] = ko.observable(viewModel[propertyName]);
}
}
ko.applyBindings(viewModel);
$(document.forms[0]).submit(function () {
alert('In Submit');
viewModel.SaveList();
});
</script>
So basically when the page loads we create a new instance of the Cart. And when the form is posted we successfully have the cart serialized to HiddenField1 and I can see the expected value in the code behind:
protected void btnSave_OnClick(object aSender, EventArgs aE)
{
if (HiddenField1.Value == null)
{
}
}
however after the postback the contents of stringViewModel
var stringViewModel = document.getElementById('<%=HiddenField1.ClientID %>').value;
is always blanl / empty? Why is that?
And then assuming I have the correct JSON is the following the correct way to apply it back to the view model?
viewModel = ko.utils.parseJson(stringViewModel);
for (var propertyName in viewModel) {
viewModel[propertyName] = ko.observable(viewModel[propertyName]);
}
EDIT: I tried a few things with no luck
Added all JS code to jQuert OnReady() handler
Tried using instead of ASP:HiddenField
In all cases in PostBack I can see the value assigned to the hidden field by SaveList(), but when the page is displayed again (after postback) the value of the hidden field is an empty string
For the first part, what you're doing is correct. Use the console (press F12 in your browser) to examine the hidden field, and check if it has the value. If you see it in the server side, it should be in the client side. You can also run js code, and set breakpoints to discover what the problem is. You can also add a PreRender handler in the server side, and add a breakpoint and debug to check that the Value has not been deleted in the server side (this event happens just before the page is rendered to be sent to the browser).
For the second part, the fastest way to do what you need is to use knockout mapping, which creates a model from a JavaScript object, or from JSON. You need to use this: ko.mapping.fromJSON. This will create a new viewmodel, which you can directly bind, from your JSON. (As you can read in the docs, you can customize how the view model is created).
However, what you're doing is quite strange. You normally use Knockout with Web API, or Web Services, or Page methods, without reloading the page. The model is recovered, and updated, changed, etc. through one of those technologies, using AJAX.
I have an ajax call that fires every five seconds to update some data on the screen. It seems that the getJSON breaks after a page refresh. What appears to be happening is that if the page refreshes via F5 while the call is happening, the future calls won't work. They simply fail with no error message. The textStatus is error but errorthrown is empty. I'm calling into an Asp.Net MVC controller and no error is fired there. The subsequent getJSON calls never hit the server. It's pretty much hosed at this point until I start and stop my website again. No idea how to fix this as I can't stop the user from refreshing the page and it's a pain for me to test updates.
Here is my code:
var RealTimeActivity = (function () {
var realTimeActivity = {};
var timer = null;
var active = false;
realTimeActivity.LastUpdated = new Date();
function queueUpdate() {
timer = setTimeout("RealTimeActivity.Update();", 5000);
}
function reset() {
clearTimer();
queueUpdate();
}
function clearTimer() {
if (timer) {
clearTimeout(timer);
timer = null;
}
}
realTimeActivity.Update = function () {
try {
clearTimer();
if (active === true) {
$.getJSON("realTimeActivity/GetRealTimeData",
function (result) {
if (result.Success) {
// do stuff
} else {
// Other stuff
}
queueUpdate();
}
).fail(function (jqXHR, textStatus, errorThrown) {
console.log(textStatus + ": " + errorThrown);
reset();
})
}
} catch (ex) {
reset();
}
}
realTimeActivity.Start = function (i) {
active = true;
if (i) {
realTimeActivity.Update();
} else {
queueUpdate();
}
}
realTimeActivity.Stop = function () {
active = false;
clearTimer();
}
return realTimeActivity;
}());
EDIT:
This appears to only be reproducible in Chrome. A bug in chrome perhaps or am I missing something?
This appears to be a caching issue with Chrome and aborted requests. It will not allow me to send another request to the same URL once the request is aborted even after refreshing the page. I worked around it by making every request unique (via a timestamp).
postRequest('realTimeActivity/GetRealTimeData?u=' + (new Date).getTime(), null, function (response) {
Apparently you can also call $.ajax and just set cache to false as well which does the same thing behind the scenes.
I know there are already a few posts on this subject but I cannot seem to figure out an answer to my specific issue. I am confused as to why I cannot pass a variable into the controller but I can pass in a hardcoded value...
This works...
<script type="text/javascript">
$().ready(function () {
$("#MessageTypes").change(function () {
//I know this is a horrible way to do it but for some reason I couldnt pass the sMessageType directly in
var sMessageType = $("#MessageTypes").val();
if (sMessageType == "Professional Voicefile") {
$.get('#Url.Action("GenerateMessageDesc", new { MessageType = "Professional Voicefile" } )', function (data) {
$('#MessageDesc').replaceWith(data);
});
}
else if (sMessageType == "Dynamic Field") {
$.get('#Url.Action("GenerateMessageDesc", new { MessageType = "Dynamic Field" } )', function (data) {
$('#MessageDesc').replaceWith(data);
});
}
else {
//default to prof
$.get('#Url.Action("GenerateMessageDesc", new { MessageType = "Professional Voicefile" } )', function (data) {
$('#MessageDesc').replaceWith(data);
});
}
});
});
...But this does not. Why?
<script type="text/javascript">
$().ready(function () {
$("#MessageTypes").change(function () {
var sMessageType = $("#MessageTypes").val();
$.get('#Url.Action("GenerateMessageDesc", new { MessageType = sMessageType } )', function (data) {
$('#MessageDesc').replaceWith(data);
});
}
});
});
It says "The name 'sMessageType' does not exist in the current context".
I think maybe I should be using some kind of ajax call instead to call the controller and update the view instead of how I am doing it -- however, why does scenario 1 work but not scenario 2?
It says "The name 'sMessageType' does not exist in the current context".
sMessageType is a javascript variable that lives on the client that you are trying to use inside a server side helper. That obviously is impossible because javascript runs on the client and server side script on the server.
Here's the correct way to achieve that:
$.get('#Url.Action("GenerateMessageDesc")', { messageType: sMessageType }, function (data) {
$('#MessageDesc').replaceWith(data);
});
this will pass the MessageType as query string parameter, so your target controller action might look like this:
public ActionResult GenerateMessageDesc(string messageType)
{
...
}
I've seen this question asked a few ways and the solutions are generally for other languages and don't apply to ASP.NET MVC 2.
I am using Jquery & Jquery forms to auto-save user data at a set interval. I still want the application to be able to time out, but the auto-saves via jquery forms keep refreshing the server.
My initial idea to fix this was pretty simple. I've already got an ActionFilter I use to see if the session expires. Well, the session won't ever expire; however, I just keep track of how many auto saves occurr based on a value in session and when it reaches a limit (specified in the web.config), it does a:
filterContext.Result = new RedirectResult("~/Account.aspx/LogOn");
Well, this doesn't work because the auto save is doing an ajaxFormSubmit to call the action in the first place. I've tried changing the action to redirect to the login page, but the same thing happens....it just doesn't do a redirect. The only thing the action can return is a Json result. In my latest version (code below) I'm setting the json return value to false and calling a redirectToLogin() function to send the page over to the login page. It doesn't work and i'm not sure why.
Any thoughts on this would be most helpful.
Excerpt of code that sets up the interval for autosaving on the view (placed just before the form is closed):
<%
double sessionTimeoutInMinutes = double.Parse(ConfigurationManager.AppSettings["SESSION_TIMEOUT_IN_MINUTES"].ToString());
double maxContiguousAutoSaves = double.Parse(ConfigurationManager.AppSettings["MAX_CONTIGUOUS_AUTO_SAVES"].ToString());
double autoSaveInterval = (sessionTimeoutInMinutes / maxContiguousAutoSaves) * 60 * 1000;
%>
<%= Html.Hidden("autoSaveInterval", autoSaveInterval) %>
<script type="text/javascript">
$(document).ready(function() {
var autoSaveFrequency = $('[id=autoSaveInterval]').val();
//alert(' Auto Save Interval in miliseconds: ' + autoSaveFrequency);
setInterval(
"initAutoSave('AutoSaveGoals', 'message')"
, autoSaveFrequency);
});
</script>
"AutoSaveGoals" goals is the name of one of my actions. It handles the post, updates certain items in session, and calls the repository.update. It is defined below:
[HttpPost]
public ActionResult AutoSaveGoals(Data data)
{
Data sessdata = Data();
sessdata.MpaGoals = data.Goals;
sessdata.MpaStatus = data.MpaStatus;
sessdata.StartPeriodDate = data.StartPeriodDate;
sessdata.EndPeriodDate = data.EndPeriodDate;
sessdata.AssociatePassword = data.AssociatePassword;
try
{
_repository.update(sessdata);
}
catch (Exception e)
{
LogUtil.Write("AutoSaveGoals", "Auto Save Goals Failed");
LogUtil.WriteException(e);
}
if (!autoLogOffUser(RouteData.GetRequiredString("action")))
return Json(new { success = true });
else
return Json(new { success = false });
}
The initAutoSave function is javascript that uses Jquery & Jquery Forms plugin. Here it is:
function initAutoSave(targetUrl, messageDivId) {
var options = {
url: targetUrl,
type: 'POST',
beforeSubmit: showRequest,
success: function(data, textStatus) {
//alert('Returned from save! data: ' + data);
if (data.success) {
var currDateAndTime = " Page last saved on: " + getCurrentDateAndTime();
$('[id=' + messageDivId + ']').text(currDateAndTime).show('normal', function() { })
}
else {
alert('redirecting to login page');
redirectToLogin();
//$('[id=' + messageDivId + ']').text(' An error occurred while attempting to auto save this page.').show('normal', function() { })
//alert('ERROR: Page was not auto-saved properly!!!!');
}
}
};
$('form').ajaxSubmit(options);
}
I try doing a javascript redirect in redirectToLogin() but it doesn't seem to get the url or something behind the scenes is blowing up. Here is how it's defined:
function redirectToLogin() {
window.location = "Account.aspx/LogOn";
}
best way to solve this is to have your code always return an Json result, i use a model called StandardAjaxResponse that has an ID, a Message and an answer answer is always false unless my code completes in the correct way and sets this to true. Any errors from try / catch are placed into the message field, so if !data.Answer and the Message is equal to not loggged in the you can then location.href to the login page, without getting the login page as your ajax response.
for example:
public class AjaxGenericResponse{
public bool Answer {get;set; }
public int Id {ge; set; } // this is for cases when i want an ID result
public string Mesage {get;set;} // this is so i can show errors from ajax
}
the controller / action
public JsonResult DoAutoSave(Data data){
var JsonResults = new AjaxGenericResponse{Answer=false};
// do code here to save etc
// no matter what always return a result, even if code is broken
return Json(model);
}
your Javascript:
$.ajax({
url:"",
dataTYpe: 'json',
success:function(data){
if(data.Answer) {
// all is good
} else {
if(data.Message === "logout') { href.location=('login'); } else { alert(data.Message); }
}
}
});
thats one solution anyway!
Stupid me. Thanks for your response minus, but I think our solutions coincided for the answer. My issue was I didn't have the right url to redirect to in the redirectToLogin method. I've made minor tweaks, and presto, its redirecting.
Javascript changes:
function redirectToLogin(url) {
window.location = url;
}
function initAutoSave(targetUrl, messageDivId) {
var options = {
url: targetUrl,
type: 'POST',
beforeSubmit: showRequest,
success: function(data, textStatus) {
//alert('Returned from save! data: ' + data);
if (data.success) {
var currDateAndTime = " Page last saved on: " + getCurrentDateAndTime();
$('[id=' + messageDivId + ']').text(currDateAndTime).show('normal', function() { })
}
else {
alert('redirecting to login page');
redirectToLogin(data.url);
//$('[id=' + messageDivId + ']').text(' An error occurred while attempting to auto save this page.').show('normal', function() { })
//alert('ERROR: Page was not auto-saved properly!!!!');
}
}
};
$('form').ajaxSubmit(options);
}
Action changes
if (!shouldAutoLogOffUser(RouteData.GetRequiredString("action")))
return Json(new { success = true, url = "" });
else
return Json(new { success = false , url = Url.Action("LogOff","Account").ToString() });
The shouldAutoLogOffUser checks a session variable that was updated by an action filter to track the # of contiguous auto saves and handles the logic to see if that value has exceeded the max # of contiguous autosaves allowed. The action filter checked the actionname for 'AutoSave' and if it found it, the counter was incremented. Otherwise the counter was reset to 0 (a non autosave post occurred).
One more random question. If this application were loaded in an IFrame and the window.location call is made, would the IFrame content be changed or the entire page (the container in essence) be changed? Our company is looking to run some of our asp.net mvc 2 apps in IFrame's via websphere portal (yeah, I know....it's not my choice).
Now this is just absurd...So, I was looking over my applications (I've got several going to QA soon) and noted that I've already solved this very question with a much better solution - it was ALL handled in an ActionFilter. I wanted this from the getgo when I asked this question, but to have already implemented it, forgot about that, AND ask again on Stack Overflow...well, I hope my memory issues helps somebody with this. Below is the full action filter code. As always, I'm open to criticism so mock it, revise it, copy it, etc, etc.
public class UserStillActiveAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
int sessionTimeoutInMinutes = int.Parse(ConfigurationManager.AppSettings["SESSION_TIMEOUT"].ToString());
int maxContiguousAutoSaves = int.Parse(ConfigurationManager.AppSettings["MAX_CONSEC_SAVES"].ToString());
int autoSaveIntervalInMinutes = int.Parse(ConfigurationManager.AppSettings["AUTO_SAVE_INTERVAL"].ToString());
string actionName = filterContext.ActionDescriptor.ActionName;
string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
HttpContext currentSession = HttpContext.Current;
LogAssociateGoalsSessionStatus(filterContext.HttpContext, actionName);
if (actionName.ToLower().Contains("autosave"))
{
int autoSaveCount = GetContigousAutoSaves(filterContext.HttpContext);
if (autoSaveCount == maxContiguousAutoSaves)
{
var result = new RedirectResult("~/Account.aspx/LogOff");
if (result != null && filterContext.HttpContext.Request.IsAjaxRequest())
{
//Value checked on Logon.aspx page and message displayed if not null
filterContext.Controller.TempData.Add(PersistenceKeys.SessionTimeOutMessage,
StaticData.MessageSessionExpiredWorkStillSaved);
string destinationUrl = UrlHelper.GenerateContentUrl(
result.Url,
filterContext.HttpContext);
filterContext.Result = new JavaScriptResult()
{
Script = "window.location='" + destinationUrl + "';"
};
}
}
else
{
RefreshContiguousAutoSaves(filterContext.HttpContext, autoSaveCount + 1);
}
}
else
{
RefreshContiguousAutoSaves(filterContext.HttpContext, 1);
}
}
private int GetContigousAutoSaves(HttpContextBase context)
{
Object o = context.Session[PersistenceKeys.ContiguousAutoUpdateCount];
int contiguousAutoSaves = 1;
if (o != null && int.TryParse(o.ToString(), out contiguousAutoSaves))
{
return contiguousAutoSaves;
}
else
{
return 1;
}
}
private void RefreshContiguousAutoSaves(HttpContextBase context,
int autoSavecount)
{
context.Session.Remove(PersistenceKeys.ContiguousAutoUpdateCount);
context.Session.Add(PersistenceKeys.ContiguousAutoUpdateCount,
autoSavecount);
}
private void LogAssociateGoalsSessionStatus(HttpContextBase filterContext, string actionName)
{
AssociateGoals ag = (AssociateGoals)filterContext.Session[(PersistenceKeys.SelectedAssociateGoals)];
bool assocGoalsIsNull = false;
bool assocGoalsInformationIsNull = false;
if (ag == null)
{
assocGoalsIsNull = true;
assocGoalsInformationIsNull = true;
}
else if (ag != null && ag.AssociateInformation == null)
assocGoalsInformationIsNull = true;
}
}
always use double quote in java script and jquery to avoid browser specific issues
like
dataTYpe: 'json' must be as "dataTYpe:"json"