Controller does not catch JavaScript variable - javascript

I am working in ASP.NET Boilerplate (ABP) and AngularJS. I am using controllers (to upload files) with Kendo upload on frontend. To access controller I am using clicking kendo button clicking it like:
($("#files").data("kendoUpload")).options.async = vm.uploadOptions(onUpdate);
$('.k-upload-selected').click();
Function vm.uploadOptions(onUpdate) takes list of Ids and returns retObject like
var retObject = { saveUrl: '/ControllerName/ActionName?id=15&id=16', autoUpload: false, batch: true }
Now the actual question:
When I assign saveUrl in retObject like above or like
retObject.saveUrl = '/ControllerName/ActionName?id=195&id=196&id=197'
(ids hardcoded), the controller is entered (I have a breakpoint there) and I have a C# List with two elements.
When I assign url like:
vm.url = '/ControllerName/ActionName?fileId=' + fileIds[0];
len = fileIds.length;
for (var i = 1; i < len; i++) {
vm.url += '&fileId=' + fileIds[i];
}
retObject.saveUrl = vm.url;
the controller is not entered.
Finally (this is what I use in code now), when I assign like
vm.url = '?fileId=' + fileIds[0];
len = fileIds.length;
for (var i = 1; i < len; i++) {
vm.url += '&id=' + fileIds[i];
}
retObject.saveUrl = '/ControllerName/ActionName' + vm.url;
it does work - controller is entered with a proper list of ids.
When I copied dynamically generated (not working) string and assigned it as hardcoded it started working.
Why it happens, I mean: why exactly the same string initialized in different ways makes different results?

Related

Issue with linking values from pre-defined list to data argument

I am programming a task-switching experiment in javascript using the jsPsych library (jsPsych 6.0.5). I use a predefined list with stimulus attributes but I can't seem to link the specific values to variables in the data argument (output of data is undefined).
I am using a for-loop to link these values to the data argument, but that doesn't seem to be working. In the sample here below the first for-loop reads in the different columns of the predefined list, with each row representing a single trial. In the second for-loop, I try to input these values for each trial (row) to the data argument of my test_stimuli
for(this_trial = 0; this_trial < blocks[0].length; this_trial ++){
curr_trial = blocks[0][this_trial];
modality[this_trial] = curr_trial[6];
cresp[this_trial] = curr_trial[10];
perc_stim[this_trial] = [];
for(j = 0; j < 4; j++){perc_stim[this_trial][j] = curr_trial[11 + j];};
probe[this_trial] = curr_trial[15];
condition[this_trial] = curr_trial[16];
}
for (i = 0; i < blocks[0].length; i++) {
test_stimuli[i] = [{
image: `<img src="images_a/${perc_stim[i][0]}.bmp" class = "training1"/>`,
data: {
correct_response: [cresp[i]],
modality: modality[i],
trial_id: 'stim'
}
}];
}
When I log the data argument to the console, I get "undefined" (even though it logs the correct values when looking at for example the cresp array).

Object array gets overwritten by the last object

