Should i use threads when executing action method through AJAX? - asp.net-mvc-3

I am building a questionnarie. When a user clicks on an answer possibility for a multiple choice question (this is a radio button), i call an action method to save this answer.
The code:
<script language="javascript">
$(document).ready(function () {
$('.MCQRadio').click(function () {
var question_id = $(this).attr('question-id');
var mcq_id = $(this).attr('mcq-id');
$.ajax({
url: '/SaveSurveyAnswers/SaveMCQAnswer',
data: { "mcq_id": mcq_id, "question_id": question_id },
success: function (data) {
}
});
});
});
The code to save the answer:
public EmptyResult SaveMCQAnswer(int mcq_id, int question_id)
{
MCQ_Answers mcqa = null;
try
{
mcqa = db.MCQ_Answers.Single(x => x.question_ID == question_id);
}
catch (InvalidOperationException e)
{
}
if (mcqa != null)
{
mcqa.mcq_id = mcq_id;
}
else
{
MCQ_Answers mcq_answer = new MCQ_Answers()
{
question_ID = question_id,
respondent_id = 1
};
db.MCQ_Answers.AddObject(mcq_answer);
}
db.SaveChanges();
return new EmptyResult();
}
If a question has 5 answer possibilities, and i click on them randomly and fast, and then go back to the previous page, ie, when i return the correct answer wont be saved. Should i use threading to make sure the correct answer is saved? And how?
Thanks

rather than saving your answer by post all the time, you can just create a JSOn object and save the answers within json. you can then at the end post all completed answers in one go.
take a look at this: http://msdn.microsoft.com/en-us/scriptjunkie/ff962533
basically this will allow you to store session data - json on the remote machine you then just need an add, delete function and away you go....
i use this to huge extent in an application that would require the server to be updated with the location of objects on a canvas, however with sessvars i just keep all the X and Y locations within there and do a final push of JSON when i am done.
if you change pages, you can then get your values from the JSON object without a server call.
as a note you may also be better off with tabs or hiden sections of form, and therfor reduce the need to re-populate say page1, page2 etc as they will already be there, just hidden!

Related

publisher initiates twice, one proper one only to self

for some reason my publisher initiates twice when I create a new a new session. However the 2nd one, isn't in the div where it's supposed to be. Also if you connect to the session you'll get the same so it only show for yourself.
I'm trying to find out why it's appearing. Here's some snippets:
var getApiAndToken, initializeSession;
​
getApiAndToken = function() {
var apiKey, customer_id, sessionId, token;
if (gon) {
apiKey = gon.api_key;
}
if (gon) {
sessionId = gon.session_id;
}
if (gon) {
token = gon.token;
}
if (gon) {
customer_id = gon.customer_id;
}
initializeSession();
};
​
initializeSession = function() {
var publishStream, session;
session = OT.initSession(apiKey, sessionId);
session.connect(token, function(error) {
if (!error) {
session.publish(publishStream(true));
layout();
} else {
console.log('There was an error connecting to the session', error.code, error.message);
}
});
$('#audioInputDevices').change(function() {
publishStream(false);
});
$('#videoInputDevices').change(function() {
publishStream(false);
});
return publishStream = function(loadDevices) {
var publisherOptions;
publisherOptions = {
audioSource: $('#audioInputDevices').val() || 0,
videoSource: $('#videoInputDevices').val() || 0
};
OT.initPublisher('publisherContainer', publisherOptions, function(error) {
if (error) {
console.log(error);
} else {
if (loadDevices) {
OT.getDevices(function(error, devices) {
var audioInputDevices, videoInputDevices;
audioInputDevices = devices.filter(function(element) {
return element.kind === 'audioInput';
});
videoInputDevices = devices.filter(function(element) {
return element.kind === 'videoInput';
});
$.each(audioInputDevices, function() {
$('#audioInputDevices').append($('<option></option>').val(this['deviceId']).html(this['label']));
});
$.each(videoInputDevices, function() {
$('#videoInputDevices').append($('<option></option>').val(this['deviceId']).html(this['label']));
});
});
}
}
});
};
};
it also asks me for device access twice.
I see two general problems in the code you provided:
The variables api_key, session_id, and token inside the getApiAndToken() function are scoped to only that function, and therefore not visible inside initializeSession() where you try to use them.
The goal of the publishStream() function is not clear and its use is not consistent. Each time you invoke it (once the session connects and each time the dropdown value changes) this function creates a new Publisher. It also does not return anything, so when using it in the expression session.publish(publishStream(true)), you are effectively just calling session.publish() which results in a new Publisher being added to the end of the page because there is no element ID specified. That last part is the reason why you said its not in the <div> where its supposed to be.
It sounds like what you want is a Publisher with a dropdown to select which devices its using. I created an example of this for you: https://jsbin.com/sujufog/11/edit?html,js,output.
Briefly, the following is how it works. It first initializes a dummy publisher so that the browser can prompt the user for permission to use the camera and microphone. This is necessary for reading the available devices. Note that if you use a page served over HTTPS, browsers such as Chrome will remember the permissions you allowed on that domain earlier and will not have to prompt the user again. Therefore on Chrome, the dummy publisher doesn't cause any prompt be shown for a user who has already run the application. Next, the dummy publisher is thrown away, and OT.getDevices() is called to read the available devices and populate the dropdown menu. While this is happening, the session would have also connected, and on every change of the selection in either of the dropdowns, the publish() function is called. In that function, if a previous publisher existed, it is first removed, and then a new publisher is created with the devices that are currently selected. Then that new publisher is passed into session.publish().

