Getting a file from html input field with vaadin - html

I have to use a HTML snippet to get an image from Android/iOS devices with
<input type="file" accept="image/*" capture="camera" />
which is displayed in a Label:
Label label = new Label("<input type=\"file\" accept=\"image/*\" capture=\"camera\" />");
label.setContentMode(Label.CONTENT_XHTML);
Because I'm using Vaadin on Liferay I'm not sure how to obtain the Image since there is no POST submit
How am I able to get this image?

You need to write custom Vaadin component which extends FormPanel. Thats not hard as it may sounds, just let Vaadin Widget creator generate classes for you and then replace auto-generated GWT component with the following code
public class CameraCaptureWidget extends FormPanel {
public CameraCaptureWidget() {
setEncoding(FormPanel.ENCODING_MULTIPART);
setMethod(FormPanel.METHOD_POST);
FileUpload f = new FileUpload();
this.add(f);
f.getElement().setAttribute("accept", "image/*");
f.getElement().setAttribute("capture", "camera");
f.addChangeHandler(new ChangeHandler(){
#Override
public void onChange(ChangeEvent event)
{
submit();
}
});
}
}
You can also add button instead of adding ChangeHandler on FileUpload itself.
After you are done with that you need to handle submit() event in the Connector. Example:
public CameraCaptureConnector() {
getWidget().addSubmitHandler(new SubmitHandler()
{
#Override
public void onSubmit(SubmitEvent event)
{
Window.alert("Hello world");
}
});
}
You may also need to remove some unnecessary auto-generated methods.
This solution generates this HTML code in DOM:
<input capture="camera" accept="image/*" class="gwt-FileUpload" type="file">
Should you encounter trouble, please make sure you have read https://vaadin.com/wiki/-/wiki/Main/Creating+a+simple+component

Related

Custom Blazor Server Component Not Invoking the OnClick Function

I have created a layout component in the same directory as with the MainLayout component. The CustomLayout #inherits LayoutComponentBase and has #Body which gets the content to be rendered inside the layout.
As obviously expected, the layout component has its own css files and UI display is fine. I am only having a problem with the #onclick function. It cannot be invoked. Is there a way to trigger the function on button click?
The reason I want to do this is that I am creating a navigation bar which will have to show/hide a dropdown. I hope I can get some ideas on this. Appreciated.
I am calling a server function this way: #onclick="SomeFuction" or #onclick="SomeFuction" or #onclick="#(() => SomeFuction())"
LayoutComponent:
#inherits LayoutComponentBase
#Body
Home page referencing a custom layout:
[AllowAnonymous]
[Layout(typeof(CustomLayoutComponent))]
[AllowAnonymous] is just for allowing anonymous access.
I am using Blazor Server and .NET 6
Additional Information:
I have put all the code in one page so that it becomes easier to read and understand.
Here is the LayoutComponent razor component:
#inherits LayoutComponentBase
<div class="navbar" id="custom-navbar">
<div class="wrapper">
<a>Home</a>
<a>Service</a>
<button class="btn btn-primary" #onclick="ToggleProductsUI">Products</button>
</div> </div>
<!--Component to show Products UI Component. Scoped css for styling-->
<ProductsComponent TValue="string" UIState="#ProductsUIState"></ProductsComponent>
#code{
// state of the Products submenu Component
string ProductsUIState { get; set; }
// a control function for toggling the Products UI
// this function somehow is not invoked.
void ToggleProductsUI()
{
if (string.IsNullOrWhiteSpace(ProductsUIState))
{
ProductsUIState = "show-ui";
return;
}
ProductsUIState = string.Empty;
}
}
Here is the ProductsUI component code (I have removed the unnecessary event to avoid confusion. The component can be fully controlled by the parent):
public partial class ProductsComponent<TValue> {
[Parameter]
public string? UIState { get; set; } }
Blazor pages that will use the custom layout component will point to it like this:
[AllowAnonymous]
[Layout(typeof(CustomLayoutComponent))]
public partial class Index
{
}
This is working fine. The issue is in the CustomLayoutComponent when trying to invoke the ToggleProductsUI() function
I accidentally inluded a <body> element in the layout and as such, the <script src="_framework/blazor.server.js"></script> was not being run hence the click event was not being invoked. I removed the <body> element. I was misled by the default body{} style in the underlining scopped css. Resolved.

