Skip to content

Commit

Permalink
#11 done
Browse files Browse the repository at this point in the history
while not really passing bean type to constructor, the GridBeanTypeConfigurator will use reflection to set bean type if it is defined, allowing `addColumn(propertyName)` to be used
  • Loading branch information
vaadin-miki committed May 5, 2020
1 parent 1b0fdee commit a8e4956
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 6 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# SuperTemplate

This is an extension of Vaadin 14.X `PolymerTemplate` class. It enables server-side access to properties defined in the template file, which is normally not possible.
This is an extension of Vaadin 14.X `PolymerTemplate` class. It enables server-side access to properties defined in the template file, which is normally not possible. It also attempts to solve a few general issues with templates.

## Limitations

Expand All @@ -19,7 +19,7 @@ Maven dependency:
<dependency>
<groupId>org.vaadin.miki</groupId>
<artifactId>super-template</artifactId>
<version>0.1.0</version>
<version>0.2.0</version>
</dependency>
```

Expand Down Expand Up @@ -77,10 +77,11 @@ For each attribute:

## Default behaviour

Unless changed, after setting each field according to its attributes, the object will go through extra configuration. This is defined by `TemplateFieldConfigurator` and by default three of those are available:
Unless changed, after setting each field according to its attributes, the object will go through extra configuration. This is defined by `TemplateFieldConfigurator` and by default those are available:
* if the value implements `HasText`, its `setText` method will be called with the text found in the element;
* if the value is an `Icon`, its server-side `Icon` will be changed to reflect the one specified in the element;
* if the value is a `Button` and the element has an icon, the icon will be set on the server side.
* if the value is a `Button` and the element has an icon, the icon will be set on the server side;
* if the value is a `Grid` and it is declared in the file as `Grid<BeanType>`, the field will be configured as if `new Grid<>(BeanType.class)` was called.

The default configurators can be skipped by:
* passing a `false` flag to the `SuperTemplate` constructor - for that particular template only;
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>org.vaadin.miki</groupId>
<artifactId>super-template</artifactId>
<version>0.2-SNAPSHOT</version>
<version>0.2.0</version>

<name>Super Template</name>
<description>An extension of PolymerTemplate that reads properties from the template and makes them available on the server side. Requires Vaadin 14.X.</description>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.vaadin.miki.supertemplate;

import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.polymertemplate.PolymerTemplate;
import com.vaadin.flow.data.binder.BeanPropertySet;
import org.jsoup.nodes.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;

/**
* Configures bean type of a field of type {@link Grid}.
* @author miki
* @since 2020-05-05
*/
public class GridBeanTypeConfigurator implements TemplateFieldConfigurator {

private static final Logger LOGGER = LoggerFactory.getLogger(GridBeanTypeConfigurator.class);

@Override
public void configureFieldValue(Field field, Object value, PolymerTemplate<?> template, Element element) {
if(value instanceof Grid) {
try {
Class<?> dataType = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
LOGGER.info("Discovered field {} to be of type Grid<{}>", field.getName(), dataType.getName());

Field beanTypeField = Grid.class.getDeclaredField("beanType");
if(beanTypeField.trySetAccessible())
beanTypeField.set(value, dataType);

Field propertySetField = Grid.class.getDeclaredField("propertySet");
propertySetField.setAccessible(true);
propertySetField.set(value, BeanPropertySet.get(dataType));
}
catch(ClassCastException cce) {
LOGGER.info("Could not discover bean type for field {}. Please make it Grid<YourDataType>.", field.getName());
} catch (IllegalAccessException | NoSuchFieldException e) {
LOGGER.warn("Could not access Grid.beanType and Grid.propertySet. Field {} will not work properly.", field.getName());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,17 @@ private TemplateFieldConfigurators() {} // instances not allowed
// buttons require extra processing, they may have an icon
public static final TemplateFieldConfigurator UPDATE_BUTTON_ICON = new UpdateButtonIconConfigurator();

/**
* Updates a grid to have a bean type, if declared in the Java class through generics.
*/
public static final TemplateFieldConfigurator GRID_BEAN_TYPE = new GridBeanTypeConfigurator();

/**
* Contains all of the {@link TemplateFieldConfigurator}s defined as {@code public static final} fields in this class.
* This array can be modified and it will affect all templates.
*/
public static final Collection<TemplateFieldConfigurator> DEFAULT_CONFIGURATORS = new ArrayList<>(Arrays.asList(
SET_TEXT, CREATE_STANDALONE_ICON, UPDATE_BUTTON_ICON
SET_TEXT, CREATE_STANDALONE_ICON, UPDATE_BUTTON_ICON, GRID_BEAN_TYPE
));

}
32 changes: 32 additions & 0 deletions src/test/java/org/vaadin/miki/supertemplate/Dummy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.vaadin.miki.supertemplate;

import java.util.Objects;

/**
* A dummy data type.
*/
public class Dummy {

private String contents;

public String getContents() {
return contents;
}

public void setContents(String contents) {
this.contents = contents;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Dummy dummy = (Dummy) o;
return Objects.equals(getContents(), dummy.getContents());
}

@Override
public int hashCode() {
return Objects.hash(getContents());
}
}
14 changes: 14 additions & 0 deletions src/test/java/org/vaadin/miki/supertemplate/SuperTemplateTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.github.mvysny.kaributesting.v10.MockNpmTemplateParser;
import com.github.mvysny.kaributesting.v10.MockVaadin;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.textfield.TextField;
import org.junit.After;
Expand Down Expand Up @@ -47,6 +48,10 @@ public void testSuperViewHasPropertiesFromTemplate() {
Assert.assertNotNull(span);
Assert.assertEquals("a-span", span.getId().orElse(null));
Assert.assertEquals("World", span.getText());
Grid<Dummy> grid = superView.getGrid();
Assert.assertNotNull(grid);
Assert.assertSame(Dummy.class, grid.getBeanType());
Assert.assertNotNull(grid.addColumn("contents")); // adding columns should work
}

@Test
Expand All @@ -59,4 +64,13 @@ public void testVaadinViewDoesNotHavePropertiesFromTemplate() {
Assert.assertFalse(vaadinView.getSpan().getId().isPresent());
}

@Test(expected = UnsupportedOperationException.class)
public void testVaadinViewDoesNotHaveProperGrid() {
VaadinView vaadinView = new VaadinView();
Assert.assertNotNull(vaadinView);
Assert.assertNotNull(vaadinView.getGrid());
Assert.assertNull(vaadinView.getGrid().getBeanType());
vaadinView.getGrid().addColumn("contents");
}

}
8 changes: 8 additions & 0 deletions src/test/java/org/vaadin/miki/supertemplate/TestView.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.polymertemplate.Id;
import com.vaadin.flow.component.textfield.TextField;
Expand All @@ -17,6 +18,9 @@ public class TestView extends SuperTemplate<TestView.TestModel> {
@Id("a-span")
private Span span;

@Id("a-grid")
private Grid<Dummy> grid;

TextField getTextField() {
return textField;
}
Expand All @@ -25,6 +29,10 @@ Span getSpan() {
return span;
}

Grid<Dummy> getGrid() {
return grid;
}

public interface TestModel extends TemplateModel {} // nothing here

}
8 changes: 8 additions & 0 deletions src/test/java/org/vaadin/miki/supertemplate/VaadinView.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.polymertemplate.Id;
import com.vaadin.flow.component.polymertemplate.PolymerTemplate;
Expand All @@ -18,6 +19,9 @@ public class VaadinView extends PolymerTemplate<TestView.TestModel> {
@Id("a-span")
private Span span;

@Id("a-grid")
private Grid<Dummy> grid;

TextField getTextField() {
return textField;
}
Expand All @@ -26,6 +30,10 @@ Span getSpan() {
return span;
}

Grid<Dummy> getGrid() {
return grid;
}

public interface TestModel extends TemplateModel {} // nothing here

}
1 change: 1 addition & 0 deletions src/test/resources/test-view.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export class TestView extends PolymerElement {
</style>
<vaadin-text-field id="a-text-field" placeholder="Hello" style="width: 100%;"></vaadin-text-field>
<span id="a-span">World</span>
<vaadin-grid id="a-grid"></vaadin-grid>
`;
}

Expand Down

0 comments on commit a8e4956

Please sign in to comment.