Unverified Commit d42322a3 authored by Daniel Bluhm's avatar Daniel Bluhm Committed by GitHub
Browse files

Merge pull request #459 from dbluhm/web-form-experiments

Experimenting with WebForm
parents 88d3b045 3ae2c7a9
/**
* Person DataElement.
* In the future, we hope to be able to generate data structures like this
* directly from DataElement Spec classes.
*/
import { DataElement } from "./data-element";
export class Person extends DataElement {
age: number;
firstName: string;
lastName: string;
constructor() {
super();
this.age = -1;
this.firstName = "";
this.lastName = "";
}
}
\ No newline at end of file
......@@ -4,14 +4,26 @@
* *Move DataElement<T> to its ts file. It would be really nice to put this in the data structures project since it shadows a class there.
* *Add a unit test?
*/
import {v4 as uuidv4} from 'uuid';
export class DataElement<T> {
export class DataElement {
privateId: string;
id: number;
name: string;
description: string;
data!: T;
comment: string;
context: string;
required: boolean;
secret: boolean;
constructor() {
this.privateId = uuidv4();
this.id = 0;
this.name = 'name';
this.description = 'description';
}
this.comment = 'comment';
this.context = 'context';
this.required = false;
this.secret = true;
}
}
\ No newline at end of file
import {css, customElement, html, LitElement, property} from 'lit-element';
import {css, customElement, html, LitElement, property, TemplateResult} from 'lit-element';
import '@vaadin/vaadin-text-field';
import { DataElement } from './data-element'
import '@vaadin/vaadin-checkbox';
import { Person } from './Person';
import { DataElement } from './data-element';
/**
* Tasks:
* *This class should be templated or take a function call back in place of updateDataElement and loadData.
* Tasks:
* *This class should be templated or take a function call back in place of
* updateDataElement and loadData.
* *Add documentation as required.
* *Remove console debug statements
* *This is just an example so the rest doesn't matter much, (the rest being changing the render() function, etc.).
* *This is just an example so the rest doesn't matter much, (the rest being
* changing the render() function, etc.).
*/
@customElement("renderer-template")
class Renderer extends LitElement {
/**
* This is the default data value. It is the empty string and indicates that the JSON has not yet been received from the server.
*/
dataElement = new DataElement<string>();
person: Person = new Person();
@property({type: String})
dataElementJSON = '';
static styles = css`
h1 {
color: hotpink;
color: blue;
text-transform: uppercase;
}
`;
constructor() {
super();
console.log("Constructed");
}
loadData() {
this.person = <Person> JSON.parse(this.dataElementJSON);
}
/**
* This operation updates the content of the template from the contents of the data element.
* Create an updater method for updating the objects field on form events.
* @param obj holding object of field
* @param field field for which an updater is generated
* @returns updater method
*/
updateDataElement(e: {target: HTMLInputElement}) {
console.log("dataElementJSON on update call = " + this.dataElementJSON);
console.log("dataElement on update call = " + this.dataElement);
// First, the event data needs to be read into the local structure
this.dataElement.data = e.target.value;
// Second, dump into the JSON property
this.dataElementJSON = JSON.stringify(this.dataElement);
console.log("dataElementJSON = " + this.dataElementJSON);
// Notify the listeners of the change.
this.dispatchEvent(new CustomEvent('data-changed', {bubbles: true, composed: true, detail: e.target.value}))
fieldUpdater(obj: object, field: string):
(e: {target: HTMLInputElement}) => void
{
let that = this;
return function(e: {target: HTMLInputElement}) {
if (typeof Reflect.get(obj, field) === 'number') {
Reflect.set(obj, field, Number(e.target.value))
} else if (typeof Reflect.get(obj, field) === 'string') {
Reflect.set(obj, field, e.target.value);
} else if (typeof Reflect.get(obj, field) === 'boolean') {
Reflect.set(obj, field, e.target.checked);
}
that.dataElementJSON = JSON.stringify(that.person);
that.dispatchEvent(new CustomEvent(
'data-changed',
{bubbles: true, composed: true, detail: e.target.value}
));
};
}
return;
/**
* Create input form element for the given field.
* @param obj holding object of field
* @param field field for which input is generated
* @returns html template result
*/
fieldInput(obj: object, field: string): TemplateResult {
if (typeof Reflect.get(obj, field) === 'boolean') {
return html`
<vaadin-checkbox
value="${field}"
?checked=${Reflect.get(obj, field)}
@change=${this.fieldUpdater(obj, field)}
>${field}</vaadin-checkbox>
`;
} else {
return html`
<vaadin-text-field
label="${field}"
.value=${Reflect.get(obj, field)}
@input=${this.fieldUpdater(obj, field)}
></vaadin-text-field>
`;
}
}
loadData() {
this.dataElement = <DataElement<string>> JSON.parse(this.dataElementJSON);
console.log("dEJ-ld = ");
console.log(this.dataElementJSON);
console.log("dataElement-ld = ")
console.log(this.dataElement);
console.log(this.dataElement.data);
/**
* Create a form for the passed DataElement.
*
* In this experiment, forms are generated by inspecting the data element.
* In the future, we hope to generate these forms directly from the DataElement
* Spec classes during Annotation Processing.
* @param element element for which a form will be generated
* @returns form html
*/
dataElementForm(element: DataElement): TemplateResult {
let form = html``;
for (let field in element) {
form = html`${form}${this.fieldInput(element, field)}`;
}
return form;
}
/**
* Render this LitElement.
*/
render() {
// Might clean this up a bit - remove some of the <h2> statements.
return html`
<script>${this.loadData()}</script>
<h1>Greetings ${this.dataElement.name}!</h1>
<h1>Greetings ${this.person.age} year old ${this.person.firstName}!</h1>
<div>
<vaadin-text-field
label="Name"
.value=${this.dataElement.data}
@input=${this.updateDataElement}
></vaadin-text-field>
${this.dataElementForm(this.person)}
</div>
<h2>${this.dataElementJSON}</h2>
<h2>${this.dataElement.description}</h2>
<h2>${this.dataElement.name}</h2>
<pre>${JSON.stringify(this.person, null, 2)}</pre>
`;
}
}
\ No newline at end of file
......@@ -1163,6 +1163,11 @@
"integrity": "sha512-oVeL12C6gQS/GAExndigSaLxTrKpQPxewx9bOcwfvJiJge4rr7wNaph4J+ns5hrmIV2as5qxqN8YKthn9qh0jw==",
"dev": true
},
"@types/uuid": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.0.1.tgz",
"integrity": "sha512-2kE8rEFgJpbBAPw5JghccEevQb0XVU0tewF/8h7wPQTeCtoJ6h8qmBIwuzUVm2MutmzC/cpCkwxudixoNYDp1A=="
},
"@vaadin/flow-deps": {
"version": "file:target/frontend",
"requires": {
......@@ -9256,6 +9261,14 @@
"requires": {
"faye-websocket": "^0.10.0",
"uuid": "^3.0.1"
},
"dependencies": {
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"dev": true
}
}
},
"sockjs-client": {
......@@ -10098,10 +10111,9 @@
"dev": true
},
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"dev": true
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz",
"integrity": "sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ=="
},
"v8-compile-cache": {
"version": "2.0.3",
......@@ -10661,6 +10673,14 @@
"requires": {
"ansi-colors": "^3.0.0",
"uuid": "^3.3.2"
},
"dependencies": {
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"dev": true
}
}
},
"webpack-merge": {
......
......@@ -3,9 +3,11 @@
"license": "UNLICENSED",
"dependencies": {
"@polymer/polymer": "3.2.0",
"@webcomponents/webcomponentsjs": "^2.2.10",
"@types/uuid": "^8.0.1",
"@vaadin/flow-deps": "./target/frontend",
"lit-element": "^2.2.0"
"@webcomponents/webcomponentsjs": "^2.2.10",
"lit-element": "^2.2.0",
"uuid": "^8.3.0"
},
"devDependencies": {
"copy-webpack-plugin": "5.1.0",
......
......@@ -159,6 +159,10 @@
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</dependency>
</dependencies>
<build>
......
/*******************************************************************************
* Copyright (c) 2020- UT-Battelle, LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Initial API and implementation and/or initial documentation -
* Jay Jay Billings
*******************************************************************************/
package gov.ornl.rse.renderer.client.test;
import org.eclipse.ice.renderer.DataElement;
/**
* @author Jay Jay Billings
*
*/
public class DataFile extends DataElement<String> {
}
/*******************************************************************************
* Copyright (c) 2020- UT-Battelle, LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Initial API and implementation and/or initial documentation -
* Jay Jay Billings
*******************************************************************************/
package gov.ornl.rse.renderer.client.test;
import java.io.Serializable;
import org.springframework.stereotype.Service;
@Service
public class GreetService implements Serializable {
public String greet(String name) {
if (name == null || name.isEmpty()) {
return "Hello anonymous user";
} else {
return "Hello " + name;
}
}
}
......@@ -11,12 +11,9 @@
*******************************************************************************/
package gov.ornl.rse.renderer.client.test;
import java.io.Serializable;
import org.eclipse.ice.data.IDataElement;
import org.eclipse.ice.renderer.DataElement;
public interface IRendererClient<T extends IDataElement> {
public interface IRendererClient<T extends IDataElement<T>> {
/**
* This operation sets the data that should be rendered.
......
......@@ -14,16 +14,10 @@ package gov.ornl.rse.renderer.client.test;
import java.util.function.BiConsumer;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import org.eclipse.ice.data.IDataElement;
import org.eclipse.ice.renderer.DataElement;
import org.eclipse.ice.renderer.JavascriptValidator;
import org.eclipse.ice.renderer.Renderer;
import org.eclipse.ice.renderer.Person;
import org.eclipse.ice.renderer.PersonImplementation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
......@@ -49,7 +43,8 @@ public class MainView extends VerticalLayout {
// Nothing to do here - just sample setup
Person ross = new PersonImplementation();
try {
ross.setName("Ross Whitfield");
ross.setFirstName("Ross");
ross.setLastName("Whitfield");
ross.setDescription("Ross' name described by a generated IDataElement");
} catch (Exception e) {
// TODO Auto-generated catch block
......
......@@ -14,20 +14,13 @@
*******************************************************************************/
package gov.ornl.rse.renderer.client.test;
import java.io.Serializable;
import org.eclipse.ice.data.IDataElement;
import org.eclipse.ice.renderer.DataElement;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mongodb.util.JSON;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.dependency.JsModule;
import elemental.json.JsonObject;
/**
* This is a base class for renderer clients tailored to Vaadin. It provides
* the basic accessors for the data member.
......@@ -36,7 +29,9 @@ import elemental.json.JsonObject;
*/
@Tag("renderer-template")
@JsModule("./src/renderer.ts")
public class VaadinRendererClient<T extends IDataElement> extends Component implements IRendererClient<T> {
public class VaadinRendererClient<T extends IDataElement<T>> extends Component implements IRendererClient<T> {
public static final String DATA_ELEMENT_JSON = "dataElementJSON";
/**
* version UID
......@@ -53,8 +48,8 @@ public class VaadinRendererClient<T extends IDataElement> extends Component impl
*/
public VaadinRendererClient() {
// This function updates the data every time the client posts and update.
getElement().addPropertyChangeListener("dataElementJSON", "data-changed", e -> {
String value = getElement().getProperty("dataElementJSON");
getElement().addPropertyChangeListener(DATA_ELEMENT_JSON, "data-changed", e -> {
String value = getElement().getProperty(DATA_ELEMENT_JSON);
data.fromJson(value);
});
}
......@@ -68,7 +63,7 @@ public class VaadinRendererClient<T extends IDataElement> extends Component impl
@Override
public void setData(T otherData) {
data = otherData;
getElement().setProperty("dataElementJSON", data.toJson());
getElement().setProperty(DATA_ELEMENT_JSON, data.toJson());
}
/**
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment