unable to Validate Custom components in SmartGWT - smartgwt

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);

Related

Unkown Key in Vaadin 14 Grid during selection

I'm using a Grid in Vaadin 14. The grid is in multi-selection mode.
The selection handler takes a couple of seconds to complete and I'm calling setItems(...) at the end to update the items in the grid.
When the user selects another row while the previous selection handler is still running, I get an "Unknown key" error similar to the one described in https://github.com/vaadin/vaadin-grid-flow/issues/322, even though the new set of items still contains the selected item (another object instance but same according to equals()). This seems to be because the keys in the KeyMapper have already been changed due to setItems(), so the key coming from the client is not present anymore.
Is there a way to work around this, for example by disabling selection while the previous request is in progress?
UPDATE
To work around this Vaadin bug, I'm also calling setPageSize() with the exact number of items as argument. But it seems the same problem occurs even if I don't call setPageSize(), so it's probably due to setItems().
Do not change the grids items inside a SelectionListener.
You can still do all the things you wanted, but setting the items anew is not actually needed. In fact it will only create problems as you are experiencing now.
While working at this answer, I realized you will need to do your own Checkbox Column in order to be able to do actions for the one item that was just "selected", instead of removing all then add all selected ones (because much better performance). Here is how that could look.
// in my code samples, a `Foo` item can have many `Bar` items. The grid is of type Bar.
Grid.Column customSelectionColumn = grid.addComponentColumn(item -> {
Checkbox isSelected = new Checkbox();
isSelected.setValue(someParentFoo.getBars().contains(item));
isSelected.addValueChangeListener(event -> {
boolean newSelectedValue = event.getValue();
if(newSelectedValue){
someParentFoo.getBars().add(item)
} else {
someParentFoo.getBars().remove(item);
}
fooRepository.save(someParentFoo);
});
});
// make a Checkbox that selects all in the header
Checkbox toggleSelectAll = new Checkbox();
toggleSelectAll.addValueChangeListener(event -> {
if(event.getValue()){
someParentFoo.getBars().addAll(allGridItems);
} else {
someParentFoo.getBars().removeAll(allGridItems);
}
fooRepository.save(someParentFoo);
grid.getDataProvider().refreshAll(); // updates custom checkbox value of each item
});
gridHeaderRow.getCell(customSelectionColumn).setComponent(toggleSelectAll);
I solved this problem. Vaadin use data as key in HashMap. You need calc hashCode use immutable data fields. For example
public class TestData {
private int id;
private String name;
public TestData(int id) {
this.id = id;
}
#Override
public int hashCode() {
return Objects.hash(id);
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

How to extend Jenkins job page with new links and icons

I'm developing my first Jenkins plugin and followed the tutorial at wiki.jenkins-ci.org. After adding a BuildStep and generating the results I now want to publish them to the user. I would like to do this via a new link entry on the job page and a corrsponding result view page.
Unfortunatelly I do not find the right extension points for the navigation bar at the left side, the main navigation links in the center as well as the new target page. Can somebody point me in the right direction or give me a link to a tutorial or blog post that explains this scenario?
Thanks
Root Action and Actions are different. The first one goes only to initial page (root), the second one can be attach to a Project/Job or to a Build.
To create a Root Action, just need to create a class that it's:
Annotated with #Extension (so it can be found and automatically
loaded by Jenkins)
Implements RootAction Interface
Override 3 methods: getIconFileName(), getDisplayName() and getUrlName()
For example:
#Extension
public class GoogleRootAction implements RootAction{
#Override
public String getIconFileName() {
return "clipboard.png";
}
#Override
public String getDisplayName() {
return "Google URL";
}
#Override
public String getUrlName() {
return "http://www.google.pt";
}
}
To create an Action at a Project it's more complicated, and there's more than a way, depending of what you want.
But first, the class Action itself is the easy part, since it's very similar to a class RootAction. It's not annotated with #Extension and implements Action interface instead of RootAction.
For example:
public class LatestConsoleProjectAction implements Action {
private AbstractProject<?, ?> project;
#Override
public String getIconFileName() {
return (Jenkins.RESOURCE_PATH + "/images/48x48/terminal.png").replaceFirst("^/", "");
}
#Override
public String getDisplayName() {
return Messages.Latest_Console_Project_Action();
}
#Override
public String getUrlName() {
return "lastBuild/console";
}
public LatestConsoleProjectAction(final AbstractProject<?, ?> project) {
this.project = project;
}
}
The tricky part is to inform jenkins that this class Action exists. As I said, there are different ways.
For instance, one can associate an Action to a Builder or Publisher or other by just overriding getProjectAction() method at those classes.
For example:
#Override
public Action getProjectAction(AbstractProject<?, ?> project) {
return new LatestConsoleProjectAction(project);
}
But this way, the Action link will only show on Project left menu, if the corresponding Builder or Publisher is used by the job (or selected at Job configurations).
Another way, that always shows your Action link on left menu, it's create a factory class to inform jenkins. There are many factories, but at my example I will use TransientProjectActionFactory class.
For this, one will need to create a class that:
It's annotated with #Extensions
Extends TransientProjectActionFactory class (or another Factory class)
Override createFor method to create your class Action associated with Project object
For example:
#Extension
public class LatestConsoleProjectActionFactory extends TransientProjectActionFactory {
#Override
public Collection<? extends Action> createFor(AbstractProject abstractProject) {
return Collections.singletonList(new LatestConsoleProjectAction(abstractProject));
}
}
One can still filter project object to just the projects types you want. The one you don't want, just return Collections.emptyList().
Beside this two ways, I think there are others. You can see this link to reference:
https://wiki.jenkins-ci.org/display/JENKINS/Action+and+its+family+of+subtypes
Although, they refer to addAction method and others, but I couldn't use it (I have 2.19.2 Jenkins version).
Also they refer groovy, but I didn't try it, since I want to stick with Java :)
Btw, my example will create an action link to open console page of last build. Useful to avoid selecting last build and then select his console page.
After a lot of trial and error I figured out the solution.
All in all you need two different things in your project:
1) A class that inherits from ProminentProjectAction:
import hudson.model.ProminentProjectAction;
public class MyProjectAction implements ProminentProjectAction {
#Override
public String getIconFileName() {
// return the path to the icon file
return "/images/jenkins.png";
}
#Override
public String getDisplayName() {
// return the label for your link
return "MyActionLink";
}
#Override
public String getUrlName() {
// defines the suburl, which is appended to ...jenkins/job/jobname
return "myactionpage";
}
}
2) Even more important is that you add this action somehow to your project.
In my case I wanted to show the link if and only if the related build step of my plugin is configured for the actual project. So I took my Builder class and overwrote the getProjectActionsMethod.
public class MyBuilder extends Builder {
...
#Override
public Collection<? extends Action> getProjectActions(AbstractProject<?,?> project) {
List<Action> actions = new ArrayList<>();
actions.add(new MyProjectAction());
return actions;
}
}
Maybe this is not the perfect solution yet (because I'm still trying to figure out how all the artifacts are working together), but it might give people which want to implement the same a good starting point.
The page, which is loaded after clicking the link is defined as index.jelly file under source/main/resources and an underlying package with the name of the package of your Action class appended by its class name (e.g. src/main/resources/org/example/myplugin/MyProjectAction).
As it happens, there was a plugin workshop by Steven Christou at the recent Jenkins User Conference in Boston, which covered this case. You need to add a new RootAction, as shown in the following code from the JUC session
package org.jenkinsci.plugins.JUCBeer;
import hudson.Extension;
import hudson.model.RootAction;
#Extension
public class JenkinsRootAction implements RootAction {
public String getIconFileName() {
return "/images/jenkins.png";
}
public String getDisplayName() {
return "Jenkins home page";
}
public String getUrlName() {
return "http://jenkins-ci.org";
}
}
https://github.com/jenkinsci/s3explorer-plugin is my Jenkins plugin that adds an S3 Explorer link to all Jenkins project's side-panel.
An addition to #dchang comment:
I managed to make this functionality work also on pipelines by extending TransientActionFactory<WorkflowJob>:
#Extension
public static class PipelineLatestConsoleProjectActionFactory extends TransientActionFactory<WorkflowJob> {
#Override
public Class<WorkflowJob> type() {
return WorkflowJob.class;
}
#Nonnull
#Override
public Collection<? extends Action> createFor(#Nonnull WorkflowJob job) {
return Collections.singletonList(new LatestConsoleProjectAction(job));
}
}

Validation icon not shown in Table fields

When I enter edit mode of my Table, I want the data validation exclamation mark icon (!) to be shown as soon as the user goes out of bounds of any of the validation constraints.
First, a couple of notes:
I'm using Vaadin 7, so the Bean Validation addon sadly won't work.
The data validation works as intended.
Now, I have a perfectly working table for which I am using a BeanItemContainer to keep my Person beans inside.
The code for the table and the TableFieldFactory looks something like this:
table.setContainerDataSource(buildContainer());
table.setTableFieldFactory(new TableFieldFactory() {
#Override
public Field createField(Container container, Object itemId, Object propertyId, Component uiContext) {
TextField field = (TextField) DefaultFieldFactory.get().createField(container, itemId, propertyId,
uiContext);
field.setImmediate(true);
if (propertyId.equals("firstName")) {
field.addValidator(new BeanValidator(Person.class, "firstName"));
}
return field;
}
});
The Person bean looks as follows:
public class Person {
#Size(min = 5, max = 50)
private String firstName;
... setters + getters...
}
The problem is that when I type something in the firstName field and then press enter or blur/unfocus that field, no indication whatsoever of error is shown. I have to mouseover the field to see that something is wrong.
My question is two folded...
How do I get the exclamation mark icon to appear when the field is
invalid? (This works for a normal TextField that is not in a Table)
Is there a way to get an immediate response from the invalid field
(show the icon) (i.e. immediately after you type under 5 chars,
without having to press enter or blur/unfocus the field in
question).
Would be great if I could have both questions answered! =)
Thanks in advance!
The Caption, Required Indicator (the red asterisk) and - most importantly here - Error Indicator (exclamation mark) are actually provided by the layouts containing the component, not the component themselves. When editable components are displayed in a table, they are displayed without a layout - that's why no error indicator is displayed.
If I were trying to square this circle, I would look at creating a CustomField as a wrapper for the editable field - and within that CustomField display an error indicator when the wrapped/delegate field becomes invalid. I've not tried this - I've not used editable fields in a table at all - but should be fairly easy to do.
Add a TextChangeListener to the field in FieldFactory, and call field.validate() in the listener. Note, though, that field.getValue() value is not normally changed until blur/unfocus, ergo the validator will be validating the old value - unless you do field.setValue(event.getText()) in the listener. See this post on the Vaadin forum for more details.
This is the sort of thing I meant for a validating wrapper - not tried using it. You'll see initComponent simply returns the field inside a FormLayout, which should give you the icon(s) you're seeking. (You may need to delegate more methods from ValidatingWrapper to delegate than I have- but quick look suggests this may be enough.)
You'd then wrap the field in your tableFieldFactory (second code block)
public class ValidatingWrapper<T> extends CustomField<T> {
private static final long serialVersionUID = 9208404294767862319L;
protected Field<T> delegate;
public ValidatingWrapper(final Field<T> delegate) {
this.delegate = delegate;
if (delegate instanceof TextField) {
final TextField textField = (TextField) delegate;
textField.setTextChangeEventMode(AbstractTextField.TextChangeEventMode.TIMEOUT);
textField.setTextChangeTimeout(200);
textField.addTextChangeListener(new FieldEvents.TextChangeListener() {
#Override
public void textChange(FieldEvents.TextChangeEvent event) {
textField.setValue(event.getText());
textField.validate();
}
});
}
}
#Override
public Class<? extends T> getType() {
return delegate.getType();
}
#Override
protected Component initContent() {
return new FormLayout(delegate);
}
#Override
public Property getPropertyDataSource() {
return delegate.getPropertyDataSource();
}
#Override
public void setPropertyDataSource(Property newDataSource) {
delegate.setPropertyDataSource(newDataSource);
}
}
table.setContainerDataSource(buildContainer());
table.setTableFieldFactory(new TableFieldFactory() {
#Override
public Field createField(Container container, Object itemId, Object propertyId, Component uiContext) {
TextField field = (TextField) DefaultFieldFactory.get().createField(container, itemId, propertyId,
uiContext);
field.setImmediate(true);
if (propertyId.equals("firstName")) {
field.addValidator(new BeanValidator(Person.class, "firstName"));
}
return ValidatingWrapper(field);
}
});

