My setup is as follows...
I have a service which brings back data from a stored procedure to a UI in no particular order. The UI binds the data to an MVC partial using knockout which looks like this...
#{ ViewBag.Title = "People List"; }
<h2>People List</h2>
<table id="people">
<thead>
<tr>
<th>Name</th>
<th>Gender</th>
<th>Job Title</th>
<th>Job Description</th>
</tr>
</thead>
<tbody data-bind="foreach: people">
<tr>
<td style="width: 125px;"><span data-bind="text: Name"></span></td>
<td style="width: 75px;"><span data-bind="text: Gender"></span></td>
<td style="width: 105px;"><span data-bind="text: JobTitle"></span></td>
<td style="width: 300px;"><span data-bind="text: JobDescription"></span></td>
</tr>
</tbody>
</table>
<button style="margin-top: 15px;" data-bind="click: sortByName">Sort by Name</button>
The JS for the VM looks like this...
<script type="text/javascript">
function Person(data) {
this.Name = ko.observable(data.Name);
this.Gender = ko.observable(data.Gender);
this.JobTitle = ko.observable(data.JobTitle);
this.JobDescription = ko.observable(data.JobDescription);
}
function PersonListVM() {
var self = this;
self.people = ko.observableArray([]);
$.getJSON('#Url.Action("DisplayPeople", "People")',
function (results) {
for (var i = 0; i < results.length; i++) {
self.people.push(new Person(results[i]));
}
});
self.sortByName = function () {
self.people.sort(function (x, y) {
return x["Name"] == y["Name"] ? 0 : (x["Name"] < y["Name"] ? -1 : 1);
});
};
};
ko.applyBindings(new PersonListVM());
</script>
I can get and display data with no problem, even adding another dummy person in a VM function goes off without a hitch. But sorting by name seems to do nothing. There are no errors, but I'm obviously doing something wrong. This is supposed to be a pretty simple search, what am I missing?
PS: The reason I'm supplying the property name as a string is because I want to make a sortable, paginated grid out of this table, sorting by table headers. My goal is to be able to pass in any valid property on which the table can be sorted dynamically but this is just as experiment working up to it.
Even though you're accessing your properties using array syntax, they're still ko.observables. You need to "unwrap" the observable to get at the value.
Try this instead:
return x['Name']() == y['Name']() ? 0 : (x['Name']() < y['Name']() ? -1 : 1);
Just off the top of my head have you tried using dot syntax to access the person object properties instead of array syntax?
self.people.sort(function (x, y) {
return x.Name == y.Name ? 0 : (x.Name < y.Name ? -1 : 1);
});
Related
I have a "DOM" list in MVC 4 using jQuery to add elements dynamically but at the moment of removing all the elements
The table no longer allows me to add more elements, I have tried to use length and comparisons but it does not work.
Am probably missing something obvious but can't see what it is. Any ideas?
Example
Here is my table code
<div><img src="~/Images/splus.png" alt="Add Product" style="border:0"/></div>
<table id="dataTable" border="0" cellpadding="0" cellspacing="0">
<thead>
<tr>
<th></th>
<th></th>
<th>Product</th>
<th>Quantity</th>
<th></th>
</tr>
</thead>
<tbody>
#if (Model != null && Model.Count > 0)
{
int j = 0;
int index = 1;
foreach (var i in Model)
{
<tr>
<td>#index</td>
<td>#Html.HiddenFor(a => a[j].SEQ_NO, new { id = "nrow", Value = index })</td>
<td>#Html.DropDownList("PRODUCTS", "Select Product")</td>
<td>#Html.TextBoxFor(a => a[j].QUANTITY)</td>
<td>
<img src="~/Images/sminus.png" alt="Add Product" style="border:0"/>
</td>
</tr>
index += 1;
j++;
}
}
</tbody>
</table>
Here is my jQuery Code when i click on Add New
$("#addNew").click(function (e) {
e.preventDefault();
var last = $('#dataTable>tbody>tr:last');
if (last.length > 0) {
var name = last.children().find('input,select')[0].name;
var index = Number(name.replace(/[^0-9]/gi, '')) + 1;
var tr = ('<tr>' + last.html().replace(/[0-9]+__/gi, index + '__') + '</tr>').replace(/\[[0-9]+\]+[.]/gi, '[' + index + '].');
numberRows($('#dataTable tbody').append(tr));
}
});
UPDATE:
Add "REMOVE CODE"
$(document).on('click', '#remove', function (e) {
e.preventDefault();
$(this).parent().parent().remove();
numberRows($('#dataTable tbody'));
});
Am probably missing something obvious but can't see what it is. Any ideas?
var name = last.children().find('input,select')[0].name;
This line of code requires one last child, which means after you removed the last under , the name will be undefined. In fact, the Add New function won’t even pass the if (last.length > 0) since when the last got removed, the last.length becomes 0.
You can debug in devtools, and will see:
BEFORE the last got removed:
AFTER the last got removed:
Thus, to fix this issue, the simplest solution is to not remove the last but hide it. Please check below code:
$(document).on('click', '#remove', function (e) {
e.preventDefault();
if ($('#dataTable>tbody>tr').length > 1) {
$(this).parent().parent().remove();
//numberRows($('#dataTable tbody'));
}
else {
$(this).parent().parent().hide();
}
});
Ps. Not sure what happens in numberRows() function so I commented it.
I have here a view model which has two list objects. I will display them as two tables in my view. I've already created EditorFor for them. I'll place four buttons (move one right, move all right, move one left, move all left) for the exchange operations. I've googled everywhere and no goal on how to accomplish that, because I need to move the entire object, replace all "name" and "id" tags, reorder indexes and so on, because this way my lists will be posted correctly. I'm using Datatables.net and jQuery too.
Does anybody have a clue on how to do that?
Thank you in advance. The code goes below
EDIT
Since list elements on ASP.NET MVC are indexed like "ListName_0__Code"(for Id) and "ListName[0].Code" (for name) how to properly reorder these indexes?
EditorFor
#model ViewModels.UserPermissionDetails
<tr id="#Model.Id">
<td>
#Html.HiddenFor(m => m.Code)
#Html.HiddenFor(m => m.Login)
#Html.HiddenFor(m => m.Name)
#Html.HiddenFor(m => m.IdEmpUser)
#Html.DisplayFor(m => m.Code)
</td>
<td>#Html.DisplayFor(m => m.Login)</td>
<td>#Html.DisplayFor(m => m.Nome)</td>
</tr>
View
<table id="tbBlock">
<thead>
<tr>
<th>Code</th>
<th>Login</th>
<th>Name</th>
</tr>
</thead>
<tbody>
#Html.EditorFor(model => model.BlockList)
</tbody>
</table>
<table id="tbAllow">
<thead>
<tr>
<th>Code</th>
<th>Login</th>
<th>Name</th>
</tr>
</thead>
<tbody>
#Html.EditorFor(model => model.AllowList)
</tbody>
</table>
Exchange method (jQuery)
function addElement(OriginTableId, DestinyTableId, OriginListName, DestinyListName) {
var originTb = $(OriginTableId).DataTable(); //initialize DataTable.Net for origin table
var destinyTb = $(DestinyTableId).DataTable(); //initialize DataTable.Net for destiny table
var objectLine = $('#' + originTb.$('tr.selected').attr('id')); //get selected line that will be moved
//Name replacing code piece
var elementsFromLine = $(objectLine.children()[0]).children().toArray();
elementsFromLine.forEach(function (item, index, array) {
$(item).attr('id', $(item).attr('id').replace(OriginListName, DestinyListName)); //Replace 'OriginListName_0' with 'DestinyListName_0'
$(item).attr('name', $(item).attr('name').replace(OriginListName, DestinyListName)); //Replace 'OriginListName[0]' with 'DestinyListName[0]'
});
//Reordering code piece here, how to?
$(DestinyTableId + ' tbody').append(objectLine.clone(true, true));
objectLine.parent().remove();
}
It will be much easier for you to calculate and set the name values with the index only before you submit the form, not for every move action.
#Html.HiddenFor(m => m.Code, new { #class = "code" } })
// same for all the inputs you send to server
$("button[type='submit']").on("click", function (e) {
e.preventDefault();
updateIndexes();
$("form").submit();
});
function updateIndexes() {
$("#tbAllow").find("tbody").children("tr").each(function (i) {
var prefix = "BlockList[" + i + "].";
var $tr = $(this);
$tr.find("input.code").attr("name", prefix + "Code");
$tr.find("input.login").attr("name", prefix + "Login");
// same for all the inputs you send to server
});
};
In the past I've used jQuery/Ajax to capture the key press of an end user, and build up a WHERE clause to filter data on a Web form.
I'm now taking advantage of the strongly type facilitates in ASP.NET 4.5. I was wondering if anyone has a way of performing real-time filtering/searching on a Gridview.
Basically, I have three grids, all showing different information. I want the end-user to key in or select from a drop down list (generic searching) and all girds reflect those changes.
I'm not adverse to resorting back to the old method, just didn't; know if there was anything new I could use?
So you can look at this. I hope this help you.
Here is the HTML:
<input id="searchInput" value="Type To Filter">
<br/>
<table>
<thead>
<tr>
<th>Column1</th>
<th>Column2</th>
</tr>
</thead>
<tbody id="fbody">
<tr>
<td>cat</td>
<td>one</td>
</tr>
<tr>
<td>dog</td>
<td>two</td>
</tr>
<tr>
<td>cat</td>
<td>three</td>
</tr>
<tr>
<td>moose</td>
<td>four</td>
</tr>
<tr>
<td>mouse</td>
<td>five</td>
</tr>
<tr>
<td>dog</td>
<td>six</td>
</tr>
</tbody>
</table>
And here is the JavaScript code:
$("#searchInput").keyup(function () {
// Split the current value of searchInput
var data = this.value.split(" ");
// Create a jQuery object of the rows
var jo = $("#fbody").find("tr");
if (this.value == "") {
jo.show();
return;
}
// Hide all the rows
jo.hide();
// Recusively filter the jquery object to get results.
jo.filter(function (i, v) {
var $t = $(this);
for (var d = 0; d < data.length; ++d) {
if ($t.is(":contains('" + data[d] + "')")) {
return true;
}
}
return false;
})
// Show the rows that match.
.show();
}).focus(function () {
this.value = "";
$(this).css({
"color": "black"
});
$(this).unbind('focus');
}).css({
"color": "#C0C0C0"
});
Enter link description here
I have a dynamically created table with id called "editTable" that looks as follows:
<tbody>
#{var i = 0;}
#foreach (var item in Model)
{
<tr>
<td width="25%">
#Html.DisplayFor(modelItem => item.Product.Name)
</td>
<td width="25%">
#Html.DisplayFor(modelItem => item.Quantity)
</td>
<td width="25%">
<div class="editor-field">
#Html.EditorFor(modelItem => item.UnitPrice)
#Html.ValidationMessageFor(model => item.UnitPrice)
</div>
</td>
<td width="25%" id="total"></td>
</td>
</tr>
}
</tbody>
The 3th td-element consists of a C# textbox that is turned into a element in html.
Now I want to multiply the quantity by the unit price to display this value in the 4th td element next to it. This value should update every time the value in the textbox is adjusted. I am a newbie at JQuery / JavaScript and came up with the following code:
// Calculating quantity*unitprice
$('#editTable tr td:nth-child(3) input').each( function (event) {
var $quant = $('#editTable tr td:nth-child(2)', this).val();
var $unitPrice = $('#editTable tr td:nth-child(3) input', this).val();
$('#editTable tr td:nth-child(4)').text($quant * $unitPrice);
});
This doesn't work and only displays NaN in the 4th element. Can anyone help me updating this code to a working version? Any help would be very much appreciated.
I geussed you accidentally switched units and price because it has more logic to change the number of units then the price. I took your html and javascript and tried to change as little as possible to make it work (I'm not saying the solution is perfect, I just don't want to give you a totaly different example of how to do it).
The html (The C# is irrelevant for this problem):
<table id="editTable">
<tbody>
<tr>
<td width="25%">
Product name
</td>
<td width="25%">
5
</td>
<td width="25%">
<div class="editor-field">
<input id="UnitPrice" name="UnitPrice" type="number" value="2" style="width:40px" />
</div>
</td>
<td width="25%" id="total"></td>
</tr>
</tbody>
</table>
The javascript/jquery (which should run on load):
$('#editTable tr td:nth-child(3) input').each(updateTotal);
$('#editTable tr td:nth-child(3) input').change(updateTotal);
var element;
function updateTotal(element)
{
var quantity = $(this).closest('tr').find('td:nth-child(2)').text();
var price = $(this).closest('tr').find('td:nth-child(3) input').val();
$(this).closest('tr').find('td:nth-child(4)').text(quantity * price);
}
The problem you had were with jquery. I've created a function that recieves an element (in our case it's your UnitPrice input), then it grabs the closest ancestor of type tr (the row it's in) and from there it does what you've tried to do.
You've used jquery selector to get all 2nd cells in all table rows, the closest('tr').find limits it to the current row.
You've tried to use .val() on a td element, you should use either .text() or .html(). Instead, You can also add a data-val="<%=value%>" on the td and then use .data('val').
It will be better to take the units directly from $(element).val() and no going to the tr and then back into the td and the input.
To see it working: http://jsfiddle.net/Ynsgf/1/
I hope I didn't caused you any confusion with my explanation and the options I gave you.
Here is another way to write the jquery part.
$('#editTable tr').each(function (i, row) {
var $quant = $(row).find('.editor-field input').val();
var $unitPrice = $(row).find('.editor-field input').val();
$(row).find('td:nth-child(4)').text($quant * $unitPrice);
});
It's my first asp.net mvc application at all. I'm using mvc 3 and razor for my views. The specific view with which I have problem is a strongly-typed view (at least I think it is) but it accepts type of this kind :
#model List<List<DataAccess.MCS_DocumentFields>[]>
I made several partial view because the main view was very customized and I need a lot of different logic for different parts of my data. My partial view are also strongly typed (again I think this is correct to say) of type :
#model List<DataAccess.MCS_DocumentFields>[]
Everything that I need to build my view is in :
#using (Html.BeginForm("ActionMethodName", "Forms", FormMethod.Post))
{
<div id="drawForm">
<table border="1">
Inside Hhtml.BeginForm I build table with all the data that comes from the controller. Now on submit :
<button type="submit">Submit</button>
I'm looking for the best way to get my data, and update in the database what is changed. In my controller I have this method :
[HttpPost]
public ActionResult ActionMethodName(FormCollection collection)
{
var test = collection;
List<MCS_Documents> model = DocumentsService.All().ToList();
return View("Index", model);
}
This way I can find my data, but it's so complicated, I'm not even sure how I'll get to it. I tried something else, using the same method but accepting as argument the type which the view accepts :
[HttpPost]
public ActionResult ActionMethodName(List<List<DataAccess.MCS_DocumentFields>[]> collection)
{
But when I do that collections has null value. And from the little I know when I use strongly-typed view I can get some additional advantages like form.validation and data binding ready for update and so on...
So what is the best way, in my specific scenario, to deal with the data submitted from the form?
P.S
This is how I render my main view :
<table border="1">
<colgroup>
<col span="1" style="width: 10%;" />
<col span="1" style="width: 40%;" />
<col span="1" style="width: 25%;" />
<col span="1" style="width: 25%;" />
</colgroup>
<tbody>
#for (int i = 0; i < Model.Count(); i++)
{
if (Model[i][0][0].ContentTypeId == 1)
{
#Html.Partial("_PartialHeader", Model[i])
}
else if (Model[i][0][0].ContentTypeId == 2)
{
#Html.Partial("_PartialDrawing", Model[i])
}
else if (Model[i][0][0].ContentTypeId == 3)
{
#Html.Partial("_PartialBody", Model[i])
}
else if (Model[i][0][0].ContentTypeId == 4)
{
#Html.Partial("_PartialFooter", Model[i])
}
}
</tbody>
</table>
<button type="submit">Save</button>
and this is one of my partials, just to showcase how I use them :
#model List<DataAccess.MCS_DocumentFields>[]
<tr>
#if (!string.IsNullOrEmpty(Model[0][0].FieldValue))
{
<td colspan="2">
#Html.DisplayFor(x => x[0][0].FieldValue)
</td>
}
else
{
<td colspan="2">
Sign in here
</td>
}
#if (!string.IsNullOrEmpty(Model[1][0].FieldValue))
{
<td colspan="2">
#Html.DisplayFor(x => x[1][0].FieldValue)
</td>
}
else
{
<td colspan="2">
Sign in here
</td>
}
</tr>
That last action method is correct (which takes your strong type), but your views will need to be structured differently. You need to make sure that every collection (even the sub collections) are correctly indexed, otherwise model binding will fail.
Your views should be something like the following (note the use of for loops):
Main view:
#model List<List<DataAccess.MCS_DocumentFields>[]>
#for (int i = 0; i < Model.Count; i++)
{
#Html.RenderPartial("PartialName", Model[i])
}
Then in your Partial:
#model List<DataAccess.MCS_DocumentFields>[]
#for (int i = 0; i < Model.Count; i++)
{
//Not sure if you need anything at this level
for (int j = 0; j < Model[i].Count(); j++)
{
//Add your EditorFor's, HiddenFor's etc for the child type
}
}