Thymleaf how to take an input and then redirect to another page

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.

GWT uibinder autocorrect off

im using GWT uibinder method and my html contains a textbox like
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui"
xmlns:idmw="urn:import:com.testing.wid.impl">
<g:HTMLPanel>
<table align="center" valign="center" height="25%">
<tr><td><g:TextBox ui:field='searchS' /></td></tr>
</table>
</g:HTMLPanel>
How can i TURN OFF autocorrect and autocapitalize for this Textbox??
i tried
<g:TextBox ui:field='searchS' autocapitalize="off" autocorrect="off"/>
but i get
[ERROR] Class TextBox has no appropriate setAutocorrect()
method Element <g:TextBox autocapitalize='off' autocorrect='off' ui:field='searchS'>
Any other way i can do this???
Thanks
As already pointed by #Boris Brudnoy there is no built-in way to do it with TextBox. Takin futher his suggestion it will be nice to extract this into new custom component (to simplify reuse and support):
Add new package (for example com.app.shared.customcontrol)
Add new CustomTextBox:
public class CustomTextBox extends TextBox {
public void setAutocomplete(String value){
this.getElement().setAttribute("autocomplete", value);
}
public void setAutocapitalize(String value){
this.getElement().setAttribute("autocapitalize", value);
}
}
Declare new namespace using UI binder and use your component:
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui"
xmlns:c="urn:import:com.app.shared.customcontrol">
<g:HTMLPanel ...>
<c:CustomTextBox ui:field="..." autocomplete="off" autocapitalize="off" />
</g:HTMLPanel>
</ui:UiBinder>
As alternative way if you want apply these settings system wide you can do it via constructor:
public class CustomTextBox extends TextBox {
public CustomTextBox() {
this.getElement().setAttribute("autocomplete", "off");
this.getElement().setAttribute("autocapitalize", "off");
}
....
}
What you've tried will not work since GWT doesn't translate UiBinder attributes directly into HTML element properties. Instead, as your error message hints, it looks up widget setter methods of the form set[UiBinder_attribute]. Since there is neither setAutocorrect nor setAutocapitalize method in the TextBox class, the errors you're getting are expected.
What you could do is drop to the element level and write something like this, e.g. in your widget's constructor:
public MyWidget() {
initWidget(uiBinder.createAndBindUi(this));
searchS.getElement().setProperty("autocapitalize", "off");
searchS.getElement().setProperty("autocorrect", "off");
}

Custom attributes added by a custom renderer appears at the wrong HTML elements

I had to write a custom renderer for <h:commandLink/> tag to support data-* HTML5 attributes in order to use JSF 2.1 with jQuery mobile framework.
My JSF markup and the output generated by this markup are as follows:
<h:commandLink value="Prev" data-theme="e" data-role="button" data-inline="true" data-mini="true" data-icon="arrow-l"/>
<h:commandLink value="Page 1 of 3" data-theme="e" data-role="button" data-inline="true" data-mini="true"/>
<h:commandLink value="Next" data-theme="e" data-role="button" data-inline="true" data-mini="true" data-icon="arrow-r" data-iconpos="right"/>
It is obvious that my custom renderer properly renders the 2nd and the 3rd <h:commandLink/> tags, but not the 1st one. It seems the data-* attributes belong to the 1st tag is rendered with the immediate parent <div/> tag. This seems a strange (and buggy) behaviour of Mojarra (I use V 2.1.11). Please advice me how to overcome this?
My custom renderer code as follows:
public class MyCommandLinkRenderer extends CommandLinkRenderer {
#Override
public void encodeBegin(FacesContext context, UIComponent component) {
String[] attributes = {"data-theme", "data-role", "data-icon", "data-inline", "data-mini", "data-iconpos"};
ResponseWriter writer = context.getResponseWriter();
try {
for (String attribute : attributes) {
String value = (String) component.getAttributes().get(attribute);
if (value != null) {
writer.writeAttribute(attribute, value, attribute);
System.out.println(attribute + " " + value);
}
}
super.encodeBegin(context, component);
} catch (Exception e) {
}
}
}
You must call super.encodeBegin() before writing the attributes.
Otherwise you're writing the attributes to the previous HTML element, as confirmed by the generated HTML output. The super.encodeBegin() starts the new HTML element.
This is just a bug in your own code, not in Mojarra.
Update: also, overriding the encodeBegin() was not right in first place. You should rather be overriding the LinkRenderer#writeCommonLinkAttributes() method.
#Override
protected void writeCommonLinkAttributes(ResponseWriter writer, UIComponent component) throws IOException {
super.writeCommonLinkAttributes(writer, component);
// ...
}
It does by the way then not matter if you call super before or after your job.

