I want to get a value of a HTML text input in my ViewModel :
<div class="InputAddOn"
visible="#bind(vm.resetPasswordEmailDiv)">
<h:input class="InputAddOn-field"
name="resetPasswordEmail" id="resetPasswordEmail"
disabled="#bind(vm.resetPasswordEmailDisabled)"
value="#bind(vm.resetPasswordEmail)"/>
<button class="InputAddOn-item"
onClick="#command('sendResetPasswordEmail')">
Send
</button>
</div>
So when I click on the send button I get the value in my #Command method.
Here even the value="#bind(vm.resetPasswordEmail)" dosen't work
I have copied your code and the binding worked just fine. Below you find an example that uses both the binding and the command parameter:
<div xmlns:h="xhtml" viewModel="#id('vm')#init('somePath.MyViewModel')">
<h:input value="#bind(vm.bindingParam)" />
<button label="Send" onClick="#command('doCommand', commandParam=self.previousSibling.value)" />
</div>
public class MyViewModel
{
String bindingParam;
public String getBindingParam()
{
return bindingParam;
}
public void setBindingParam(String bindingParam)
{
this.bindingParam = bindingParam;
}
#Command
public void doCommand(#BindingParam("commandParam") String commandParam)
{
System.out.println(bindingParam + " " + commandParam);
}
}
When you click the button, it will output null null or myText myText according to the content of the input.
Retrieving the text from the input is done with the same expression language as the bindings. self is the replacement for this, and I used getPreviousSibling but you should be able to use IDs, too.
Related
For testing purpouses I want to have custom attribute on each, for example, button each page of my app without any manual adding.
Let's say I have page:
#page "/mypage"
<h1>This is page</h1>
<button class="btn btn-primary" #onclick="IncrementCount">Click me</button>
<button class="btn btn-primary" #onclick="IncrementCount">NO, click ME</button>
#code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
and, eventually, after some kind of additional rendering step, I want to see this html in my browser:
<h1>This is page</h1>
<button class="btn btn-primary" my-custom-attribute="button-click-me">Click me</button>
<button class="btn btn-primary" my-custom-attribute="button-no-click-me">NO, click ME</button>
So, the rule for makeing my-custom-attribute content comes from component content itself. It can be described as "component name + dash + text from component to lower case" => button(component name) + - + click-me(text from component to lower case with dashes instead spaces). Rule should be described somewhere in C# code and should "targeting" on list of components(buttons, divs, etc).
Is there any way to achieve that? Maybe there is a way to tweak Blazor rendering process somehow?
I can see no other solution than using a custom generic component. Someone may come up with a better answer. If so, I'll happily delete this one.
Here's a basic custom component to render html elements:
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
namespace BlazorApp1.Pages
{
public class MyComponentBase : ComponentBase
{
[Parameter] [EditorRequired] public string Tag { get; set; } = "div";
[Parameter] public string MyAttributeValue { get; set; } = string.Empty;
[Parameter] public string? Content { get; set; }
[Parameter] public RenderFragment? ChildContent { get; set; }
[Parameter(CaptureUnmatchedValues = true)] public IDictionary<string, object> UserAttributes { get; set; } = new Dictionary<string, object>();
private string myAttribute => string.IsNullOrWhiteSpace(Content) ? this.MyAttributeValue : this.Content.Replace(" ", "-");
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenElement(0, Tag);
builder.AddMultipleAttributes(1, UserAttributes);
if (!string.IsNullOrWhiteSpace(this.myAttribute))
builder.AddAttribute(2, "my-attribute", this.myAttribute);
if (ChildContent is not null)
builder.AddContent(3, ChildContent);
else if (!string.IsNullOrWhiteSpace(Content))
builder.AddMarkupContent(4, Content);
builder.CloseElement();
}
}
}
And a Demo:
#page "/"
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<MyComponentBase Tag="button" MyAttribute="Blazor-Component" class="btn btn-primary" #onclick=this.OnClick Content="My Blazor Button" />
<MyComponentBase Tag="div" MyAttributeValue="Survey-Prompt-Div" class="m-2">
<SurveyPrompt Title="How is Blazor working for you?" />
</MyComponentBase>
<div>
#this.message
</div>
#code {
private string message = string.Empty;
private void OnClick()
{
this.message = $"Hello Blazor at {DateTime.Now.ToLongTimeString()} ";
}
}
While this sort of works, it highlights some issues with what you are trying to do.
You can't just get the "content" between the opening and closing tags of an element. The content is a RenderFragment, not a string that you can parse and use for your attribute. Therefore anything like the second block in the example requires a manual setting of the attribute (you may as well define it directly!).
The complexity of the component depends on what type of content you want to apply your attribute to.
Anyway, the component demonstrates the basic principles of how to capture parameters and attributes and use them to construct elements.
I'm learning Spring boot. I have a list of products with unique ids, and I want to implement a "lookup by id" functionality, but I don't know how to do it, I searched but got totally different stuff.
I already have a #Getmapping method like this:
#Getmapping(/products/{id})
If I manually type in the id in the url I'll get what I what. But I want to have an input box in the HTML page like:
<form>
Look up by id: <input/>
</form>
and after I submit the form it'll redirect to that page. For example, if I enter input of 1, it'll go to localhost:8080/products/1
I've been searching but all I got was stuff about #Postmapping.
Add a #PostMapping to your controller:
#Controller
#RequestMapping("/products")
public class ProductController {
#GetMapping //Controller method for showing the empty form
public String index(Model model) {
model.addAttribute("formData", new SearchFormData()); // Create an empty form data object so Thymeleaf can bind to it
return "index";
}
#PostMapping
public String searchById(SearchFormData formData) {
return "redirect:/products/" + formData.getId(); //Use the value the user entered in the form to do the redirect
}
#GetMapping("/{id}")
public String showProduct(#PathVariable("id") long id) {
...
}
}
With SearchFormData representing the form fields (there is only 1 field in this case):
public class SearchFormData {
private long id;
// getters and setters
And update Thymeleaf template like this:
<form th:action="#{/products}" th:method="post" th:object="${formData}">
<input th:field="*{id}" type="number">
<button type="submit">Search</button>
</form>
Note that the value of th:object needs to match with the name used to add the SearchFormData instance to the model.
See Form handling with Thymeleaf for more info.
The following simple code will direct you to a URL that is generated from a concatenation of the base address of the <form>'s action attribute and the value of its first <input>:
document.querySelector("form").addEventListener("submit",function(ev){
ev.preventDefault();
this.action="/product/"+this.querySelector("input").value;
console.log(this.action);
// in real code: uncomment next line!
// this.submit()
})
<form>
Look up by id: <input type="text" value="123" />
</form>
In the real code you will delete the console.log() und uncomment the following line: this.submit().
Alternatively you could also do:
document.querySelector("form").addEventListener("submit",function(ev){
ev.preventDefault();
location = "/product/"+this.querySelector("input").value;
})
This will redirect you to the page without actually submitting the form.
I created form with one input text and one textarea. The input text works fine, but textarea isn't even displayed:
<div id="news" th:fragment="admin_panel">
<form method="POST" th:action="#{/addNews}" th:object="${news}" id="myform">
Tytuł:
<input type="text" th:field="*{title}"/>
<input type="submit" value="Wstaw"/>
</form>
<textarea name="news_content" rows="20" cols="80" th:field="${news.content}" form="myform">
...
</textarea>
</div>
When I delete the th:field the textarea is displayed and when I use th:value instead of th:field it's displayed too but doesn't save the written text to news.content (news.title is saved ok).
I don't have any idea... I read thymeleaf references but can't find answer, so please help good people!
You have to use the selected object expression *{content} AND place the textarea tag inside the form tag!
In the end its all about the generated name attribute in the resulting form. The name needs to correspond to the propertyAccessor from the selected root object th:object.
The form is processed by spring (without any thymeleaf interception).
The docs about spring integration are really good: http://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html
They state this:
Values for th:field attributes must be selection expressions (*{...}), which makes sense given the fact that they will be evaluated on the form-backing bean and not on the context variables (or model attributes in Spring MVC jargon).
EDIT:
Thanks to the link to the project, the fix was easy:
Thymeleaf 3.0.0.BETA03 had a bug in the textarea processor, moving to 3.0.0.RELEASE fixed this issue
Additionally I have moved the textarea inside the form element.
My textarea input was working fine when i was saving some data through it(in clean state when i was performing a save to DB) but in case of edit form(where my textarea input was supposed to show the prefilled description from the model property book.description) was blank, the reason for that was the th:value attribute, i changed it to the th:field attribute and it started working as expected.
<textarea class="form-control" id="description" rows="5"
name="description"
placeholder="Description" th:field="${book.description}"
required="required"></textarea>
You don't need the form text field. Linking the textarea with the form by the form id is sufficient.
<textarea rows="8" cols="120" name="lines" form="usrform" th:text="${message}"></textarea>
<form method="POST" enctype="multipart/form-data" th:action="#{/}" id="usrform">
<button type="submit" name="action" value="submitlines">Submit</button>
</form>
and the controller:
#RequestMapping(value="/", method=RequestMethod.POST, params="action=submitlines")
public String handleForm(
#RequestParam("lines") String input,
RedirectAttributes redirectAttributes) {
}
About exceptions :
SEVERE: Servlet.service() for servlet [dispatcher] in context with path [/eniupage] threw exception [Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateProcessingException: Error during execution of processor 'org.thymeleaf.spring4.processor.SpringTextareaFieldTagProcessor' (template: "templates/fragments" - line 144, col 60)] with root cause
java.lang.StringIndexOutOfBoundsException: String index out of range: 0
In my form, there are text input and textarea as You see. news.title is saved ok, but news.content don't. When I replace for test these parameters (in the text input I use news.content and in the textarea there is th:field = ${news.title}) it works well too. Maybe should I use another expression instead of th:field?
News.java
package eniupage.domain;
public class News
{
private String title;
private String content;
private Date date;
public String getTitle()
{
return title;
}
public void setTitle(String title)
{
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content)
{
this.content = content;
}
public Date getDate()
{
return date;
}
public void setDate(Date date)
{
this.date = date;
}
}
HomeController.java
package eniupage.web;
#Controller
#RequestMapping( "/" )
public class HomeController
{
#Autowired
AddNewsService addNewsService;
#RequestMapping( method = GET )
public String home( Model model )
{
model.addAttribute( "newses", addNewsService.getNewses() );
return "home";
}
#RequestMapping( value = "/addNews", method = POST )
public String addNews( News news )
{
addNewsService.addNews( news );
return "redirect:/";
}
}
AdminController.java
#Controller
#RequestMapping( "/admin" )
public class AdminController
{
#RequestMapping( method = GET )
public String admin( Model model )
{
model.addAttribute( new News() );
return "admin";
}
}
There isn't any resulting of HTML form, because it isn't even displayed in div. There are only text input and submit button.
Edited html:
<form action="#" method = "POST" th:action="#{/addNews}" th:object = "${news}" id = "myform">
Tytuł: <input type = "text" th:field = "*{title}" />
<input type = "submit" value = "Add" /></br>
<textarea rows = "20" cols = "80" th:field = "*{content}" form = "myform" >... </textarea>
</form>
I'm using thymeleaf 3.0. Maybe this is the reason?
In reference I read :
The th:field attribute behaves differently depending on whether it is attached to an , or tag (and also depending on the specific type of tag).
But I can't find what is this difference between using th:field in input and textarea.
I have an html helper method for a hidden field. It is bound to a byte[] and I have no problem as it displays the result correctly. But instead of the helper function if I use an html tag, the correct value is not displayed. Instead it displays its type.
following code and image will clarify what I am trying to say.
HTML code:
foreach (var path in Model.PathToImages)
{
<div class="form-group">
<div class="col-sm-6" style="vertical-align:central;">
<input type="button" value="Delete" class="btn btn-primary delete-property" name="#path.ImagePath" />
#Html.HiddenFor(m => path.ConcurrencyCheck)
<input id="#path.ImagePath" name="#path.ImagePath" type="hidden" value="#path.ConcurrencyCheck">
</div>
</div>
}
Property in my model:
public byte[] ConcurrencyCheck { get; set; }
Ignoring the names and id's of the control (this is just to reproduce the problem), following is the html generated:
Now as the image says when i use #Html.HiddenFor(m => path.ConcurrencyCheck) the value is correctly displayed but when I use <input id="#path.ImagePath" name="#path.ImagePath" type="hidden" value="#path.ConcurrencyCheck"> the value is the type System.Byte[].
So why I am not getting the value when I am using the html input tag or the problem is with the way model value should be displayed.
This because byte[] is a a complex array and needs to be converted to Base64String. The Html.HiddenFor() method takes this into account but #path.ConcurrencyCheck does not, and is using the .ToString() method of the properties value to generate the output.
You can view the source code here, but the relevant lines of code are
private static MvcHtmlString HiddenHelper(HtmlHelper htmlHelper, ModelMetadata metadata, object value, bool useViewData, string expression, IDictionary<string, object> htmlAttributes)
{
....
byte[] byteArrayValue = value as byte[];
if (byteArrayValue != null)
{
value = Convert.ToBase64String(byteArrayValue);
}
....
Is there a way I can use a numeric updown in ASP.NET without using JavaScript?
And if not, is there an alternative?
I was trying to do the same thing, and it turns out the asp textbox has an option for it. what worked for me was this:
<asp:TextBox TextMode="Number" runat="server" min="0" max="20" step="1"/>
this gave me a textbox which, when mouse hovers over it or is given focus, shows the up-down controls, and only allows numbers from min to max.
it works the same as
<input type="number" min="0" max="20" step="1" />
Please look into the Ajax Control Toolkit
http://www.asp.net/ajaxLibrary/AjaxControlToolkitSampleSite/NumericUpDown/NumericUpDown.aspx
<ajaxToolkit:NumericUpDownExtender ID="NUD1" runat="server"
TargetControlID="TextBox1"
Width="100"
RefValues="January;February;March;April"
TargetButtonDownID="Button1"
TargetButtonUpID="Button2"
ServiceDownPath="WebService1.asmx"
ServiceDownMethod="PrevValue"
ServiceUpPath="WebService1.asmx"
ServiceUpMethod="NextValue"
Tag="1" />
Also consider adding the reference with NuGet by using PM> Install-Package AjaxControlToolkit
I think the following html can answer your question:
<head runat="server">
<title>Numeric Up Down</title>
<script type="text/jscript">
function Load() {
/*numericUpDown1.value = or*/ document.getElementById("numericUpDown1").value = parseFloat(document.getElementById("<%=NumericUpDown1.ClientID%>").value);
}
function Change() {
document.getElementById("<%=NumericUpDown1.ClientID%>").value = document.getElementById("numericUpDown1").value; //or numericUpDown1.value
}
</script>
</head>
<body onload="Load()">
<form id="form1" runat="server">
<div>
<input type="number" id="numericUpDown1" onchange="Change()" />
<asp:HiddenField ID="NumericUpDown1" runat="server" />
</div>
</form>
</body>
And then on the asp server side code in C# or Visual Basic, you can treat that HiddenField as NumericUpDown, but note that his value is string, and not decimal, like System.Windows.Forms.NumericUpDown control, or float, or double, or int, so you will have to Parse it to one of these types for what you need the most.
If you want to style the numeric up down, then in javascript it is simple. Just set document.getElementById("numericUpDown1").style, but if you want to do it through the asp server side code in C# or Visual Basic, then the html must be different:
<head runat="server">
<title>Numeric Up Down</title>
<script type="text/jscript">
function Change() {
document.getElementById("<%=NumericUpDown1.ClientID%>").value = document.getElementById("numericUpDown1").value; //or numericUpDown1.value
}
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<% this.Response.Write(string.Format("<input type='number' id='numericUpDown1' value='{0}' onchange='Change()' style='{1}' />", this.NumericUpDown1.Value, this.numericUpDown1Style)); %>
<asp:HiddenField ID="NumericUpDown1" runat="server" />
</div>
</form>
</body>
numericUpDown1Style is a protected field whose type is string defined in the asp server side code in C# or Visual Basic.
If you want to give it a class and not to style it, then the html must be:
<head runat="server">
<title>Numeric Up Down</title>
<script type="text/jscript">
function Change() {
document.getElementById("<%=NumericUpDown1.ClientID%>").value = document.getElementById("numericUpDown1").value; //or numericUpDown1.value
}
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<% this.Response.Write(string.Format("<input type='number' id='numericUpDown1' value='{0}' onchange='Change()' class='{1}' />", this.NumericUpDown1.Value, this.numericUpDown1CssClass)); %>
<asp:HiddenField ID="NumericUpDown1" runat="server" />
</div>
</form>
</body>
numericUpDown1CssClass is a protected field whose type is string defined in the asp server side code in C# or Visual Basic.
If you want to style it and give it a class, then html is like html #2 or html #3, but the only change is in the following line:
<% this.Response.Write(string.Format("<input type='number' id='numericUpDown1' value='{0}' onchange='Change()' style='{1}' class='{2}' />", this.NumericUpDown1.Value, this.numericUpDown1Style, this.numericUpDown1CssClass)); %>
I think you know what is numericUpDown1Style and numericUpDown1CssClass from #2 and #3
RECOMMENDED TIP:
If your website contains a lot of numeric up down controls that are used in your asp server side code, and this is disadvantageous to create all of them this way, then you can add new "Web User Control" item to your website and name it "NumericUpDown". Then in its source html you can copy the html #1 or html #2 or html #3 or html #4 that I posted above (depends on if you want to style the numeric up down or not, or give it a class or not, or both or not) with some removes and changes, because it is not "WebForm", but "Web User Control"
and in the asp server side code make the following properties (They are in C#, but if you use Visual Basic, I don't think it will be a problem for you to translate the code):
public decimal Value
{
get
{
return decimal.Parse(this.HiddenField.Value);
}
set
{
this.HiddenField.Value = value.ToString();
}
}
//Like the System.Windows.Forms.NumericUpDown.Value, but if you dislike 'decimal', and you want other type, then you can change the return type and the type Parse.
//Note that the ID of the HiddenField is simply "HiddenField", and not "NumericUpDown1", so make sure in the Source html to rename "NumericUpDown1" to "HiddenField", but probably you would like a different ID, so if you gave it different ID, then ensure that in the code you refer this HiddenField with the ID you chose, and not "HiddenField" or "NumericUpDown1".
//The following properties are for only if you want to style your Numeric Up Down:
protected string style;
public string Style
{
get
{
return this.style;
}
set
{
this.style = value;
}
}
//If you chose, copied, pasted and changed html #2 or html #4, then don't forget to replace this.numericUpDown1Style to this.Style in the source html of the Web User Control.
//Optional
public Unit Width
{
get
{
int startIndex = this.style.IndexOf("width") + 6;
if (index != -1)
{
int endIndex = this.style.IndexOf(';', startIndex);
return Unit.Parse(this.style.Substring(startIndex, endIndex - startIndex));
}
return Unit.Empty;
}
set
{
if (this.style.Contains("width"))
{
this.style = this.style.Replace("width:" + this.Width.ToString() + ';', "width:" + value.ToString() + ';');
}
else
{
this.style += "width:" + value.ToString() + ';';
}
}
}
//The same you can do with the Height property.
//You can replace all the style code with the CssClass code instead, or just add it:
protected string cssClass;
public string CssClass
{
get
{
return this.cssClass;
}
set
{
this.cssClass = value;
}
}
//If you chose, copied, pasted and changed html #3 or html #4, then don't forget to replace this.numericUpDown1CssClass to this.CssClass in the source html of the Web User Control.
If you style the NumericUpDown, so know also that in every ASP.NET control, you can type after their ID .Style["style"] = "value".
If you want to be able to do this with NumericUpDown too, then change the type of the protected field style from string to MyStyle
There is the definition of MyStyle:
public class MyStyle
{
internal string style;
public string this[string style]
{
get
{
int startIndex = this.style.IndexOf(style) + style.Length + 1;
int endIndex = this.style.IndexOf(';', startIndex);
return this.style.Substring(startIndex, endIndex - startIndex)
}
set
{
this.style = this.style.Replace(style + ':' + this[style] + ';', style + ':' + value + ';')
}
}
}
Add this class to the Code of the Web User Control, and change the Style property:
public string Styles
{
get
{
return this.style.style;
}
set
{
this.style.style = value;
}
}
and then add the property:
public MyStyle Style
{
get
{
return this.style;
}
}
and change the line from:
protected string style;
to:
protected readonly MyStyle style = new MyStyle();
Don't forget in the source html of Web User Control, to replace this.Style to this.Styles.
NOTE: I didn't had patience to test the code by myself, so it might not work, so you'll have to fix it by yourself.
At least you understood my idea.
After the fixes, you can edit my answer and replace the wrong code with your fixed code.
I will appreciate it very much!
This Web User Control is the ASP NumericUpDown you wanted!
If you are stuck on .NET 4.0 and you want to use the native HTML5 input type "number" (rather than the NumericUpDown from Ajax Control Toolkit) you can use a combination of the ASP TextBox control with extra "type" tag:
<asp:TextBox runat="server" ID="txtMyTextBox" type="number" min="0" max="10" step="1"/>
If you want to prevent any text input, you could even add a FilteredTextBoxExtender from Ajax Control Toolkit:
<ajaxToolkit:FilteredTextBoxExtender runat="server" TargetControlID="txtMyTextBox" FilterType="Numbers" />