Why is my element not seen as equating to itself? - c#

I've got an html input text element created like this in C#:
boxIndex1 = new TextBox()
{
CssClass = "dplatypus-webform-field-input indexcell",
Width = TEXTBOX_WIDTH,
ID = "boxIndex1foapalrow2"
};
...and this jQuery to respond to the blur event of "boxIndex1foapalrow2" and its cousins ("boxIndex2foapalrow3", "boxIndex3foapalrow4", etc.):
$(document).on("blur", '.indexcell', function (e) {
var $currentindexcell = $(this);
if ($currentindexcell == $('[id$=boxIndex1foapalrow2]')) {
alert('index cell 1 exited'); // testing
}
});
I stepped through it, and the element assigned to $currentindexcell when I tab out of "boxIndex1foapalrow2" seems to be what I'm expecting:
<input name="ctl00$ctl24$g_5f3fedca_19f7_4bc3_b84e_efbef0c48a33$ctl00$boxIndex1foapalrow2" type="text" id="ctl00_ctl24_g_5f3fedca_19f7_4bc3_b84e_efbef0c48a33_ctl00_boxIndex1foapalrow2" class="dplatypus-webform-field-input indexcell" style="width:88px;">
...but the alert is not showing/the if condition equates to false. Why? It seems to me that the value of $currentindexcell in this instance does equal $('[id$=boxIndex1foapalrow2]'), but why doesn't it seem that way to the Javascript execution engine?

