Vaadin addStyleName problem - vaadin

I created a TextField with TextChangeListener. When user types in certain values (in this case 'admin') then addStyleName is invoked on that field and font color becomes red. But afterwards, the value is blank and each entered character is being cleared.
Here is the code of the application. Why after adding new style to TextField its value changes?
public class VaadintestApplication extends Application {
#Override
public void init() {
Window mainWindow = new Window("Vaadintest Application");
setTheme("test");
TextField textField = new TextField("username");
textField.setEnabled(true);
textField.setTextChangeEventMode(TextChangeEventMode.EAGER);
textField.addListener(new TextChangeListener() {
public void textChange(TextChangeEvent event) {
if ("admin".equals(event.getText())) {
((TextField) event.getComponent()).addStyleName("text-error");
} else {
((TextField) event.getComponent()).removeStyleName("text-error");
}
}
});
mainWindow.addComponent(textField);
setMainWindow(mainWindow);
}
}

I would guess that the following happens:
The style name change triggers a repaint on the server, causing the TextField component to be serialized again to the client
The client receives the serialization (the whole bloody thing, not just the changed parts, because that's how things work with Vaadin), and hence it changes the contents of the textfield, while ignoring any changes that are pending from the text change listener
Solutions:
Update the value of the TextField at the same time you add/remove the style name: ((TextField) event.getComponent()).setValue(event.getText())
Create a custom client side widget which extends VTextField and add the functionality there

Related

Vaadin 23 select DatePicker content at onfocus in dialog

DatePicker behavior in view: In a Vaadin 23 application there is a view with two DatePickers and a button:
When a user steps between the fields with TAB, then the DatePicker marks the whole content as selected (which is fine and the intended behavior):
DatePicker behavior in Dialog is different: When I put two DatePicker instances into a Dialog then the TAB behavior is different: it does not mark the whole content, but sets the focus after the content:
Code:
#Route("sandbox")
public class SandboxView extends VerticalLayout {
public SandboxView() {
this.add(createDatePicker(), createDatePicker());
this.add(new Button("Open dialog", event -> {
openDialog();
}));
}
private void openDialog() {
VerticalLayout layout = new VerticalLayout(createDatePicker(), createDatePicker());
Dialog dialog = new Dialog(layout);
dialog.open();
}
private DatePicker createDatePicker() {
DatePicker datePicker = new DatePicker();
datePicker.setValue(LocalDate.now());
datePicker.setAutoOpen(false);
return datePicker;
}
}
Intended behavior: I'd like the DatePicker to show the same behavior in a Dialog as it is in a view.
Question: How can I do this?
What I tried: When a focus listener calls select() at the INPUT child node in JavaScript (see code below), the whole content is marked/selected (which is as intended). But this also marks/selects the whole content when the user clicks with the mouse into the field (which is not intended).
field.getElement().addEventListener("focus", new DomEventListener() {
#Override
public void handleEvent(DomEvent event) {
event.getSource().executeJs("for (var i=0;i<$0.childNodes.length;i++){if($0.childNodes[i].nodeName=='INPUT'){$0.childNodes[i].select();}}");
}
});
Update for TextField: When using TextFields instead of DatePickers, it's the same behavior: in a view a TAB marks/selects the whole content. In a Dialog a TAB sets the focus before the content, but does not mark/select it:
This behavior is fixed in Vaadin 23.1.6.

Customize SmartGwt ListGrid dynamically for passwords

I have a com.smartgwt.client.widgets.grid.ListGrid for my configurations screen.
I have 3 ListGridFields name, value, isHidden.
I want to use PasswordItem if isHidden is true, and TextItem if isidden is false.
How can I customize the grid?
I tried with setEditorCustomizer, but it only works when I am editing a cell. In view mode I am able to see the text.
I don't think there's a way to do what you want (show the PasswordItem editor when visualizing the ListGrid's fields). As you already found out, setEditorCustomizer works only when in editing mode.
But you can mask the field values. Here is how to do it:
// very important for not having to set all fields all over again
// when the target field is customized
listGrid.setUseAllDataSourceFields(true);
// customize the isHidden field to make it respond to changes and
// hide/show the password field accordingly
ListGridField isHidden = new ListGridField("isHiddenFieldName");
isHidden.addChangedHandler(new ChangedHandler() {
#Override
public void onChanged(ChangedEvent event) {
// the name of this field has to match the name of the field you
// want to hide (as defined in your data source descriptor,
// ListGridField definition, etc).
ListGridField passwordField = new ListGridField("passwordFieldName");
if ((Boolean) event.getValue() == true) {
passwordField.setCellFormatter(new CellFormatter() {
#Override
public String format(Object value, ListGridRecord record, int rowNum, int colNum) {
return ((String) value).replaceAll(".", "*");
}
});
}
// you need to re-add here the isHidden field for the ChangeHandler to
// be present when recreating the ListGrid
listGrid.setFields(isHidden, passwordField);
listGrid.markForRedraw();
}
});
// add the customized field to the listGrid, so that we can have the
// desired ChangeHandler for the isHidden field
listGrid.setFields(isHidden);
Bear in mind that if you hide the value (or use a PassowrdItem), an 'expert' user could see the value, simply because the server is sending the value to the client.
If you actually have a security constraint, you may use DataSourceField.viewRequires, which accepts velocity expressions.

ListGrid put focus in the FilterEditor

I have a ListGrid defined like this:
ListGrid lgrid = new ListGrid();
ListGridField first = new ListGridField("first",first");
ListGridField second = new ListGridField("second ",second ");
lgrid.setFields(first, second);
lgrid.setShowFilterEditor(true);
¿How can i put the keyboard focus in the first filter editor field after i call show() in the layout?
Thxs in advance.
Depending on what your use case is (which would be useful to provide a more focused answer), the solution you posted might not be what you really need, because if you scroll on your ListGrid, it could trigger a new data fetch (if there are more records to show), and move the cursor to the filter editor as a result (if your user is editing some records at that point, the cursor moving to the filter row is not what she would want to happen!!).
In such a case, you probably just want to call grid.focusInFilterEditor("fieldToFocus") after the listGrid.show() statement or in the ClickHandler of some button you use to fetch the data, etc.
Anyway, you don't need the Timer either. This works:
listGrid.addDataArrivedHandler(new DataArrivedHandler() {
#Override
public void onDataArrived(DataArrivedEvent event) {
grid.focusInFilterEditor("fieldToFocus");
}
});
I got the solution, its focusInFilterEditor, this is an example to set the focus after the data arrived to the grid:
// Put the focus on the first listGrid field when is loaded
listGrid.addDataArrivedHandler(new DataArrivedHandler() {
#Override
public void onDataArrived(DataArrivedEvent event) {
Timer t = new Timer() {
public void run() {
if(listGrid.getFilterEditorCriteria() == null){
listGrid.focusInFilterEditor("fieldToFocus");
}
}
};
t.schedule(600);
}
});

VaadinSession attribute and updating session-bound components

I have a Vaadin Navigator with multiple View elements. Each view has a different purpose however some also contain common traits that I have put inside custom components.
One of those custom components is the menu - it is positioned at the top and allows navigation between the different views. I create and add this component inside the constructor of each view (if you are interested in the menu's implementation see the end of this post). Here is a skeleton for each custom view:
class MyViewX implements View {
MenuViewComponent mvc;
public MyViewX() {
mvc = new MenuViewComponent();
addComponent(mvc);
}
#Override
public void enter(ViewChangeEvent event) {
}
}
So far, so good. In order to make things simple I will explain my problem using a simple label and not one of my other custom components but the dependency that I will describe here is the same for those components just like with the label.
Let's say I have a label which sole purpose is to display a greeting with the user's username. In order to do that I use VaadinSession where I store the attribute. This is done by my LoginController, which validates the user by looking into a database and if the user is present, the attribute is set and one of the views is opened automatically. The problem is that VaadinSession.getCurrent().getAttribute("username") returns null when called inside the constructor. This of course makes sense omho because a constructor should not be bound by a session-attribute.
So far I have managed to use the enter() method where there is no problem in retrieving session attributes:
class MyViewX implements View {
MenuViewComponent mvc;
public MyViewX() {
mvc = new MenuViewComponent();
addComponent(mvc);
}
#Override
public void enter(ViewChangeEvent event) {
String username = (String)VaadinSession.getCurrent().getAttribute("username");
Label greeting = new Label("Hello " + username);
addComponent(greeting);
}
}
The issue that comes from this is obvious - whenever I open the view where this label is present, a new label is added so if I re-visit the view 10 times, I will get 10 labels. Even if I move the label to be a class member variable the addComponent(...) is the one that screws things up. Some of my custom components really depend on the username attribute (in order to display user-specific content) hence I also have to place those in the enter(...) method. The addComponent(...) makes a mess out of it. I even tried the dirty way of removing a component and then re-adding it alas! in vain:
class MyViewX implements View {
MenuViewComponent mvc;
Label greeting;
public MyViewX() {
mvc = new MenuViewComponent();
addComponent(mvc);
}
#Override
public void enter(ViewChangeEvent event) {
String username = (String)VaadinSession.getCurrent().getAttribute("username");
greeting = new Label("Hello " + username);
// Remove if present
try { removeComponent(greeting); }
catch(Exception ex) { }
// Add again but with new content
addComponent(greeting);
}
}
but it's still not working. So my question is: what is the simplest way of updating a component that requires session-bound attributes?
The navigation via the menu custom component is omho not the issue here since all components of the menu are loaded in it's constructor. That's why it's also load that component in particular in a view's own constructor. Here is an example of a button in my menu that opens a view:
#SuppressWarnings("serial")
#PreserveOnRefresh
public class MenuViewComponent extends CustomComponent {
public MenuViewComponent(boolean adminMode) {
HorizontalLayout layout = new HorizontalLayout();
Label title = new Label("<h2><b>Vaadin Research Project</b></h2>");
title.setContentMode(ContentMode.HTML);
layout.addComponent(title);
layout.setComponentAlignment(title, Alignment.TOP_LEFT);
Button personalDashboardButton = new Button("Personal dashboard", new Button.ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
getUI().getNavigator().navigateTo(MainController.PERSONALDASHBOARDVIEW);
}
});
personalDashboardButton.setStyleName(BaseTheme.BUTTON_LINK);
layout.addComponent(personalDashboardButton);
layout.setComponentAlignment(personalDashboardButton, Alignment.TOP_CENTER);
// Add other buttons for other views
layout.setSizeUndefined();
layout.setSpacing(true);
setSizeUndefined();
setCompositionRoot(layout);
}
}
PERSONALDASHBOARDVIEW is just one of the many views I have.
It may be worth considering how long should your view instances "live", just as long they're displayed, until the session ends or a mix of the two. With this in mind and depending on what needs to happen when you enter/re-enter a view, you have at least the following 3 options:
1) Recreate the whole view (allowing for early view garbage-collection)
first register a ClassBasedViewProvider (instead of a StaticViewProvider) which does not hold references to the created views:
navigator = new Navigator(this, viewDisplay);
navigator.addProvider(new Navigator.ClassBasedViewProvider(MyView.NAME, MyView.class));
simple view implementation
public class MyView extends VerticalLayout implements View {
public static final String NAME = "myViewName";
#Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
// initialize tables, charts and all the other cool stuff
addComponent(new SweetComponentWithLotsOfStuff());
}
}
2) Keep some already created components and replace others
public class MyView extends VerticalLayout implements View {
private MySweetComponentWithLotsOfStuff mySweetComponentWithLotsOfStuff;
public MyView() {
// initialize only critical stuff here or things that don't change on enter
addComponent(new MyNavigationBar());
}
#Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
// oh, so the user does indeed want to see stuff. great, let's do some cleanup first
removeComponent(mySweetComponentWithLotsOfStuff);
// initialize tables, charts and all the other cool stuff
mySweetComponentWithLotsOfStuff = new SweetComponentWithLotsOfStuff();
// show it
addComponent(mySweetComponentWithLotsOfStuff);
}
}
3) Lazy creating and updating (or not) the content when entering
public class MyView extends VerticalLayout implements View {
private boolean isFirstDisplay = true;
private MySweetComponentWithLotsOfStuff mySweetComponentWithLotsOfStuff;
public MyView() {
// initialize only critical stuff here, as the user may not even see this view
}
#Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
// oh, so the user does indeed want to see stuff
if (isFirstDisplay) {
isFirstDisplay = false;
// lazily initialize tables, charts and all the other cool stuff
mySweetComponentWithLotsOfStuff = new SweetComponentWithLotsOfStuff();
addComponent(mySweetComponentWithLotsOfStuff);
} else {
// maybe trigger component updates, or simply don't do anything
mySweetComponentWithLotsOfStuff.updateWhateverIsRequired();
}
}
}
I'm sure (and curious) that there may be other options, but I've mainly used a variation of 1) using spring with prototype views and component tabs.