I have this array of objects being loaded:
$(document).ready(function () {
$("<div></div>").load("/Stats/Contents #stats", function () {
statcount = $(".list-group-item", this).length;
for (var j = 0; j < statcount; j++) {
statList.push(stat);
}
for (var i = 0; i < statcount; i++) {
statList[i].statId = document.getElementById("statId-" + (i + 1) + "").value;
statList[i].productDescription = document.getElementById("productType-" + (i + 1) + "").value;
statList[i].lastMeasuredInventoryAmount = document.getElementById("statLastMeasureAmount-" + (i + 1) + "").value;
}
)}
)}
.... and so on
Then I get the changed values and save them, however, in the ajax post call, all of the array objects are same (the last one assigned), looks like they get overwritten.
Any ideas? I saw these deferred/promise type code but not sure if there's simpler way.
Thanks.
It sounds like you take the statList array and then push that up to the server, with any respective change. Rather than building and maintaining a list like this, have you thought of switching it around and just grabbing the results out of the markup and building up the array at save point?
$("btnSave").on("click", function(e) {
var data = [];
$(".list-group-item").each(function() {
data.push({
statId: $(this).find(".statid").val(),
.
.
})
});
You wouldn't need to give every element an ID (but could) as my sample uses CSS classes to find the element within the current list item. Additionally, if these are inputs, you could serialize them from the root element more efficiently...

How to improve typeAhead performance on xPage?

Here is my inputText control with typeAhead enabled:
<xp:inputText id="inputNameEditBox">
<xp:typeAhead
mode="full"
minChars="3"
ignoreCase="true"
valueList="#{javascript:return mytypeAheadList();}"
var="searchValue"
valueMarkup="true"
id="typeAhead1">
</xp:typeAhead>
</xp:inputText>
SSJS mytypeAheadList() function calls custom Java userTools.userLookup() function to get a set of suggestions. (Our server cannot access corporate directory so we have to use LDAP HTTP Java API).
SSJS library:
function mytypeAheadList(){
var v=new userTools.userLookup(); //Java library
var usrList = v.getUserList(searchValue);
var lenList = usrList.length;
var retList = "<ul>";
if(lenList>0){
for (var i=0; i<lenList; i++) {
var matchDetails:string = ["<li>",#Name("[ABBREVIATE]", #Left(usrList[i], "#")),"</li>"].join("");
retList += matchDetails;
}
} else {
retList += ["<li>","None found","</li>"].join("");
}
retList += "</ul>";
return retList;
}
So that means userTools Java object is created each time user type a character. Is there a way to avoid it, e.g. make var v a global variable on page load? Seems scope variables cannot accept Java objects.
I would do the following:
Implement the Serializable interface to your POJO returned by getUserLookup. This allows to store the object in viewScope
Limit the max size of lenlist. E.g. 20 results would reduce the time of looping, the size of the HTTP response and the performance in the browser
Cache the result of the search (add searchValue and the resulting HTML string to a map). If a user hits backspace, the whole result must not be recomputed.
Drop SSJS. Use Java.
optional: If possible, precompute the results.
EDIT
Something like this:
function mytypeAheadList(){
// check if value is already cached
if( viewScope.containsKey("search~" + searchValue) ){
return viewScope.get("search~" + searchValue);
}
// get the userLookup object
var v = null;
if( viewScope.containsKey("userLookup") ){
v = viewScope.get("userLookup");
}else{
v = new userTools.userLookup();
viewScope.put("userLookup", v);
}
// if usrList is "unlimited", limit the max size
var usrList = v.getUserList(searchValue);
var lenList = usrList.length > 20 ? 20 : usrList.length;
// if getUserList has a restriction parameter
var usrList = v.getUserList(searchValue, 20);
var lenList = usrList.length;
// build the list
var retList = null;
// reuse a variable is up to 2 times faster
var matchDetails = null;
if(lenList>0){
retList = "<ul>";
for (var i=0; i<lenList; i++) {
// concatenating a string is up to 2 times faster then join
matchDetails = "<li>" + #Name("[ABBREVIATE]", #Left(usrList[i], "#")) + "</li>";
retList += matchDetails;
}
retList += "</ul>";
} else {
// why join a static string?
retList = "<ul><li>None found</li></ul>";
}
// put the result to the cache
viewScope.get("search~" + searchValue, retList);
return retList;
}
Yes you can do that.
Either you can put var v outside of your function to keep it loaded (and just lazy load it first time by checking if it is null).
Or you can put it all in a Java bean and let the scope of the bean determine how long you want to keep the data: ViewScope for just this page - ApplicationScope to allow all users to share it (and you can build in a check to force a maximum age of xx minutes - this could be relevant to consider if the data you look up could change).

Sort input files after selection

Is there any way for me to sort files for POST to server using any client side solution?
More specific, i am using the following tag <input type="file" name="Myfiles[]" multiple> To choose some images.
With the code at the end i display a preview of the images and using jQuery UI sortable i'm able to change the position of the elements.
So, when i post the data to the server how can i insert the files in the order of the sort? that's the point where i'm stuck
for(var i = 0; i< this.files.length; i++)
{
var file = this.files[i];
if(!file.type.match(‘image’))
continue;
var picReader = new FileReader();
picReader.addEventListener('load',function(event){
var picFile = event.target;
$('#sortable').append('<li><img height="100" width="100" src="'+picFile.result+'" /></li>');
});
picReader.readAsDataURL(file);
}
Assuming you are storing the files into an array:
var storedFiles = [];
You can create a hidden field to store the IDs of the images in the order you want (3,1,2,4..) These IDs must be generated after your images are selected.
Then when the upload button is clicked, grab the sorted contents of the hidden input field and run a for loop to reprocess the order of the files.
var data = new FormData();
var items_array = $('.cvf_hidden_field').val();
var items = items_array.split(',');
for (var i in items){
var item_number = items[i];
data.append('files' + i, storedFiles[item_number]);
}
Then append the sorted files into the variable data, then send it using AJAX:
$.ajax({
url: 'upload.php',
type: 'POST',
contentType: false,
data: data,
processData: false,
cache: false,
success: function(response, textStatus, jqXHR) {}
});
The order in which the server receives the files will be the order in which they were placed in the form to be submitted to the server.
That means it's probably easier to re-order them client-side before submitting e.g. by re-ordering the order in which they appear in the form for submission. Heres a rough-and-ready snippet of what you could use:
var newOrder = [];
for(var i = 0; i< this.files.length; i++){
var indiv_file = this.files[a];
// processing to calculate desired array position for submission
idealPos = function_returning_new_array_position(indiv_file);
newOrder[idealPos] = this.files[a];
}
Thus re-order your 'this.files' array to reflect your chosen order. And use the newOrder array in the form when you submit the files. I can't quite make out what you're doing in the above code, but unless your users are expecting their images to be re-ordered, it could turn out a bit disorienting to see the files they are planning to submit jumping around.
You can use plain JavaScript code to sort the files using the file names and store them as an array.
var files = evt.target.files
var RESULT = []
var m = files.length
for (var a = 0; a < m; a++) {
var min = 0
for (var b = 0; b < (m - 1) - a; b++) {
if ((files[b].name).localeCompare(files[b + 1].name) == 1) {
min = b + 1
}
}
RESULT[a] = files[min]
delete files[min]
}
The above code sorts the uploaded files in ascending order based on the file name and stores it in RESULT.

MVC3 jquery post serialize array

In my website there is a table, in every td, there is a div with <input> inside.
I run over the table and serialize every div and post it to the server.
for (var r = 0; r < tbodyRow.length; r++) {
var tbodyCols = tbodyRow[r].cells;
for (var c = 0; c < tbodyCols.length; c++) {
row = r + 1;
cell = c + 1;
div = $("#tbody tr:nth-child(" + row + ") td:nth-child(" + cell + ") :input").serialize();
if (div != "") {
$.post("../Contoller/Action?Mode=" + Mode, div, function () { });
}
tbodyCounter++;
};
};
and in the server - the action have an object as parameter that get it.
I would like to post all the list of the object (that i have in the divs) just once
and get it as List<T> in the server side.
is it possible ?
Yes, model binding allows to do that. Refer below article
http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
Also refer below one for introduction
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
Have the inputs inside the form tag and serialize form and send it.
$.post("#Url.Action("YourAction","YourController")",
$("#yourFormID").serailize(),function(data){
});
In the controller, you can use the model /viewmodel which your view was strongly typed to , as the parameter so that MVC model binding will bind that posted data to it.
[HttpPost]
public ActionResult YourAction(YourViewModel model)
{
//do something
}

Categories