Two jQuery objects that contain the same set of elements are not equal. To test whether your jQuery object matches a selector, use .is():
if ($currentindexcell.is('[id$=boxIndex1foapalrow2]')) {
If you really wanted to check for equality, you should compare the actual elements (not the jQuery objects that hold them):
if (this == $('[id$=boxIndex1foapalrow2]')[0]) {

Related

Capture value of Textbox when OnChange is defined in the textbox

I have a dynamically generated grid with x number of textboxes that will be in it. As each textbox is generated, I give it an OnChange event that is a set function.
Html.TextBox(... new { #onchange = "ChangeItemQuantity(" + vm.ID + ", " + fk.id + ")" ...
So when it's rendered, it looks like this:
<input ... type="text" onchange="ChangeItemQuantity(1939, 3)" />
Then, in the script section:
function ChangeItemQuantity(ItemId, ForeignKeyId) {
...
}
In the ChangeItemQuantity() function, how would I also capture the new value of the textbox? I don't really want to use an id on the textbox, because it is part of a grid with many textboxes.
Should I pass it in as a parameter? If so, what would the syntax be of the code that renders the textbox?
Or, is there a way to capture is inside the javascript function?
Thanks!
If you want to store data in the html element why not use data- attributes?
Set them like so
#Html.TextBox(.... new { #class="someClass" data-vmId="vm.ID", data-fkId="fk.id" })
Then set a listener on that class
$('.someClass').change(function() {
var value = $(this).val();
var vmid = $(this).data('vmid');
var fkid = $(this).data('fkid');
}

Set limit for checked value in list box

I have multiple select listbox, i want to restrict the user when he selects more than 3 values. The values can be country or cities, they are coming from database. The code for same is written in lstArea_SelectedIndexChanged method.
<asp:ListBox ID="lstArea" runat="server" SelectionMode="Multiple" OnSelectedIndexChanged="lstArea_SelectedIndexChanged" AutoPostBack="true"></asp:ListBox>
I found jquery code to validate the same, but the change function is not working.
var limit = 3;
$("#<%=lstArea.ClientID%>").change(function () {
if ($(this).siblings(':checked').length >= limit) {
this.checked = false;
}
});
I think this address can help you
https://stackoverflow.com/a/27490795/5631805
Maybe you should instead of
if ($(this).siblings(':checked').length >= limit)
this
if($('#lstArea:checked').length >= limit)
Because you have set the property AutoPostBack to true, and you have also set the OnSelectedIndexChanged event handler, .NET will generate onchange attribute for your select which will trigger POST back to the server, whenever an item will be selected. To prevent that you need to change your .NET code to this:
<asp:ListBox ID="lstArea" runat="server" SelectionMode="Multiple"/>
and also update your javascript code to this:
// Handle for the list
var list = $("#<%=lstArea.ClientID%>");
// Our limit
var limit = 3;
// Get currently selected values
var selected = list.val();
// Handle change event
list.change(function () {
// If we reached the limit
if (list.find(":checked").length >= limit) {
// Set the list value back to the previous ones
list.val(selected);
return;
}
// Postback to the server
setTimeout("__doPostBack('<%=lstArea.ClientID%>','')", 0);
});
Each time the page will be rendered the selected variable will hold the last selected items. Than in the change handler you will get all the selected/checked options, and when the count of the newly selected options is more than or equal to your limit you will unselect the last selected value by setting back all previous values. And if the limit is ok, you will made your postback, by calling the .NET function __doPostBack.

Updating ApplicationData record from the HTML client

HTML Client Lightswitch:
In a ViewDetials screen i have a button where i would like to update my ApplicationData. However, the screens Dataworkspace does not contain the table i would like to update. My current button execute code looks like:
var filter = "SerialNumber eq " + screen.Hardware.SerialNumber; // Filter to find the Hardware Serial number in the active details window.
myapp.activeDataWorkspace.ApplicationData.Scans.filter(filter).execute().then(function (result) {
var scan = result.results[0];
scan.NoSticker = false; // Set the property to false
myapp.activeDataWorkspace.ApplicationData.saveChanges();
});
What I'm trying to do is update the NoSticker property on a record in the Scans table that matches the SerialNumber on my details screen. If i understand what I've been reading correctly, because the record is not in the screens Dataworkspace i have to query the Scans table in my ApplicationData. I can't recall ever having to do a query or a query with a filter in JS so I'm not sure If I'm even doing this correctly. When i click the button, nothing happens. Any guidance would be greatly appreciated.
At first glance, and assuming your SerialNumber is alphanumeric, the only issue with your approach appears to be the styling of your filter expression which should be as follows:
var filter = "SerialNumber eq " + msls._toODataString(screen.Hardware.SerialNumber, ":String");
This uses the standard LightSwitch library function to correctly markup your serial number value in the filter expression. In this case it basically wraps the value in single quotes as follows:
"SerialNumber eq 'ABC123'"
The _toODataString function also supports the following additional options for the second dataType parameters (covering the various LightSwitch data types):
":Binary", ":Binary?"
":Date", ":DateTime", ":Date?", ":DateTime?"
":DateTimeOffset", ":DateTimeOffset?"
":Decimal", ":Decimal?"
":Guid", ":Guid?"
":Int64", ":Int64?"
":Single", ":Single?"
":String", ":String?"
":TimeSpan", ":TimeSpan?"
":Byte", ":Byte?", ":Boolean", ":Boolean?", ":Double", ":Double?", ":Int16", ":Int16?", ":Int32", ":Int32?", ":SByte", ":SByte?"
Each of the groups above use the same markup approach e.g. ":Decimal" and ":Decimal?" are both suffixed by an M data type identifier. These markup options are a feature of the oData v3 protocol's filter operation used by LightSwitch.
As an alternative, you could always add a query against your Scans table which accepts the SerialNumber as a parameter and then call it as follows:
myapp.activeDataWorkspace.ApplicationData.ScanBySerialNumberQuery(screen.Hardware.SerialNumber).execute().then(function onComplete(result) {
if (result && result.results && result.results.length !== 0) {
var scan = result.results[0];
if (scan) {
scan.NoSticker = false; // Set the property to false
myapp.activeDataWorkspace.ApplicationData.saveChanges();
}
}
});
If you're still experiencing issues after trying the above approaches, the other aspect you could check is that your screen.Hardware.SerialNumber property is available at the point you're executing the search. If not, you may need to do the following:
screen.getHardware().then(function onComplete(hw) {
if (hw) {
myapp.activeDataWorkspace.ApplicationData.ScanBySerialNumber(hw.SerialNumber).execute().then(function onComplete(result) {
if (result && result.results && result.results.length !== 0) {
var scan = result.results[0];
if (scan) {
scan.NoSticker = false; // Set the property to false
myapp.activeDataWorkspace.ApplicationData.saveChanges();
}
}
});
}
});

Ajax jquery autocomplete in ASP.NET MVC 5 C#

I know there's a lot of these kind of post but I wasn't able to find any that suited me. I don't have knowledge of ajax and jquery (in fact I've just started with MVC and ASP.NET) so I need your help in this little thing.
There must be almost everywhere this kind of silly thing, I want to write a city name in a combobox, dropdownlist (or whatever) and using a method that I've already created which returns a list of locations (city, country and state names) that match the entered city. I want it to be dinamyc that's why I thought AJAX would solve this (but any other solution is accepted)
I found this jQuery autocomplete but I don't understand where to implement it. I want the combobox to match the bootstrap theme. Could someone tell me if this is an appropiate solution and if so where do I put the ajax content and else? (by where I mean, is it in the view, or controller or where?)
Or you could give mi a hint here is the method I've created for getting the elements from the database:
public List<LocationsViewModel> GetHeadquarter(string query)
{
var context = new HeadquarterContext();
//var city = context.Cities.Where(p => p.Name == query).Single();
//var province = context.Provinces.Where(p => p.ProvinceID == city.Province).ToList();
//foreach(Province prov in province) {
//}
var hq =
from c in context.Cities
join p in context.Provinces on c.Province equals p.ProvinceID
join co in context.Countries on p.Country equals co.CountryID
where c.Name == query
select new { country = co.Name, province = p.Name, city = c.Name };
List<LocationsViewModel> listLocation = new List<LocationsViewModel>();
foreach (var hqe in hq)
{
LocationsViewModel loc = new LocationsViewModel();
loc.City = hqe.city;
loc.Country = hqe.country;
loc.Province = hqe.province;
listLocation.Add(loc);
}
return listLocation;
}
Lets see if we can get it to work.
View:
This is added in your view, #Url.Action(Action, Controller) is the Action that is the source for the autocomplete function.
<input type="search" class="form-control ui-autocomplete"
data-autocomplete="#Url.Action("Autocomplete", "Home")" />
Controller (Home):
As you can see the Action Autocomplete was used to search for a product. I have an instance of my database entity called '_db' and have select a table called 'product_data' (can also use a Stored Procedure). I'm using LINQ to query the datasource and store it in the variable 'model', so it's querying where the 'term' StartsWith what is typed in the textbox, it takes the top 10 and for each one it add label and product. [{"label":value}]
public ActionResult Autocomplete(string term)
{
try
{
var model = _db.product_data // your data here
.Where(p => p.product.StartsWith(term))
.Take(10)
.Select(p => new
{
// jQuery UI needs the label property to function
label = p.product.Trim()
});
// Json returns [{"label":value}]
return Json(model, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
Settings.ReportException(ex);
return Json("{'ex':'Exception'}");
}
}
JavaScript:
This code is when you select a value from the list that is displayed from your search. The 'window.location.href' redirects to a different controller once a value from the autocomplete has been selected.
// submits form upon selecting a value
var submitAutocompleteForm = function (event, ui) {
var $input = $(this); // the HTML element (Textbox)
// selected value
$input.val(ui.item.label); // ui.item.label = the label value (product)
window.location.href = "/Product/Details?id=" + ui.item.label;
};
The next function sets up the autocomplete API. You declare your options, the above is optional and it comes under select, the source is required and it points to the data-attribute on the HTML element.
var createAutocomplete = function () {
var $input = $(this); // the HTML element (Textbox)
var options = {
// selecting the source by finding elements with the 'data-' attribute
source: $input.attr("data-autocomplete"), // Required
select: submitAutocompleteForm // Optional
};
// apply options
$input.autocomplete(options);
};
// targets input elements with the 'data-' attributes and each time the input changes
// it calls the 'createAutocomplete' function
$("input[data-autocomplete]").each(createAutocomplete);
You'll have to reference the jQueryUI file for autocomplete to work.

JQuery MVC collection name attributes

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.

Categories