smartgwt listgrid set cursor to hand over an icon field

I've been working on this problem for quite a while but have not been able to solve it.
I have a listgrid with a field type icon. I would like to change the cursor to "hand" over the icon.
I've been searching the web and saw that a couple of solutions existed.
One of them is using addCellOverHandler for the list grid. But I don't understand how you can change the cursor for the specified field of the listgrid.
this.addCellOverHandler(new CellOverHandler() {
#Override
public void onCellOver(CellOverEvent event) {
// not able to get the field and setCursor()
}
});
My field in the listgrid is defined as:
ListGridField iconField = new ListGridField("icon");
iconField.setAlign(Alignment.CENTER);
iconField.setType(ListGridFieldType.ICON);
iconField.setIcon("icons/icon.gif");
Like someone pointed out on the forum, a setCursor() method exist for the listgrid, but not for the field only...
If anybody has a clue...
Thanks
After some more (a lot more...) googling, I found this:
http://forums.smartclient.com/showthread.php?t=15748
The thing is to Override the getCellStyle method in the listgrid.
Here is the code I use:
#Override
protected String getCellStyle(ListGridRecord record, int rowNum, int colNum) {
if (colNum==6){
return "EC_pointer";
}
return super.getCellStyle(record, rowNum, colNum);
}
and in my CSS file:
.EC_pointer {
cursor: pointer;
}
The major fallout is that you have to know in advance the column number of the field.
Further to my comment and adding information from here I tested the following code which works with SmartGwt2.4 under Firefox 5.0.
demandesGrid.setCanHover(true);
demandesGrid.setShowHover(false);
demandesGrid.addCellHoverHandler(new CellHoverHandler() {
#Override
public void onCellHover(CellHoverEvent event) {
if (event.getColNum() == demandesGrid.getFieldNum("icon")) {
// SC.say(demandesGrid.getChildren()[3].toString());
demandesGrid.getChildren()[3].setCursor(Cursor.POINTER);
} else {
demandesGrid.getChildren()[3].setCursor(Cursor.DEFAULT);
}
}
});
I don't know if the index of the ListGridBody is constant; I found it with the SC.say line.
How about
grid.addCellOverHandler(new CellOverHandler() {
#Override
public void onCellOver(CellOverEvent event) {
//cellOver event to get field and refresh the cell
//grid.refreshCell(i, j);
}
});
The best approach is fully demonstrated here (take a look at how "comments/stats" field is being initialized).
In short, u have to extend ListGrid and override createRecordComponent method. In this method you can make any custom component you like and it will be show in grid cell.
Also ListGrid should be initialized with:
listGrid.setShowRecordComponents(true);
listGrid.setShowRecordComponentsByCell(true);