making an Ajax call on 'Save' button click on popup

I am working on a form where the user clicks on Register button, then a popup opens for the user to enter the details for registering....The user name should be unique for this, so when the user clicks on the submit button im checking with the database whether that username is already existing or not, if it already exists the im showing the error message on the same popup that 'username already exists'...
This is my code for that which gets fired on the save button click
$.ajax({
url: '#Url.Content("~/Registration/CheckUNameExists")',
type: 'GET',
data: { UName: $('#UserName').val() },
success: function (data) {
if (data == 'True') {
$('#UserAddError').html('User Name already exists!');
} else {
$("#update-message").html('');
$("#addUserForm").submit();
$(this).dialog("close");
}
}
});
public JsonResult CheckUNameExists(string UName)
{
}
[HttpPost]
public ActionResult RegisterUser(Regitration Reg)
{
}
This is working fine, but in some rare cases this is giving a problem. After filling the details in the popup when the user clicks the save button some times its taking 3-4 seconds time for it to save and in that case the users are clicking the save button 2-3 times...In that case the save button click is checking the method CheckUNameExists on for the 1st time and then RegisterUser is being called multiple times based on the number of times the user clicks the save button nd the same records are getting saved into db multiple times..
For testing purposes i put a breakpoint so that i can click the 'save' button n number of times, then clicked the save button 4 times and CheckUNameExists was hit only the 1st time but after that RegisterUser was called 4 times and the record was inserted 4 times.
Don't understand the issue here and how to get rid of it?
You are sending a GET ajax request to check if the user exists, so the respone is probably getting cached by the browser. jQuery provides a setting for the ajax function that you can use for enabling/disabling caching which is enabled by default, see the cache setting here
Try disabling caching for that ajax call, adding the option cache:false to the ajax settings:
$.ajax({
url: '#Url.Content("~/Registration/CheckUNameExists")',
type: 'GET',
data: { UName: $('#UserName').val() },
cache: false,
success: function (data) {
...
That should fix the issue of CheckUNameExists not being called.
However you can still have race conditions with multiple ajax calls. A better solution would be to send a single ajax POST that does both the checking of the unique name and the user registration. This should prevent errors due to the asynchronous nature of ajax calls, when your GET for veryfing the user happens before the first time the button was clicked finishes submitting the form.
This will require a bit of refactoring:
$(".actionSubmit").click(function () {
$(".actionSubmit").attr('disabled', 'disabled');
$.ajax({
url: '#Url.Content("~/Account/RegisterUser")',
type: 'POST',
data: $("#addUserForm").serializeArray(),
success: function (data) {
if (!data.success) {
$('#UserAddError').html(data.errorMessage);
$(".actionSubmit").removeAttr('disabled');
} else {
$(this).dialog("close");
//Possibly redirect here, same logic you had in RegisterUser method
}
}
});
return false;
});
[HttpPost]
public ActionResult RegisterUser(Regitration Reg)
{
//Check if user exists, whatever you were doing in CheckUNameExists
bool userAlreadyExists = ...;
if (userAlreadyExists)
{
return Json(new { success = false, errorMessage = "User Name already exists!" });
}
//Register user
...
return Json(new { success = true });
}
You may want to combine that solution with JavaScript that disables the button on its click event and does not enables it again until you are done (either because the name already exists or because the POST finished). I have showed this in the code above, where I am disabling/enabling the submit button.
The changes above should prevent issues related with requests coming from the same client. However you can still have users from different machines trying to register at the same time with the same user name. To prevent those errors you will need to wrap the C# code in a transaction scope, so both user verification and registration happen inside a transaction:
[HttpPost]
public ActionResult RegisterUser(LoginModel model)
{
using (var tran = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
//Check if user exists, whatever you were doing in CheckUNameExists
bool userAlreadyExists = ...;
if (userAlreadyExists)
{
return Json(new { success = false, errorMessage = "User Name already exists!" });
}
//Register user
...
tran.Complete();
}
return Json(new { success = true });
}
Hope this helps!

Kendo grid batch editing - making a single call to save

With Kendo grid batch editing turned on, I know that you can hook into the create, update and destroy commands where Kendo will send 3 separate commands to the server when you click on Save Changes.
I was wondering if there was any way to send all three sets of updates as a single call to the server -like a transaction. Or even send each in a specified order, with a check for success before sending the next .
The only way I could come up with was to have a custom Save Changes implementation which ,when invoked, would lookup the grid datasource to find out all rows that have been added (isNew() for added rows), deleted (_destroyed for deleted rows), updated (isDirty for updated rows) and then craft my own call to a server endpoint using ajax using the identified datasets.
Telerik posted a work-around in their code library recently: http://www.kendoui.com/code-library/mvc/grid/save-all-changes-with-one-request.aspx. Unfortunately the work-around is rather bare-bones. It gives a good example of how to capture destroyed, dirty, and new records but finishes with some hand waving to handle any errors in the response and synchronizing the data source on success. Also note that there is no check to ensure there are destroyed, dirty, or new records before making the ajax request.
Here is the relevant code. Download the full example from the link above to see how the grid is setup and to ensure you have the latest version.
function sendData() {
var grid = $("#Grid").data("kendoGrid"),
parameterMap = grid.dataSource.transport.parameterMap;
//get the new and the updated records
var currentData = grid.dataSource.data();
var updatedRecords = [];
var newRecords = [];
for (var i = 0; i < currentData.length; i++) {
if (currentData[i].isNew()) {
//this record is new
newRecords.push(currentData[i].toJSON());
} else if(currentData[i].dirty) {
updatedRecords.push(currentData[i].toJSON());
}
}
//this records are deleted
var deletedRecords = [];
for (var i = 0; i < grid.dataSource._destroyed.length; i++) {
deletedRecords.push(grid.dataSource._destroyed[i].toJSON());
}
var data = {};
$.extend(data, parameterMap({ updated: updatedRecords }), parameterMap({ deleted: deletedRecords }), parameterMap({ new: newRecords }));
$.ajax({
url: "/Home/UpdateCreateDelete",
data: data,
type: "POST",
error: function () {
//Handle the server errors using the approach from the previous example
},
success: function () {
alert("update on server is completed");
grid.dataSource._destroyed = [];
//refresh the grid - optional
grid.dataSource.read();
}
})
}
Maybe you can enable the batch property of the Datasource
batch Boolean(default: false)
If set to true the data source will batch CRUD operation requests. For example updating two data items would cause one HTTP request instead of two. By default the data source makes a HTTP request for every CRUD operation.
Source : Datasource API
After six years we have an answer, check submit function to execute single request to save all changes: https://docs.telerik.com/kendo-ui/api/javascript/data/datasource/configuration/transport.submit

Angular $http returning new values only once

I am new to Angular, and set up a simple example with a REST Api config in Codeigniter that returns a json (default) thread list. No problems!
Until, I add an update to the Database. If I clear/then call getThreads again, I receive the same list of items. A page refresh solves this. I can see in firebug that its only calling the url:api/example/threadlist/id/'x' once per page load.
function ThreadsCtrl($scope, $http, $templateCache) {
$scope.getThreads = function(id) {
if (!id) { id = 'reset'; }
$http({method: 'GET', url: 'api/example/threadlist/id/' + id, cache: $templateCache}).
success(function(data) {
$scope.threadslist = data; //set view model
}).
error(function(data) {
$scope.threadslist = data || "Request failed";
});
};
How would I make it so that it always calls a new list of data rather than reuses the old.
Thanks!
If i understood your question correctly your ajax call is being cached so you have to remove cache:$templatecache from your code

Filtering a loaded kml file in OpenLayers

I'm trying to create an interactive search engine (for finding event tickets) of which one of its features is a visual map that shows related venues using OpenLayers. I have a plethora of venues (3000+) in a kml file that I would like to selectively show a filtered subsection of. Below is the code I have but when I try to run it has a JavaScript error. Running firebug and chrome developer tools makes me think that it is not getting passed the parameters I give because it says that the variables are null. However, I cannot figure out why they are not getting passed. Any insight is greatly appreciated.
var map, drawControls, selectControl, selectedFeature, select;
$('#kml').load('venuesComplete.kml');
kml=$('#kml').html();
function showVenues(state, city, venue){
filterStrategy = new OpenLayers.Strategy.Filter({});
var kmllayer = new OpenLayers.Layer.Vector("KML", {
strategies: [filterStrategy,
new OpenLayers.Strategy.Fixed()],
protocol: new OpenLayers.Protocol.HTTP({
url: "venuesComplete.kml",
format: new OpenLayers.Format.KML({
extractStyles: true,
extractAttributes: true
})
})
});
select = new OpenLayers.Control.SelectFeature(kmllayer);
kmllayer.events.on({
"featureselected": onFeatureSelect,
"featureunselected": onFeatureUnselect
});
map.addControl(select);
select.activate();
filter = new OpenLayers.Filter.Comparison({
type: OpenLayers.Filter.Comparison.LIKE,
property: "",
value: ""
});
function clearFilter(){
filterStrategy.setFilter(null);
}
function setFilter(property, value){
filter.value = value;
filter.property = property;
filterStrategy.setFilter(filter);
}
var vector_style = new OpenLayers.Style();
if(venue!=""){
setFilter('name', venue);
}else if(city!=""){
setFilter('description', city);
}else if(state!=""){
setFilter('description', state);
}
map.addLayer(kmllayer);
function onPopupClose(evt) {
select.unselectAll();
}
function onFeatureSelect(event) {
var feature = event.feature;
var selectedFeature = feature;
var popup = new OpenLayers.Popup.FramedCloud("chicken",
feature.geometry.getBounds().getCenterLonLat(),
new OpenLayers.Size(100,100),
"<h2>"+feature.attributes.name + "</h2>" + feature.attributes.description +'<br>'+feature.attributes,
null,
true,
onPopupClose
);
document.getElementById('venueName').value=feature.attributes.name;
document.getElementById("output").innerHTML=event.feature.id;
feature.popup = popup;
map.addPopup(popup);
}
function onFeatureUnselect(event) {
var feature = event.feature;
if(feature.popup) {
map.removePopup(feature.popup);
feature.popup.destroy();
delete feature.popup;
}
}
}
function init() {
map = new OpenLayers.Map('map');
var google_map_layer = new OpenLayers.Layer.Google(
'Google Map Layer',
{type: google.maps.MapTypeId.HYBRID}
);
map.addLayer(google_map_layer);
state="";
state+=document.getElementById('stateProvDesc').value;
city="";
city+=document.getElementById('cityZip').value;
venue="";
venue+=document.getElementById('venueName').value;
showVenues(state,city,'Michie Stadium');
map.addControl(new OpenLayers.Control.LayerSwitcher({}));
map.zoomToMaxExtent();
}
IF I UNDERSTAND CORRECTLY, your kml does not load properly. if this is not the case, please disconsider my answer.
it is very important to check if your kml layer was properly loaded. i have a map that loads multiple dynamic (from php) kml layers and it is not uncommon to have a large layer simply not load. when that happens, the operation is aborted, but, as far as openlayers is concerned, the layer was properly loaded.
so i do 2 things: i check if the amount of loaded data meets the expected number of features in my orginal php kml parser (i use a jquery or ajax call for that) and then, in case there is a discrepancy, i try reloading (since this is a loop, i limit it to 5 attempts, so as not to loop infinitely).
check out some of my code here

Resources