I have a setup where a user can enter a zip code into an ASP.NET TextBox control, and I have an AutoCompleteExtender from the Ajax Control Toolkit attached to that textbox. It gets its data from a static page method on the ASPX page.
When the user starts typing a Swiss zip code, e.g. 3 and then waits a brief moment, a list of matching zip code shows up - something like:
3000 - Bern
3001 - Bern
and so on. Works like a charm.
The normal way to pick one of the options shown is to move your mouse pointer to the list and select the one you want, click on it or press Enter, and get the zip code into that textbox (and the city name into a second textbox next to it).
Now, I got some additional requirements from my project manager:
we would like to be able to just press Enter without going into the list of choices to select one - he'd like to just get the first (or often times: only) entry shown put into those two textboxes...
we would like to be able to enter a valid 4-digit zip code and then just press Tab and move out of the textbox for the zipcode, and have the first (possibly only) entry with that zipcode be chosen and "selected" (and stuffed into the two textboxes).
Seems like a tall order to me (I'm not a great Javascript guru at all.....) - any ideas?
This is my ASP.NET page (in a standard ASP.NET 4.0 webforms sample app - with a master page; the script is simplified; in reality, I'm splitting up the text 3001 - Bern at the dash and stick the first part into the zip code and the second part into the city textbox):
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
<script type="text/javascript">
function IAmSelected(source, eventArgs) {
$get('tbxCity').value = eventArgs.get_value();
}
</script>
<asp:ScriptManager runat="server" EnablePageMethods="True" />
<asp:Literal runat="server" ID="litPrompt" Text="Please enter zip code: " />
<asp:TextBox runat="server" ID="tbxZipcode" MaxLength="10" />
<act:AutoCompleteExtender runat="server" ID="acZipCode" TargetControlID="tbxZipcode" MinimumPrefixLength="1"
CompletionInterval="25" ServiceMethod="GetMatchingZipCodes" CompletionSetCount="15"
OnClientItemSelected="IAmSelected" />
<asp:TextBox runat="server" ID="tbxCity" MaxLength="50" />
</asp:Content>
and my code-behind (this, too, is simplified - of course, in reality, I get this data from a Entity Framework data model):
[WebMethod]
[ScriptMethod]
public static string[] GetMatchingZipCodes(string prefixText, int count)
{
return new string[] { "3000 - Bern", "3001 - Bern", "4000 - Basel", "6000 - Lucerne", "6001 - Lucerne" };
}
Check the FirstRowSelected property of AutoCompleteExtender. From your requirements seems like it's exactly what you need.
Related
I am building a blazor app (WASM) where I use some <input>'s with those inputs. to both I binded a variable, for easy overview I put that in the #code part of the (index)page.
on the second <input> I also put an onclick event, and in the onclick event I fill a third variable (ThirdText) with the first (FirstText).
When I start the application, and I click on the first <input>, and fill in some characters and then click on the second <input> the value of the third variable is still empty. But when I go back to the first, change a bit, and click on the second afterwards, it directly fills the NEW value.
The last bahaviour is what I also hoped for in the first situation.
Here I have my code in index.cs:
#page "/"
<h3>#FirstText</h3> <br/>
<h3>#SecondText</h3> <br/>
<h3>#ThirdText</h3> <br/>
<input #bind="FirstText" style="width:50%" /> <br/>
<input #bind="SecondText" #onclick="OnclickHandler" style="width:50%" /> <br/>
#code{
private string FirstText { get; set; } = string.Empty;
private string SecondText { get; set; } = string.Empty;
private string ThirdText { get; set; } = string.Empty;
void OnclickHandler(MouseEventArgs mouseEventArgs)
{
ThirdText = FirstText;
}
}
This code is an easy example, the real application has a lot more logic, but this is the base of my problem.
When I make the first box empty, and click on the second box, again it does not work as expected.
I already tried to first fill the 3th variable in oninitialised() , but then the first variable is also empty so, that does not work.
I know in javascript there is a simular problem, that is because the onclick is firstly called, and the onexit or other event you want to use on the exit of the first box is then forgotten. But in that case, it goes wrong al the time. I hope that blazor has a solution for this situation. Because it goes well in Blazor if the first input was already filled.
The reason your code is not working is because of the timing of the click event:
If the button is pressed on one element and the pointer is moved
outside the element before the button is released, the event is fired
on the most specific ancestor element that contained both elements.
click fires after both the mousedown and mouseup events have fired, in
that order.
source
Long story short, by the time mouseup (or even the mousedown for that matter) occurs, the layout has changed and input element has shifted outside of the range of the mouse pointer. For example, change your code to the following and you will see it works now because the elements do not shift:
<h3>AAA #FirstText</h3> <br/>
<h3>AAA #SecondText</h3> <br/>
<h3>AAA #ThirdText</h3> <br/>
The solution to your problem would depend on what you are trying to achieve, but the basic idea here is to not rely on click events when layout changes occur.
To really prove what is happening, in Chrome Dev Tools go to Sources > Event Listener Breakpoints > Mouse > click and make sure the checkbox is checked. After that, type something in the first input and then click the second input element. The click event will fire, however notice the event target in the below screenshot! It is not the input element but the parent element <article>. This proves that the click event was never dispatched to the input element because it is not underneath the mouse pointer when the mouseup event occurs.
You describe the current behaviour but at the same time you are a bit vague about what you expect. As far as I can tell the code does what you ask for.
And what do you expect (want) to happen when a User uses the Tab key to go to the next input? Is it about mouseclicking or about completing an input?
I think you should consider one of these two changes:
<input #bind="FirstText" style="width:50%" /> <br/>
<input #bind="SecondText" #onfocus="OnUpdateHandler" style="width:50%" />
or, and this seems more logical:
<input #bind="FirstText" #onblur="OnUpdateHandler" style="width:50%" />
<input #bind="SecondText" style="width:50%" />
with
void OnUpdateHandler()
{
ThirdText = FirstText;
}
If that doesn't work then be more specific: when/why do you want ThirdText to mirror FirstText?
#Third Answer:
Here's a test page to capture the problem in it's simplest form.
I've removed most of the original code and made it a simple data entry and save form - without using EditForm.
In the current configuration SaveData isn't called when you enter your name and click on the save button.
Comment out the line <h3>#this.YourName</h3> before the form and it now works. Note there's a second <h3>#this.YourName</h3> below the form that has no impact.
Now change the line to:
<h3>Your name is: #this.YourName</h3>
and it works.
The answer lies in the answer provided by #JesseGood To quote his comment:
When you remove the single line of code, the elements do not shift down anymore so the click event occurs on the button. However when you add the line of code, the element becomes populated causing all the other elements to shift down and by the time you lift your finger off of the mouse the element is no longer under your mouse pointer causing the click event to occur on the parent element.
#page "/"
#using System.Text
<PageTitle>Index</PageTitle>
#*Comment out this line and the event triggers on first click*#
<h3>#this.YourName</h3>
<h3>Your Name is #DisplayName</h3>
<div class="m-2">
Your Name: <input value="#this.YourName" #onchange=this.NameChange style="width:50%" />
</div>
<div class="m-2">
<button class="btn btn-primary" #onclick="SaveData">Save</button> <br />
</div>
<h3>#YourName</h3>
<div>
<pre>
#log.ToString()
</pre>
</div>
#code {
private string YourName { get; set; } = string.Empty;
private StringBuilder log = new StringBuilder();
private string DisplayName = string.Empty;
void NameChange(ChangeEventArgs e)
{
this.YourName = e.Value?.ToString() ?? String.Empty;
log.AppendLine("FirstChange Called");
}
void SaveData(MouseEventArgs mouseEventArgs)
{
this.DisplayName = this.YourName;
log.AppendLine("Value Saved");
}
}
Follow on answer:
When you "bind" in Razor, the Razor compiler actually builds out the following code. In the example I've just wired it up manually so I can output some debug code when the onchange event is raised.
__builder.OpenElement(15, "input");
__builder.AddAttribute(16, "style", "width:50%");
__builder.AddAttribute(17, "value", Microsoft.AspNetCore.Components.BindConverter.FormatValue(this.FirstText));
__builder.AddAttribute(18, "onchange", Microsoft.AspNetCore.Components.EventCallback.Factory.CreateBinder(this, __value => this.FirstText = __value, this.FirstText));
__builder.SetUpdatesAttributeName("value");
__builder.CloseElement();
First and second Answer replaced
I have an asp.net radgrid that contains a column thusly:
<telerik:GridTemplateColumn UniqueName="RenewsContainer" HeaderText="Renews" HeaderStyle-Width="60px" ItemStyle-Width="60px" HeaderStyle-HorizontalAlign="Center" ItemStyle-HorizontalAlign="Center">
<ItemTemplate>
<asp:CheckBox ID="chkRenewsView" runat="server" Checked='<%#Eval("Renews")%>' onclick="checkboxChange(this);" />
</ItemTemplate>
<EditItemTemplate>
<asp:CheckBox ID="chkRenews" runat="server" Checked='<%# Bind("Renews") %>' />
</EditItemTemplate>
</telerik:GridTemplateColumn>
Notice the onclick="checkboxChange(this). This works fine, but inside that checkBoxChange() function I need to access the checkbox state (checked/unchecked) of a sibling GridCheckBoxColumn checked state. I don't know how to get the row index (or rowid) of the checked row, and even when the row is obtained, I don't know how to get the checked state of that GridCheckBoxColumn (uniquename: "Locked").
Seems like there could be 2 approaches to this, at least:
Inside the checkboxChange() function, pass as an arg the current row index or telerik rowid. I don't know how to get this.
Inside the js function, somehow determine the row index or rowid of the row containing the checkbox that was clicked.
And then once the rowid/rowidx is obtained, how to get that sibling checked state?
Any ideas?
Couldn't you give your checkbox a unique CssClass and then use jQuery to select it right out of the DOM? The RadGrid is a programmatic wrapper, but if you inspect the rendered HTML it's just a DIV/Table like anything else.
<ItemTemplate>
<asp:CheckBox ID="chkRenewsView" runat="server" CssClass="MyCheckBox" Checked='<%#Eval("Renews")%>' onclick="checkboxChange(this);" />
</ItemTemplate>
//jQuery
var isChecked = $(".MyCheckBox").is(':checked');
In my page there is one textbox by default and one add button beside it. I need to add the another textbox when user click Add button. And there should be two buttons Add and Remove beside newly added text box. And same process goes on i.e., user can add Textbox using Add button and remove it using remove button.
I am new to mvc 3 so i am confused how to proceed. Is there any way like placeholder in asp.net so that we can add control at runtime.
Any suggestion and idea will be helpful to me
MVC is a very "hands-off" framework compared to Web Forms, so you're free to add the new textboxes how you like. Note that "controls" don't exist in MVC.
Here's how I'd do it:
Model:
class MyModel {
public Boolean AddNewTextBox { get; set; }
public List<String> MultipleTextBoxes { get; set; } // this stores the values of the textboxes.
}
View (I prefer the Web Forms view engine, I'm not a fan of Razor):
<% for(int i=0;i<Model.MultipleTextBoxes.Count;i++) { %>
<%= Html.TextBoxFor( m => m.MultipleTextBoxes[i] ) /* this might look like magic to you... */ %>
<% } %>
<button type="submit" name="AddNewTextbox" value="true">Add New Textbox</button>
<button type="submit">Submit form</button>
Controller:
[HttpPost]
public ActionResult MyAction(MyModel model) {
if( model.AddNewTextBox ) model.MultipleTextBoxes.Add("Yet another");
else if( ModelState.IsValid ) {
// your regular processing
}
}
You can also add more textboxes with Javascript and it work perfectly fine. All that matters is the HTML input elements. There's no cryptic viewstate. MVC is stateless.
Note that because I used <button type="submit"> my example will not work reliably in Internet Explorer 6-8 (sucks, I know), but you can replace them with <input type="submit"> with no ill-effects.
This requires some Javascript/JQuery... The following is a sketch only, but will hopefully be useful as a general approach.
The remove button
You want to render a button that can target its own container for removal. To do that, use some markup like this:
<div class="item-container">
<input type="button" onclick="removeItem(this)" />
</div>
And the Javascript for removeItem:
<script>
function removeItem(element) {
// get the parent element with class "item-container" and remove it from the DOM
$(element).find(".item-container").remove();
}
</script>
The add button
You could either use a partial view with Ajax, or use straight Javascript; which one is best likely depends on whether you need a round-trip to the server to create a new item. Let's say you need to go the the server to generate a new ID or something.
First, create a partial view and corresponding controller action; this should contain the remove button as above, as well as the text box and add button.
Now, create an Ajax form on your main page that gets invoked when you click Add:
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
#using (Ajax.BeginForm("New", new AjaxOptions() { UpdateTargetId="ajaxTarget", HttpMethod = "GET" })) {
<input type='submit' value='Add New' />
}
<div id="ajaxTarget"></div>
This code fetches your partial view (from the action New in the current controller) and adds the result to the ajaxTarget element.
Note The Ajax form requires Unobtrusive Ajax, which you can install via Nuget: Install-Package JQuery.Ajax.Unobtrusive.
I am trying to use a text box input as a SqlParameter but it only goes into DataSelecting when the page first loads. Not after the from is submitted.
Here is the code on the aspx page.
protected void DataSelecting(object sender, SqlDataSourceSelectingEventArgs e)
{
e.Command.Parameters["#zip"].Value = ZipBox.Text;
}
"
SelectCommand="SELECT Name FROM Names WHERE (ZipCode = #zip)"
OnSelecting="DataSelecting">
SelectParameters>
parameter Name="zip" DefaultValue="1" />
SelectParameters>
SqlDataSource>
FORM
id="ZipSearch" runat="server" action="Default.aspx" method="post">
TextBox ID="ZipBox" runat="server" />
Button id="btnSubmit" Text="Submit" runat="server" />
FORM
Thanks for your help,
Matt
You need to place that code in the button click event. Selecting event is for different purpose.
Old reply (Before OP's comment) :
What do you have in button click event?
The Selecting event would fire before your select command is executed. Hence if your button click event is firing any command, the Selecting event won't be fired.
How do you dynamically pass parameter/control to contextKey?
<asp:TextBox ID="tbA" runat="server" autocomplete="off"></asp:TextBox>
<asp:TextBox ID="tbB" runat="server">hello</asp:TextBox>
<asp:TextBox ID="tbC" runat="server">world</asp:TextBox>
<cc1:AutoCompleteExtender ID="aceListA" ServiceMethod="myListServiceA" ServicePath="WebService.asmx" TargetControlID="tbA" runat="server" EnableCaching="true" UseContextKey="true"> </cc1:AutoCompleteExtender>
[WebMethod]
public string[] myListServiceA(string prefixText, int count, string contextKey)
Because I want to check tbB, tbC at Web Service level.
I've read this article like 5 times, but still doesn't make a sense out of it, per half missing complete codes.
You can set the context key in two ways:
Client-Side, through Javascript:
<script type="text/javascript">
function SetContextKey()
{
$find('auJobs').set_contextKey('moo');
return;
}
</script> `
or
on the server side
auJobs.ContextKey = "Whatever You need to put in here"
Terry