Different behaviours for two different submit buttons?

I am trying to have two submit buttons in my form - one accepts meetings; the other declines them. They will both have different behaviours. How can I do is in my C# code?
The functionality I want is essentially
if(isPost) {
if(//accept button pressed(Request.Form[???]))
{
}
else
{
}
}
Here is my HTML :
<button name="accept" type="submit">Accept</button>
<div class="spacer"></div>
<button name="decline" type="submit">Decline</button>
<div class="spacer"></div>
Simple enough, but I cannot find a test for this on the Internet or in any documentation. Does anyone know what I would have for this ?
Give each button element the same name (in this example, 'SubmitButton') but a different value, then do a check for the different values in your server code, ie:
<button type="submit" name="SubmitButton" value="Accept">Accept</button>
<button type="submit" name="SubmitButton" value="Decline">Decline</button>
Then in your server code:
string buttonClicked = Request.Form["SubmitButton"]
if(buttonClicked == "Accept")
{
// Accept code
}
else if(buttonClicked == "Decline")
{
// Decline code
}
Be aware that this won't work on some earlier versions of IE though, and you may have to do a little javascript on the client prior to the post request being made.
As far as I remember, you have a controller action looking like this:
public ActionResult MyAction(string accept, string decline)
{
if (!string.IsNullOrEmpty(accept))
{
//do something
}
else
{
//do something else
}
}
Presuming you are using MVC3 you would do something like
#using (Html.BeginForm("Accept","Meeting"))
{
<input type="submit" value="Accept" />
}
#using (Html.BeginForm("Decline","Meeting"))
{
<input type="submit" value="Decline" />
}
You would then just have your accept and decline code in your Accept and Decline actions of your meeting controller respectively. No need for an if statement at all.
Example controller
public class MeetingController : Controller
{
[HttpPost]
public ActionResult Accept()
{
//Do accept stuff
return View();
}
[HttpPost]
public ActionResult Decline()
{
//Do decline stuff
return View();
}
}
Generally in the codebehind in c# you'd be looking for which button was clicked. You'd have an event handler for each button, and you code would react according to which button was clicked.
this.btnRejectAppointment.Click += new System.EventHandler(this.btnRejectAppointment_Click);
And then your method
private void btnRejectAppointment_Click(object sender, System.EventArgs e)
{
//code to reject appt.
}
And you'd have the SAME set for the other button.
ASP.NET Button control has OnClick event, so add different handler to every your button:
<asp:Button OnClick="MyHandler1" ... />
in code behind:
protected void MyHandler1(object sender, EventArgs args)
{ /* do stuff */ }
One of the possible solutions - use two buttons of type LinkButton but specify slightly different PostBackUrl:
<!-- Accept meeting button -->
<asp:LinkButton ID="acceptMeeting" runat="server"
PostBackUrl="MeetingsManager.aspx?Mode=Accept" />
<!-- Decline meeting button -->
<asp:LinkButton ID="declineMeeting" runat="server"
PostBackUrl="MeetingsManager.aspx?Mode=Decline" />
And in code behind:
public enum Mode
{
Unknown,
Accept,
Decline
}
protected void Page_Load(object sender, EventArgs args)
{
Mode currentMode = Mode.Unknown;
var rawMode = Request.QueryString["Mode"];
if (rawMode != null)
{
currentMode = (Mode)Enum.Parse(
typeof(Mode),
rawMode.ToString())
}
}