Putting parameters in velocity context in Jira 4.4

I'm developing a plugin to display additional information related to a project.
So I'm developing a Project Tab Panel module but my page does not display the paramenters I put in the velocity context.
Here is the a part of plugin xml:
<project-tabpanel key="stats-tab-panel" name="Stats Tab Panel" i18n-name-key="stats-tab-panel.name" class="it.pride.jira.plugins.StatsTabPanel">
<description key="stats-tab-panel.description">The Stats Tab Panel Plugin</description>
<label key="stats-tab-panel.label"></label>
<order>10</order>
<resource type="velocity" name="view" location="templates/tabpanels/stats-tab-panel.vm"/>
Here instead the useful part of my class:
public class StatsTabPanel extends GenericProjectTabPanel {
public StatsTabPanel(JiraAuthenticationContext jiraAuthenticationContext,
FieldVisibilityManager fieldVisibilityManager) {
super(jiraAuthenticationContext, fieldVisibilityManager);
// TODO Auto-generated constructor stub
}
public String testvalue="112002";
#Override
public boolean showPanel(BrowseContext context){
return true;
}
#Override
public Map<String, Object> createVelocityParams (BrowseContext context) {
Map<String, Object> contextMap = createVelocityParams(context);
contextMap.put("testvalue", testvalue);
return contextMap;
}
}
So, as in this case, when i write `$testvalue in my template the number doesn't show up.
What am I doing wrong?
The problem is that the getHtml method that is used to return the HTML for the tab has a Map that is the Velocity context but only contains the params in the BrowseContext object. The way to fix this is to override createVelocityParams in your class, e.g.
protected Map createVelocityParams(final BrowseContext ctx) {
Map params = super.createVelocityParams(ctx);
params.put("myparam", "some value for it");
return params;
}
The problem is that method is protected so I ended up also overriding getHtml which calls createVelocityParams in a parent class.
Some Project tabs extend GenericProjectTabPanel but some such as SummaryProjectTabPanel.java seem to use UI Fragments. I suspect that Fragments is what things are moving towards.

Resources