How do I prevent one specific character to be entered into a UITextView (in Xamarin)?

I need to prevent users from entering a caret ("^") into a notes field that is implemented in a UITextView. I found this question: prevent lower case in UITextView, but it's not clear to me when/how often the shouldChangeTextInRange method will be called. Is it called for each keystroke? Is it named this way because it will be called once for a paste? Instead of preventing the entire paste operation, I'd rather strip out the offending carets, which it doesn't look like that method can do.
Our main application (written in C++Builder with VCL components) can filter keystrokes, so that if ^ is pressed, it beeps and the character is not added to the text field. I would like to replicate that behavior here.
Is there any way to do that sanely in Xamarin? I'm doing iOS first, and might be asking about Android later.
Thanks for your help!
Are you using Xamarin.Forms to build your UI? If you're going to be targeting Android, I highly recommend doing so.
If that is the case, then you can easily do this with a custom Entry subclass:
public class FilteredEntry : Entry
{
private string FilterRegex { get; set; }
public FilteredEntry (string filterRegex)
{
// if we received some regex, apply it
if (!String.IsNullOrEmpty (filterRegex)) {
base.TextChanged += EntryTextChanged;
FilterRegex = filterRegex;
}
}
void EntryTextChanged (object sender, TextChangedEventArgs e)
{
string newText = e.NewTextValue;
(sender as Entry).Text = Regex.Replace (newText, FilterRegex, String.Empty);
}
}
Usage:
// The root page of your application
MainPage = new ContentPage {
Content = new StackLayout {
VerticalOptions = LayoutOptions.Center,
Children = {
new FilteredEntry(#"\^")
}
}
};
A typed ^ will be stripped out of the Entry's Text.

Resources