I have a table with rows that is sorted based on the INT Priority in my database.
Every row has a <input type="hidden" /> with an ID reference to the database. It also have some up and down arrows (class .up and .down) with the following JavaScript to move the row.
$(document).ready(function(){
$(".up,.down").click(function(){
var row = $(this).parents("tr:first");
if ($(this).is(".up")) {
row.insertBefore(row.prev());
} else {
row.insertAfter(row.next());
}
});
});
Now my question is, how to I update the priority in the database? I need somehow to get the order of the ID's and update the priority column - is there a neat solution for this?
Use jQuery to get a list of the IDs after you have moved a row. Something like:
var inputs = $("#myTable").find("tr").find("input");
// store each rows id in an array - this will be in the correct order
var ids = [];
inputs.each(function(){
ids.push($(this).val());
});
Then pass this list to a PageMethod or WebService and loop through the list, setting the priority of each row in the database. Obviously you will also need to take into account paging or any filtering you have applied to the items too.
Update:
You might want to also look at jQueryUI Sortable to enable drag/drop sorting. You would update the db in the same way.
Update 2:
Here is a simple delay function.
var delay = (function () {
var timer = 0;
return function (callback, ms) {
clearTimeout(timer);
timer = setTimeout(callback, ms);
};
})();
Then use it as follows:
delay(function () {
MySortFunction();
}, 300);
This will delay the function for x milliseconds and cancel any previous calls if you call it again within the specified time.
well, if you also update the prioirty number (index?) when you reorder two rows (),
right in the handler for 'up' / 'down' next to the row.insertBefore(row.prev());
then you could simply loot through the rows on the server and generate a simple "update x set priority = #priority where id = #id"
This may be overkill but I wanted to randomly move a row more than one row up or down. Still have to submit back to the server and this snippet doesn't have the <form> tag, but it should be pretty easy to process the inputs into a sort order based on looping through the input name form.hdrSort* values.
<cfoutput>
<script type="text/javascript">
var maxHdr=#qX.recordCount#;
var curHdr=0;
$(document).ready(function(){
bindReorder();//set with function because when the table gets regenerated, the ordering functionality gets stripped and has to be rebound to the content
});
function bindReorder(ok2do){
if(ok2do==null) ok2do=true;
$("input[id^='hdr']").each(function(){
$(this).mask("?999").focus(function(){
curHdr=parseInt($(this).val());//original value held in global js var
}).blur(function(){
var tVal=parseInt($(this).val());//entered value
if(curHdr!=tVal){
var tId =parseInt($(this).attr("id").substr(3));//id of changed value - this is the new value we don't change'
if(tVal>#qX.recordCount# || tVal<1){//validate entered is in scope
alert("please enter a positive number less than or equal to #qX.recordCount#");
$(this).val(curHdr);
}else if(ok2do){
var lo=Math.min(tVal,curHdr);//lower of original and entered values
var hi=Math.max(tVal,curHdr);//higher of original and entered values
var upDn=1;//default that entered value is less than original value
var aryHdrTbls=new Array(#qX.recordCount#+1);//zero based
if(lo==curHdr) upDn=-1;
$("input[id^='hdr']").each(function(i){
var checkVal=parseInt($(this).val());
var thisId=parseInt($(this).attr("id").substr(3));
if(checkVal<=hi && checkVal>=lo){
if(thisId!==tId) $(this).attr("value",checkVal+upDn);
else $(this).attr("value",checkVal);
aryHdrTbls[$(this).val()]=$("##tbl"+thisId).parent().html();
}
});
for(var i=lo; i<=hi; i++){
$("##td"+i).html(aryHdrTbls[i]);
}
bindReorder(false);
}
}
});
});
}
</script>
<table width="80%">
<cfloop query="qX">
<tr>
<td id="td#qX.currentRow#">
<table style="width:100%;border:1px solid gray;" id="tbl#qX.currentRow#">
<tr>
<td colspan="2" style="background-color:##dddddd;">
<div style="float:right;"><input name="hdrSort#qX.currentRow#" id="hdr#qX.currentRow#" size="1" value="#qX.currentRow#"> <input type="button" width="3" value="go"></div></td>
</tr>
<tr>
<td>#qX.currentRow# #qX.nada#</td>
<td>#qX.nada# more your content original header #qX.currentRow#</td>
</tr>
</table>
</td>
</tr>
</cfloop>
</cfoutput>
</table>
Related
In an MVC application, I have a list of exam questions and I want to present a small number of them to the user on the same page but where each answer can be submitted separately.
So my page looks like this ....
The view code is ....
#model List<QuestionResponseVM>
#for (int i = 0; i < Model.Count(); i++)
{
using (Html.BeginForm("CheckQuestions", "Checks", FormMethod.Post, new {questResponses = Model[i] }))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model[i].QuestionID)
<tr>
<td width="35%">
#Html.Raw(Model[i].QuestionText)
#Html.HiddenFor(model => model[i].QuestionText)
</td>
<td>
#Html.TextAreaFor(model => model[i].Response, new { #name = "DisplayTextEdit", #id = "DisplayTextEdit", #rows = 1, #cols = 80 })
</td>
<td width="30%">
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</div>
</td>
</tr>
}
}
My problem is I can only get data returned to the POST method for Question 1.
Here is the Controller Code ....
public class ChecksController : Controller
{
public ActionResult CheckQuestions()
{
return View(LoadQuestions());
}
// POST: Checks
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CheckQuestions(List<QuestionResponseVM> questResponses)
{
List<QuestionResponseVM> testList = new List<QuestionResponseVM>();
if (ModelState.IsValid)
{
testList = LoadQuestions(questResponses[0].QuestionID, questResponses[0].Response);
}
return View(testList);
}
private List<QuestionResponseVM> LoadQuestions (int _QuestionID = -1, string _Response = "")
{
List<QuestionResponseVM> thisList = new List<QuestionResponseVM>();
thisList.Add(new QuestionResponseVM()
{
QuestionID = 1,
QuestionText = "Question 1",
Response = (_QuestionID == 1 ? _Response : "")
});
thisList.Add(new QuestionResponseVM()
{
QuestionID = 2,
QuestionText = "Question 2",
Response = (_QuestionID == 2 ? _Response : "")
});
thisList.Add(new QuestionResponseVM()
{
QuestionID = 3,
QuestionText = "Question 3",
Response = (_QuestionID == 3 ? _Response : "")
});
return thisList;
}
}
If the Controller POST method has a parameter of QuestionResponseVM questResponses which is what I was expecting (hoping for) then null is returned from the view no matter which "Save" button is clicked.
However, if I change the parameter to a list (i.e. List<QuestionResponseVM> questResponses) then the "Save" button for Question 1 returns a list with a single item and correct data. But, any other "Save" button (e.g. Question 2 or Question 3) returns a null list.
The behaviour for scenario 1. seems counter-intuitive to me since the "Begin Form" is set to return a single model item (instance of the model) i.e. "Model[i]".
And in scenario 2., I just don't understand why it works for the first form ("Save" button) but not for the others.
I don't believe I should need to use JScript or AJAX to do this.
But clearly, I am not "connecting some dots" here.
Can someone please explain my observed behaviour and maybe give me a push in the right direction to meet this requirement.?
I would greatly appreciate any help.
Before going through your questions, I don't get what new {questResponses = Model[i] })) is doing in your forms:
using (Html.BeginForm("CheckQuestions", "Checks", FormMethod.Post, new {questResponses = Model[i] }))
{
...
}
Model[i] is a complex object. All you got there was the name of the object:
Q1: If the Controller POST method has just a single parameter
Since you're using a for loop to generate each form and inputs within the form, the name of those inputs will be in the forms of [INDEX].NAME:
By default, the model binding will bind those inputs (QuestionId, QuestionText and Response) to a matching object. QuestionResponseViewModel indeed matches that. The problem is [INDEX]. prefix.
In order for the default model binding to work, the parameter name you declare in the POST method has to be called [INDEX], i.e., [0] for the first form, [1] for the second form and so on:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CheckQuestions(QuestionResponseVM [0])
{
...
}
But you know we can't declare anything like that in C#.
The Fix for Q1
Instead of using the regular for loop, you can use foreach to generate each form. In that way, you get rid of the need for naming a parameter that's changing for each form.
Another "GOTYOU" here is that the parameter in the controller has to match the variable you declared in the for loop for each QuestionResponseViewModel:
#foreach (var qrVM in Model)
{
using(Html.BeginForm("..."))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(x => qrVM.QuestionId)
<tr>
<td>
#Html.DisplayFor(x => qrVM.QuestionId)
#Html.HiddenFor(x => qrVM.QuestionId)
</td>
...
</tr>
}
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CheckQuestions(QuestionResponseVM qrVM)
{
// If you name the parameter something else, it won't bind!
...
}
If you think about it, that makes sense, because you know the form will post data with keys like qrVM.QuestionId, qrVM.QuestionText back to the server. The default model binding will search for a model that has those properties and is named qrVM.
Q2: Change parameter to a list
When the first form posts back to the server, the form data in the request body will look like:
[0].RequestionId: 1
[0].RequestionText: Question 1
[0].Response: xxx
MVC model binding is still smart enough and thinks you're posting the first item of the list you declared. Hence you will see List<QuestionResponseVM> questResponses capture the correct data for the first form.
Well what about the second and third form? If you submit the data on the second form for example, the form data in the request body will look like:
[1].RequestionId: 2
[1].RequestionText: Question 2
[1].Response: xxx
MVC model binding sees it as the 2nd item of the list, but where's the 1st item? And it got confused so it couldn't bind the data to the parameter. Hence you will see NULL from the parameter List<QuestionResponseVM> questResponses.
My 2 cents
You actually cannot put a form inside a table or between table rows like this. It's considered as invalid HTML structure. It's never a good idea to use tables as structures to display data on the page anyway. Instead, You can use Bootstrap's row and columns.
I don't know why or what made you think you shouldn't need AJAX. Your case is like the best scenario to go with an AJAX approach! For example, with AJAX, the user can save each question's response individually. The page doesn't have to be refreshed.
Your save button on Question 1 is submitting the form to the controller. You will need to either have one Save/Submit button at the end of a set of questions and utilize the FormCollection object or spend time setting up JQuery/Ajax for click events on each button and removing the form element. You could have a bit of both if the button at the bottom becomes a 'Next' and then submits to a controller to get the next set of related questions.
I am trying to print my data coming from controller in a table but unable to do so. I am using Kendo UI template in order to print. I am attaching the code below along with the error that I am getting.
<div id="example"></div>
<script>
$.ajax(
{
type: 'POST',
url: '/default1/KendoDataAjaxHandle/',
dataType: 'json',
success: function (result) {
//Get the external template definition using a jQuery selector
var template = kendo.template($("#javascriptTemplate").html());
console.log(result);
var data = template(result); //Execute the template
$("#example").html(data); //Append the result
}
})
</script>
<script id="javascriptTemplate" type="text/x-kendo-template">
<table>
<thead>
<tr>
<th>Customer ID</th>
<th>Customer name</th>
<th>Gender</th>
</tr>
</thead>
<tbody>
# for (var i=0; i < result.length; i++){ #
<tr>
# for (var j=0; j < result[i].length; j++){ #
<td>
#= result[j] #
</td>
# } #
</tr>
# } #
</tbody>
</table>
</script>
What i am doing in the above code is making an ajax call to the action method and getting the results in JSON. Here's my controller method:
public ActionResult KendoDataAjaxHandle([DataSourceRequest]DataSourceRequest request)
{
IQueryable<Customer> products = db.Customers;
DataSourceResult result = products.ToDataSourceResult(request);
return Json(result, JsonRequestBehavior.AllowGet);
}
Error that I am getting on executing the code is: result is undefined. However on consoling the results returned after ajax call, I get an object which has all the values.
How do I print those values coming back in the table? Also please correct me if I have done something wrong here. Thank in advance.
Try changing the the variable name referenced inside the template from "result" to "data" as that is what Kendo uses in its template execution code.
Example: http://dojo.telerik.com/#Stephen/ENUze
Updated Example
Showing how to iterate the object's fields(based on comment):
http://dojo.telerik.com/#Stephen/oXOJU
NOTE: this assumes that the fields are listed in the order you have specified in your comment. If the fields don't come back in this order, you will have to add code to map the field name to the column index instead of just blindly looping.
Update 2:
Updated example using exact format of returned data:
http://dojo.telerik.com/#Stephen/ipeHec
Note: you have to deal with the CustomerAltID that is in the returned data but not in the table somehow. My example chooses to remove it from list of keys to process. How you deal with it is up to you.
I have a .cshtml page where jQuery data table is created dynamically. There are 10 columns and sorting is working for 8 column, first two columns are not working.
I have taken value from model like below
#foreach (var item in ViewData["OGWELLS"] as IEnumerable<PersonalPropertyCost>)
{
<tr>
<td class="table_heading text-right"> #Utility.FormattedLongValue(item.beg_value)
</td>
</tr>
}
And in utility.cs
public static string FormattedLongValue(long value)
{
var formattedvalue = string.Format(CultureInfo.InvariantCulture,{0:#,###0}", value);
return formattedvalue;
}
In database I have beg_value as integer, for e.g : beg_value is 1250 but after processing FormattedLongValue() method I am getting a 1,250 value.
But the problem is it is not sorted in jQuery data table. If I take only #item.value then only 1250 is displayed and sorting is enable.
But I want formatted value like 1,250 and it needs to sort on jQuery data table grid.
Please help me.
Get help from this.
1) http://live.datatables.net/oborug/2/edit
OR
2) You can do with your own logic not complex but trickiiiiiii.
a) Get the values in object
b) Remove the formatting,
c) Sort the values
d) Set the formatting
e) Set the values to table.
I wrote the below code for finding the records in a table grid.
$(function () {
grid = $('#tblsearchresult');
// handle search fields key up event
$('#search-term').keyup(function (e) {
text = $(this).val(); // grab search term
if (text.length > 1) {
// iterate through all grid rows
grid.find('tr').each(function (i) {
if ($(this).find('td:eq(1)').text().toUpperCase().match(text.toUpperCase()))
$(this).css({ background: "#A4D3EE" });
});
}
else {
grid.find('tr:has(td)').css({ background: "" });
grid.find('tr').show();
} // if no matching name is found, show all rows
});
});
<table id="tblsearchresult" class="tablesorter"">
<thead>
<tr>
<th>ApplicationName</th>
</tr>
</thead>
<tbody>
<% foreach (var request in Model.ApplicationRoles)
{ %>
<tr>
<td>
<span id="appName_<%: request.Id%>">
<%: request.Application.Name%></span>
</td>
</tr>
</tbody>
</table>
EDIT Table Data
applicationame role
application1 appadministrator
app developer
application2 tester
if i given 'app'as search text need to highlight secondrow only .highlightling firstrow also because 'app' is there in role of firstrow..exact match should be highlight on every rows.please tell me.
Your code is behaving correctly. Just that you need to clear all previously highlighted rows on "keyup" of input text first.
if (text.length > 1) {
grid.find('tr:has(td)').css({ background: "" });
grid.find('tr').show();
......rest of your code.......
You need to clear the highlight before you parse. Add this statement of yours:
grid.find('tr:has(td)').css({ background: "" });
before entering this loop:
// iterate through all grid rows
grid.find('tr').each(function (i) {
...
});
Check this fiddle: http://jsfiddle.net/F3jRj/1/
And this updated fiddle with 3 columns: http://jsfiddle.net/F3jRj/2/
I have a table, and I've dynamically added / removed rows from it.
The table contains fields that will be posted back as a collection to MVC.
In this scenario updating the table with jquery means I up with collections that contain incomplete collections for example ...
function add() {
$.ajax({
url: "GetRow",
type: 'post',
dataType: 'html',
timeout: 30000,
data: {
RowIndex: otherRows.length,
"Item.Property1": $("option:selected", dropDown).text(),
"Item.Property2": $("option:selected", dropDown).val()
},
success: function (result) {
var newRow = $(result);
tableBody.append(newRow);
}
});
}
This function makes an ajax call to go get an mvc action result that returns the result of a row at the given index and additionally sets some default values from a drop down on my page.
Now lets assume I have a similar function called "delete" in which using jquery i remove a row and at this point the table has 3 rows (ignoring the header row of course), and the row i'm removing is the middle row.
this means the fields in my rows end up looking something like this ...
//Row 1:
<input type="text" name="SomeCollection[0].Property1" />
//Row 2:
Nothing, deleted of course
//Row 3:
<input type="text" name="SomeCollection[2].Property1" />
So if i now do a postback, because of the inconsistent id range, the MVC model binder will only bind item 1 for me and item 2 (actually item 3 prior to the client side delete) is not mapped.
The idea is that i want my server logic to do a very simple "if the id for an item in my collection is not in the post data, delete it from the database", this way all the collection manipulation can be entirely client side in save on constant postbacks on this very heavy page.
So I started putting a function together in jquery to fix the problem ...
function updateNames(table) {
var rows = $("tr", table);
var index = 0;
rows.each(function () {
var inputFields = $("input");
inputFields.each(function (){
//replace name="bla[<any number>].Anything" with
// name="bla[" + index + "].Anything"
});
index++;
});
}
So my question is ... How do i say to jquery "replace [] in the name attribute with [index]"?
I know this wont solve the problem of nested collections / other such complex scenarios but for that once i have this function solved I can always extend it later and my requirements are not that involved yet.
EDIT:
Some additional detail on my current thought pattern ... good or bad?
if i grab the name attribute value and "walk the chars" until i find the first "[" and the next "]" then replace everything in between, it should solve my problem, but this type of thing on IE probably slow as hell.
Anyone got a neat "just call this clever function" type answer? (if there is one).
EDIT 2:
Wow I really gotta look harder ... what a dumbass i feel like right now for 2 reasons (not looking and regex is an obvious solution here)
JQuery: how to replace all between certain characters?
if i can figure out the right regex i've got my solution (maybe that's the question I should have asked as i'm constantly annoyed by the crazyness of regex).
But the power of a regex cannot be under estimated :)
This should work for you:
function updateNames(table) {
var rows = $("tr", table);
var index = 0;
rows.each(function () {
var inputFields = $(this).find("input");
inputFields.each(function (){
var currentName = $(this).attr("name");
$(this).attr("name", currentName.replace(/\[(.*?)\]/, '['+index+']'));
});
index++;
});
}
If there are multiple inputs inside each row and you just want to update one then consider using the 'starts with' jQuery selector: http://api.jquery.com/attribute-starts-with-selector/.
Though you've already got a resolution, I thought a working demo of how one might implement this functionality from the ground up might be useful.
Supposing you have a button with a class of delete on each row, you can achieve this with the following:
$('.delete').click(function(e) {
// Get table body and rows
var body = $(this).closest('tbody');
// Remove current row
$(this).closest('tr').remove();
// Get new set of rows from table body
var rows = body.find('tr')
// Update all indeces in rows
var re = new RegExp(/\[[0-9]+\]/);
var index = 0;
rows.each(function () {
$(this).find('input').each(function (e) {
var input = $(this).attr('name');
if (input) {
$(this).attr('name', input.replace(re, '['+index+']'));
}
});
index ++;
});
});
This should delete the row and update all indeces for every input in the remaining rows so that they increment properly again (and thus will be bound properly by the model binder). Additionally, it should be restricted to the current table. Here it is in action.