Vaadin's comboBoxes were designed to show captions and items are line by line (you can see them in here). If I want to see them in a same line , what is the solution ?
Now I am trying to get as below ....
HorizontalLayout hlMain = new HorizontalLayout();
hlMain.addComponent(new Label("Gender:"));
final ComboBox gender = new ComboBox("" , genderList);
hlMain.addComponent(gender);
But I know above code is too ugly , So I am trying to use with CSS . I would like to know has there easy way to get it with Vaadin ? Any suggestions will be great help for me.
If you are willing to dedicate a layout to this, use a FormLayout:
// run with: spring run vaadin.groovy
#Grapes([
#Grab('org.vaadin.spring:spring-boot-vaadin:0.0.5.RELEASE'),
#Grab('com.vaadin:vaadin-server:7.4.4'),
#Grab('com.vaadin:vaadin-client-compiled:7.4.4'),
#Grab('com.vaadin:vaadin-themes:7.4.4'),
])
import org.vaadin.spring.annotation.VaadinUI
import com.vaadin.server.VaadinRequest
import com.vaadin.ui.*
#VaadinUI
class MyUI extends UI {
protected void init(VaadinRequest request) {
setContent(new FormLayout( // XXX
new ComboBox("Gender:").with{ addItem("M"); addItem("F"); it }
))
}
}
Your current code would work, if you would not put the empty caption on the ComboBox (it makes Vaadin think, that there is something to show and render an empty line, break, and then the combobox). Yet FormLayout is the superior solution.
// run with: spring run vaadin.groovy
#Grapes([
#Grab('org.vaadin.spring:spring-boot-vaadin:0.0.5.RELEASE'),
#Grab('com.vaadin:vaadin-server:7.4.4'),
#Grab('com.vaadin:vaadin-client-compiled:7.4.4'),
#Grab('com.vaadin:vaadin-themes:7.4.4'),
])
import org.vaadin.spring.annotation.VaadinUI
import com.vaadin.server.VaadinRequest
import com.vaadin.ui.*
#VaadinUI
class MyUI extends UI {
protected void init(VaadinRequest request) {
setContent(new HorizontalLayout(
new Label("Gender"),
new ComboBox().with{ addItem("M"); addItem("F"); it } // no label!
))
}
}
You can use Vaadin FormLayout component wherever you required to put caption before input fields.
Example:
// A FormLayout used outside the context of a Form
FormLayout fl = new FormLayout();
// Make the FormLayout shrink to its contents
fl.setSizeUndefined();
TextField tf = new TextField("A Field");
fl.addComponent(tf);
// Mark the first field as required
tf.setRequired(true);
tf.setRequiredError("The Field may not be empty.");
TextField tf2 = new TextField("Another Field");
fl.addComponent(tf2);
Related
MainView include InformationCOmponent:
#Push
#Route
public class MainView extends VerticalLayout {
InformationComponent infoComponent;
public MainView(#Autowired StudentRepository studentRepo, #Autowired Job jobImportCsv, #Autowired JobLauncher jobLauncher, #Value("${file.local-tmp-file}") String inputFile) {
[...] // some stuffs
infoComponent = new InformationComponent(studentRepo);
add(infoComponent);
}
//update when job process is over
private void uploadFileSuccceed() {
infoComponent.update(myUploadComponent.getFile());
}
InformationComponent:
public class InformationComponent extends HorizontalLayout {
StudentRepository studentRepo;
Label nbLineInFile = new Label();
VerticalLayout componentLeft = new VerticalLayout();;
VerticalLayout componentRight = new VerticalLayout();;
public InformationComponent(StudentRepository studentRepo) {
[...] // some init and style stuff
addLine("Nombre de lignes dans le fichier", nbLineInFile);
}
private void addLine(String label, Label value) {
componentLeft.add(new Label(label));
componentRight.add(value);
}
public void update(File file) {
try {
long nbLines = Files.lines(file.toPath(), Charset.defaultCharset()).count();
System.out.println("UPDATED! " +nbLines); // value is display in console ok!
UI.getCurrent().access(() -> nbLineInFile.setText(nbLines)); // UI is not updated!!
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
When I call InformationComponent from MainView the Label is not update in the browser.
UI.getCurrent().access(() -> nbLineInFile.setText(nbLines))
also try wwith #Push(PushMode.MANUAL) and ui.push(); but doesn't work either...
Complete source code is here: https://github.com/Tyvain/ProcessUploadedFile-Vaadin_SpringBatch/tree/push-not-working
I suspect the problem here is that uploadFileSuccceed() is run from a background thread, in which case UI.getCurrent() will return null. This would cause a NullPointerException that either kills the background thread or alternatively the exception is caught and silently ignored by the caller. Another alternative is that uploadFileSuccceed() happens through a different browser window and thus also a different UI instance, which means that the changes would be pushed in the context of the wrong UI.
For exactly these reasons, UI.getCurrent().access(...) is generally an anti pattern, even though it's unfortunately quite widely used in old examples.
You can check whether this is the cause of your problem by logging the value of UI.getCurrent() in the beginning of the update method, and comparing that to the value of UI.getCurrent() e.g. in the constructor of InformationComponent.
To properly fix the problem, you should pass the correct UI instance through the entire chain of events originating from whatever triggers the background processing to start. You should also note that it might be tempting to use the getUI() method that is available in any Component subclass, but that method is not thread safe and should thus be avoided in background threads.
As a final notice, I would recommend using the Span or Text component instead of Label in cases like this. In Vaadin 10, the Label component has been changed to use the <label> HTML element, which means that it's mainly intended to be used as the label of an input component.
Based on information provided by Leif you should do something like the following example.
At runtime, when this HorizontalLayout subclass object is attached to a parent UI object, its onAttach method is called. At that point we can remember the UI by storing its reference is a member variable named ui. Actually, an Optional<UI> is returned rather than a UI object, so we need to test for null, though it should never be null at point of onAttach.
public class InformationComponent extends HorizontalLayout {
UI ui;
StudentRepository studentRepo;
Label nbLineInFile = new Label();
VerticalLayout componentLeft = new VerticalLayout();;
VerticalLayout componentRight = new VerticalLayout();;
public InformationComponent(StudentRepository studentRepo) {
[...] // some init and style stuff
addLine("Nombre de lignes dans le fichier", nbLineInFile);
}
private void addLine(String label, Label value) {
componentLeft.add(new Label(label));
componentRight.add(value);
}
public void update(File file) {
try {
long nbLines = Files.lines(file.toPath(), Charset.defaultCharset()).count();
System.out.println("UPDATED! " +nbLines); // value is display in console ok!
this.ui.access(() -> nbLineInFile.setText(nbLines)); // UI is not updated!!
} catch (IOException e) {
throw new RuntimeException(e);
} catch (UIDetachedException e) {
// Do here what is needed to do if UI is no longer attached, user has closed the browser
}
#Override // Called when this component (this `HorizontalLayout`) is attached to a `UI` object.
public void onAttach() {
ui = this.getUI().orElseThrow( () -> new IllegalStateException("No UI found, which should be impossible at point of `onAttach` being called.") );
}
I'm very new to dart, so don't judge me. :))
I just started to write a simple ToDo App in Dart. I want to add a button to the item, so i can delete it from the list. I add the button successfully, but don't get the click event working.
I know why the code isn't working, but don't know, what the best solution would be to solve this.
Some improvements would be awesome.
Thanks in advance
Ron
my little sexy dartpad
You need to register the onClick listen to remove the current item on every button. Here's a working version of your code.
import 'dart:html';
InputElement toDoInput;
UListElement toDoList;
void main() {
toDoInput = querySelector('#to-do-input');
toDoList = querySelector('#to-do-list');
toDoInput.onChange.listen(addToDoItem);
}
// Add item to list
void addToDoItem(Event e) {
final toDoItem = new LIElement();
toDoItem.text = toDoInput.value;
final deleteItemButton = new ButtonElement()
..text = 'Delete'
..onClick.listen((_) => toDoItem.remove());
toDoItem.children.add(deleteItemButton);
toDoList.children.add(toDoItem);
toDoInput.value = '';
}
So I have controller class with this object:
#FXML
private TextArea textArea;
then Im trying to add new MenuItem to its standard Items(that is "copy" and "select all")
#Override
public void initialize(URL location, ResourceBundle resources) {
ContextMenu contextMenu = textArea.getContextMenu();
X contextMenu.getItems().add(new MenuItem("chuj"));
textArea.setContextMenu(contextMenu);
and line marked with X gets me null pointer exception. Why?
Interesting part is that I can get contextMenu from textArea and set it back to its place with no error. I just cant add something new.
Unfortunately, there's no current way to access the default context menu, which is private API in the TextInputControl. This is a known bug.
If you set a context menu, it will remove the default one. You can recreate most of the functionality in the default context menu as these simply map to public methods defined in TextArea. The exceptions are "undo" and "redo".
So you can do something like this:
private List<MenuItem> createDefaultMenuItems(TextInputControl t) {
MenuItem cut = new MenuItem("Cut");
cut.setOnAction(e -> t.cut());
MenuItem copy = new MenuItem("Copy");
copy.setOnAction(e -> t.copy());
MenuItem paste = new MenuItem("Paste");
paste.setOnAction(e -> t.paste());
MenuItem delete = new MenuItem("Delete");
delete.setOnAction(e -> t.deleteText(t.getSelection()));
MenuItem selectAll = new MenuItem("Select All");
selectAll.setOnAction(e -> t.selectAll());
BooleanBinding emptySelection = Bindings.createBooleanBinding(() ->
t.getSelection().getLength() == 0,
t.selectionProperty());
cut.disableProperty().bind(emptySelection);
copy.disableProperty().bind(emptySelection);
delete.disableProperty().bind(emptySelection);
return Arrays.asList(cut, copy, paste, delete, new SeparatorMenuItem(), selectAll);
}
Now you can do
public void initialize() {
ContextMenu contextMenu = new ContextMenu();
contextMenu.getItems().addAll(createDefaultMenuItems(textArea));
contextMenu.getItems().add(new MenuItem("chuj"));
textArea.setContextMenu(contextMenu);
}
It's a bit of a hack (replicating functionality, etc) and you lose the undo/redo (which is a real issue); but it's the best I can suggest until they fix the bug. I suggest you vote for it...
I am unable to get my custom compoenent to be validated in the dynamic form. I tried many versions but it is not working as expected. For e.g. either the label is not showing in BOLD to indicate the field is mandatory and it aloows to save the form without entering anything in the field. Only when the user enters something in the field and deletes it, then the red icon is displayed to the user that the field is mandatory.I dont know what i am missing. please help. code is below
telnumber = new CustomTelephoneTextItem();
telnumber.setName("tel");
telnumber.setTitle("Tel");
telnumber.setTitle(nerpweb.clientFactory.getMessages().tel());
Below is my Custom TextItem which i am using in the above class
public class CustomTelephoneTextItem extends CanvasItem
{
textField_value = new CustomIntegerItem();
textField_value.setShowTitle(false);
textField_value.setWidth(100);
textField_value.setRequired(true);
form.setItems(textField_value, textField_code);
form.validate();
setWrapTitle(false);
this.setCanvas(form);
First, if you want to item title showin bold, you must call item's setRequired(true).
in your code is telnumber.setRequired(true);
Second, if you want to validate item on form.validate(), you must override validate() function in your item and write validation code in this function.
in your code is call form.validate() in CustomTelephoneTextItem validate() function
Here is the code to be implemented to validate custom component
This code will go in your Custom component which you will implement
#Override
public Object getValue()
{
if (validate() && textField_value.getValue() != null)
return textField_value.getValue();
return null;
}
#Override
public void setRequired(Boolean required) {
super.setRequired(true);
}
#Override
public Boolean validate() {
return super.validate();
}
#Override
public void setValidators(Validator... validators) {
textField_value.setValidators(validators);
}
Then in the class where you will create the custom component you will call the setRequired() method,like so
telnumber.setRequired(true);
I'm wondering if it's possible to add a stylesheet or styling rules to the iframe on a RichTextArea field?
I need to make a couple of CSS tweaks to the default styling but I can't target the RichTextArea through my application stylesheet because it's loaded within an iframe.
The "problem" with the Vaadin RichTextArea component is not only in the fact that the editor field is inside an iframe element, but as with all the other Vaadin components, you also have to keep in mind that your components will not be available when the DOM ready callback (i.e. for example $(document).ready(function() {}) if using jQuery or the callback bound to a DOMContentLoaded event) will execute.
This is because, as you know, when the Vaadin application starts, you actually don't have your components inside the DOM yet, but a vaadin bootstrap process will request and take care of the rendering of your UI for you. This is actually the principle with whom GWT works also (see How does GWT provide the correct Javascript code to every browser e.g. to carry out i18n and browser compatibility?) (after all Vaadin is based on GWT).
So e.g. if you use jQuery and you have a script like this loaded at the very beginning right after the vaadinBootstrap.js script loads and executes:
$(function() {
// this code will execute, but no components are available yet.
var rTa = $(".v-richtextarea"); // this won't select your Rich text area
var len = rTa.length // len will be 0 here, as no element matches the previous selector because as stated before, there is not an element with such a class in the DOM yet.
});
After this code executes, the very "heavy" process of creating the UI components and your layout begins, your widgetsets and/or the default one get loaded, and after that you have your beautiful UI set up and ready to interact with the user.
In order to customise an existent component such a RichTextArea and e.g. add a style element to the body of its iframe element, you can certainly venture into the depths of GWT and use JSNI as you did in your answer, but there's also another way to do it, in my opinion, more compact, simple, and does not require the usage of JSNI.
All you need to do is to implement a JavaScriptExtension with a connector on the client side for your component (you can just extend Vaadin's RichTextArea), check out this simple code example:
#!java
package com.package.example;
#JavaScript({"vaadin://js/src/rich_text_area_connector.js"})
public class RichTextAreaExtension extends AbstractJavaScriptExtension {
#Override
public void extend(AbstractClientConnector connector) {
super.extend(connector);
}
}
This is the extension, then you would need to create the client side connector, which is basically a JavaScript file with a function which name is based on the package name of the extension and, of course, the extension's class name:
#!javascript
com_package_example_RichTextAreaExtension = function() {
var connectorParentId = this.getParentId();
var element = this.getElement(parentId); // this is the rich text area element, which at this point is
// If you are using jQuery, then you can just select your element like so:
var jQueryElement = $(element);
// and do whatever you would normally do with the element like
// when you are inside $(document).ready(function() {});
// or you can add a style element to the head element inside the iframe, doing something like the following:
$(element).find("iframe").contents().find('head')
.append('<link rel="stylesheet" href="./VAADIN/themes/your_theme/style_for_richtextarea_body.css" type="text/css" />');
}
And you are done. Another benefit, as you can see is that you don't have to write different code for different browsers (Mozilla, Chrome, IE), you can just use jQuery and the library will handle the compatibility for you. The last part is the extended component itself. As I said before, you can just extend Vaadin's RichTextArea:
public class RichTextAreaWithStyleOnBody extends RichTextArea {
public RichTextAreaWithStyleOnBody(String caption, Property<?> dataSource) {
super(caption);
if (dataSource != null)
setPropertyDataSource(dataSource);
new RichTextAreaExtension().extend(this);
}
public RichTextAreaWithStyleOnBody(String caption, Property<?> dataSource) {
this(caption, dataSource);
}
public RichTextAreaWithStyleOnBody(String caption) {
this(caption, null);
}
public RichTextAreaWithStyleOnBody(Property<?> dataSource) {
this(null, dataSource);
}
public RichTextAreaWithStyleOnBody() {
this(null, null);
}
}
Note the usage of the JavaScript extension inside the main constructor. And finally you can use it in your layout just as you would with any other component:
// Inside your UI's class
#Override
protected void init(VaadinRequest request) {
VerticalLayout layout = new VerticalLayout();
layout.setMargin(true);
layout.setSpacing(true);
setContent(layout);
RichTextArea rTa = new RichTextAreaWithStyleOnBody("A rich text area with a styled body");
rTa.setStyleName("myRichTextArea"); // you can do whatever you'll like on the server side just because your rich text area extends a Vaadin server side component.
rTa.setSizeFull();
layout.addComponent(rTa);
}
As far as I know it is not possible. I had the same problem and wrote a little add-on based on a copy of the vaadin code from the RichTextArea. I added some additional methods to set font-family and font-size. Unfortunately I didn't have enough time recently to clean up my code publish a new version of the add-on.
The main functionality of the add-on is to decouple the toolbar from the area.
You can find the code in the v7 branch here: https://gitorious.org/richtexttoolbar-vaadin-addon/richtexttoolbar-vaadin-addon
After continued searching I found this discussion, which led me to a solution:
https://groups.google.com/forum/#!msg/Google-Web-Toolkit/9kwAJNhnamY/1MVfFFRq8tUJ
I ended up wrapping the RichTextImpl* classes, cloning the initElement() method from the parent class, and inserting these lines...
...for Mozilla/Safari:
_this.#com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document.designMode = 'On';
var doc = _this.#com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document;
head=doc.getElementsByTagName('head')[0];
link=document.createElement('link');
link.setAttribute('rel',"stylesheet");
link.setAttribute('href',"/path/to/richtext.css" );
link.setAttribute('type',"text/css");
head.appendChild(link);
...for IE:
var ct = "<html><head><style>#import url('/path/to/richtext.css');</style></head><body CONTENTEDITABLE='true'></body></html>" ;
doc.write( ct );
... to get a style sheet loading in my RichTextArea fields.
Hy guys, i don't know if ist still important but i did something different.
I had a token function made for my richtextarea, and for the token that a was saving in the db just save it with a style class that i already have in my "styles.css" declared.
Step by step is something like this, i am taking the stylesheet and looking for my token classes, and then i am adding the styles in my iframes header, so that my token class in the iframe is styled.
function styleRichtextareaIframe(id, themeClass, tokenClass, tokenSelectedClass) {
setTimeout(function() {
// iframe by id selected
var $iframe = $("#" + id).find(".gwt-RichTextArea");
var $head = $iframe.contents().find("head");
var $body = $iframe.contents().find("body");
var classNameToken = "." + themeClass + " " + "." + tokenClass;
var classNameToken_selected = "." + themeClass + " " + "." + tokenSelectedClass;
var fontClass = "." + themeClass + ".v-app, " + "." + themeClass + " " + ".v-window";
var styleSheets = window.document.styleSheets;
var styleSheetsLength = styleSheets.length;
var style = document.createElement('style');
style.type = 'text/css';
for (var i = 0; i < styleSheetsLength; i++) {
var classes = styleSheets[i].rules || styleSheets[i].cssRules;
for (var x = 0; x < classes.length; x++) {
if (classes[x].selectorText == classNameToken) {
style.appendChild(document.createTextNode(getClassFor(classes[x])));
}
if (classes[x].selectorText == classNameToken_selected) {
style.appendChild(document.createTextNode(getClassFor(classes[x])));
}
if (classes[x].selectorText == fontClass) {
style.appendChild(document.createTextNode(getClassFor(classes[x])));
}
}
}
function getClassFor(classObj) {
if (classObj.cssText) {
return classObj.cssText;
} else {
return classObj.style.cssText;
}
}
//Adding the classes also to the body
//of dourse you can change the output string to just give you the style class that you need.
$body.addClass("v-app " + themeClass);
$head.append(style);
}, 200);
}
I had to inset the timeout function because of the "problem" with the Vaadin RichTextArea component is not only in the fact that the editor field is inside an iframe element, but as with all the other Vaadin components, you also have to keep in mind that your components will not be available when the DOM is ready, (THE COMPONENT IS NOT IN THE DOM).
Hope this help someone, and sorry for my bad english.
Cheers.
Simplest way I came up with was extending the RichTextArea component and setting a custom style with JavaScript:
public class MyRichTextArea extends RichTextArea {
public MyRichTextArea(String className) {
setStyleName(className);
String js = "var iframeContainer = document.getElementsByClassName('" + className + "')[0];" +
"var iframe = iframeContainer.getElementsByTagName('iframe')[0];" +
"var iframeBody = iframe.contentDocument.getElementsByTagName('body')[0];" +
"iframeBody.style.fontFamily='Arial';";
JavaScript.getCurrent().execute(js);
}
}
Usage:
MyRichTextArea field = new MyRichTextArea("fieldClassName");
Please note that iframe.contentDocument should be supported with all major browsers, but for better support additional tweaks should be added - see Getting the document object of an iframe