-
Notifications
You must be signed in to change notification settings - Fork 64
ModelGenerator
Since 1.2.1
ExtDirectSpring contains a simple generator that inspects java classes and creates the corresponding Javascript code for Model objects. The generator is able to create code for Ext JS 4.x, 5.x and Sencha Touch 2.x. Note: the generator has been moved into its own project named extclassgenerator.
To add the generator to your application create a @Controller class and use ModelGenerator.writeModel to write the Javascript code into the response. The important part here is the url in the @RequestMapping annotation. If the application uses Ext.Loader to load classes on demand this has to match the path to the directory where the models are normally stored.
@Controller
public class ModelController {
@RequestMapping("/app/model/User.js")
public void user(HttpServletRequest request, HttpServletResponse response) throws IOException {
ModelGenerator.writeModel(request, response, User.class, OutputFormat.EXTJS4);
//or for Sencha Touch 2
//ModelGenerator.writeModel(request, response, User.class, OutputFormat.TOUCH2);
}
}
since 1.2.3 the ModelGenerator also works in a simple servlet:
@WebServlet(urlPatterns = "/app/model/User.js")
public class SongModelServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ModelGenerator.writeModel(request, response, User.class, OutputFormat.EXTJS4);
}
}
This code writes the generated Javascript on one line. To make debugging easier writeModel supports a fifth parameter (debug). If true the generator writes the code in pretty format:
ModelGenerator.writeModel(request, response, User.class, OutputFormat.EXTJS4, true);
Example
Provided with this class
package test;
public class User {
private Integer id;
private String firstName;
private String lastName;
private String email;
private String city;
//get and set methods
//....
}
the generator creates this Ext JS code
Ext.define('test.User', {
extend: 'Ext.data.Model',
fields: [ {
name: 'id',
type: 'int'
}, {
name: 'firstName',
type: 'string'
}, {
name: 'lastName',
type: 'string'
}, {
name: 'email',
type: 'string'
}, {
name: 'city',
type: 'string'
} ]
});
The fields of the class do not have to be annotated with a special annotation if the datatype is one of the supported datatypes according to the table below. If a field uses an unsupported datatype (for example: List<Integer> ids) annotate it with a simple @ModelField. This will create a field in the model code with datatype 'auto'.
The generator tries to read all public accessible properties. If a field should not be part of the model code annotate it with @JsonIgnore. The annotations @ModelField and @ModelAssociation have precedence over @JsonIgnore. A field will appear in the model code if one of these two annotations is present despite the @JsonIgnore annotation.
As default the generator takes the fully qualified name of the class as the name of the model object and the name of the property as the name of the field.
Datatypes are mapped according to this list if not overwritten with the @ModelField annotation (see below)
Java | Ext JS/Sencha Touch |
---|---|
Byte | int |
Short | int |
Integer | int |
Long | int |
java.math.BigInteger | int |
byte | int |
short | int |
int | int |
long | int |
Float | float |
Double | float |
java.math.BigDecimal | float |
float | float |
double | float |
String | string |
java.util.Date | date |
java.util.Calendar | date |
java.util.GregorianCalendar | date |
java.sql.Date | date |
java.sql.Timestamp | date |
org.joda.time.DateTime | date |
org.joda.time.LocalDate | date |
Boolean | boolean |
boolean | boolean |
Associations (since 1.2.3)
The generator is able to generate code for associations. To recognize associations the bean in question needs to have the @ModelAssociation annotation on the
field that forms the association.
Based on this class definition:
public class Book {
public int id;
@ModelAssociation(value = ModelAssociationType.HAS_MANY, model = Author.class)
public List<Author> authors;
}
The generator creates this Ext JS model:
Ext.define('Book', {
extend : 'Ext.data.Model',
fields : [ {
name : 'id',
type : 'int'
}],
associations : [ {
type : 'hasMany',
model : 'Author',
associationKey : 'authors',
foreignKey : 'book_id',
name : 'authors'
} ]
});
Description about the @ModelAssociation annotation see below.
Validation (since 1.2.2)
The generator is able to add validation configurations. The generator reads the javax.validation and
Hibernate validator annotations and creates corresponding validation configurations.
The above call to ModelGenerator.writeModel does not add any validation configurations. The program has to call the method with an an additional parameter that specifies what
validations should be generated (IncludeValidation.BUILTIN or IncludeValidation.ALL).
ModelGenerator.writeModel(request, response, User.class, OutputFormat.EXTJS4, IncludeValidation.BUILTIN, false);
This call only adds validations that are built into Ext JS and Sencha Touch.
Java | Ext JS/Sencha Touch Code |
---|---|
javax.validation.constraints.NotNull | presence |
org.hibernate.validator.constraints.NotEmpty | presence |
javax.validation.constraints.Size | length |
org.hibernate.validator.constraints.Length | length |
javax.validation.constraints.Pattern | format |
org.hibernate.validator.constraints.Email |
ModelGenerator.writeModel(request, response, User.class, OutputFormat.EXTJS4, IncludeValidation.BUILTIN, false);
This call will all the above and the following additional validations. These validations are not built in Ext JS and Sencha Touch. You have to add the javascript code for this validations if you want to use them. Here is an example: https://github.com/ralscha/extdirectspring-demo/blob/master/src/main/webapp/ux-validations.js
Java | Ext JS/Sencha Touch Code |
---|---|
javax.validation.constraints.DecimalMax | range |
javax.validation.constraints.DecimalMin | range |
javax.validation.constraints.Max | range |
javax.validation.constraints.Min | range |
javax.validation.constraints.Digits | digits |
javax.validation.constraints.Future | future |
javax.validation.constraints.Past | past |
org.hibernate.validator.constraints.CreditCardNumber | creditCardNumber |
org.hibernate.validator.constraints.NotBlank | notBlank |
org.hibernate.validator.constraints.Range | range |
Part of the generator are the annotations @ModelField and @Model. With these two annotations the default behaviour can be overwritten and additional informations can be specified that are written to the model object source code.
@Model
Attribute | Description |
---|---|
value | "Classname" of the model. See Ext.data.Model. If not present full qualified name of the class is used. |
idProperty | Name of the id property. See Ext.data.Model#idProperty. If not present default value of 'id' is used. |
paging | Set this to true if the read method returns an instance of ExtDirectStoreResult. This adds reader: { root: 'records' } to the proxy configuration |
readMethod | Specifies the read method. This is a ExtDirect reference in the form action.methodName. See Ext.data.proxy.Direct#api. If only the readMethod is specified generator will write property directFn instead. |
createMethod | Specifies the create method. This is a ExtDirect reference in the form action.methodName. See Ext.data.proxy.Direct#api. |
updateMethod | Specifies the update method. This is a ExtDirect reference in the form action.methodName. See Ext.data.proxy.Direct#api. |
destroyMethod | Specifies the destroy method. This is a ExtDirect reference in the form action.methodName. See Ext.data.proxy.Direct#api. |
messageProperty | (since 1.3.6) Specifies the messageProperty property in the reader config. See Ext.data.reader.Reader#messageProperty. |
disablePagingParameters | (since 1.3.8) When set to true the generator sets the paging parameters (pageParam, startParam, limitParam) in the proxy config to undefinded (Ext JS) or false (Touch). Defaults to false |
@ModelField
Attribute | Description |
---|---|
value | Name of the field. Property name. If not present the name of the property is used. |
type | Type of the field. Property type. If not specified the library uses the list above to determine the type. |
defaultValue | Default value. Property defaultValue. |
dateFormat | Specifies the format of date. Property dateFormat. For a list of all supported formats see Sencha Doc: Ext.Date. Will be ignored if the field is not a date field. |
useNull | If true null value is used if the value cannot be parsed. If false default values are used (0 for integer and float, "" for string and false for boolean). Property useNull.Only used if type of field is int, float, string or boolean. |
mapping | Typical use for a virtual field to extract field data from the model object. Property 'mapping' in JS. |
persist | Prevent the value of this field to be serialized or written with Ext.data.writer.Writer. Typical use for a virtual field. Property 'persist' in JS. |
convert | Function which coerces string values in raw data into the field's type. Typical use for a virtual field. Property 'Ext.data.Field.convert' in JS. |
@ModelAssociation
Attribute | Valid for | Description |
---|---|---|
value | all |
Describes the type of the association. ModelAssociationType.HAS_MANY, ModelAssociationType.BELONGS_TO or ModelAssociationType.HAS_ONE.
Corresponds to the type config property. |
model | all |
The class of the model that this object is being associated with.
If not specified the full qualified class name is used.
Corresponds to the model config property. |
autoLoad | HAS_MANY |
True to automatically load the related store from a remote source when instantiated. Defaults to false.
Corresponds to the autoLoad config property. |
foreignKey | all |
The name of the foreign key on the associated model that links it to the
owner model. If missing the lowercased name of the owner class plus "_id" is used.
Corresponds to the foreignKey config property. |
name | HAS_MANY |
The name of the function to create on the owner model to retrieve the
child store. If not specified, the name of the field is used.
Corresponds to the name config property. |
primaryKey | all |
The name of the primary key on the associated model. Default is "id".
If the associated model is annotated with @Model(idProperty="..") this value is used.
Corresponds to the primaryKey config property. |
setterName | BELONGS_TO, HAS_ONE |
The name of the setter function that will be added to the local model's
prototype. Defaults to 'set' + name of the field, e.g. setCategory.
Corresponds to the setterName config property. |
getterName | BELONGS_TO, HAS_ONE |
The name of the getter function that will be added to the local model's
prototype. Defaults to 'get' + name of the field, e.g. getCategory.
Corresponds to the getterName config property. |
Example
Java classes
@Model(value = "MyApp.Bean", idProperty = "myVerySpecialId",
paging = true, readMethod = "beanService.read",
createMethod = "beanService.create", updateMethod = "beanService.update",
destroyMethod = "beanService.destroy")
public class Bean {
@ModelField("myVerySpecialId")
private int id;
private String firstName;
@NotEmpty
private String lastName;
@ModelField
private List<Integer> someIds;
@ModelField(dateFormat = "c")
@Past
private Date dateOfBirth;
@JsonIgnore
private String password;
@ModelField
@JsonIgnore
@Email
private String email;
@ModelField(type = ModelType.STRING, useNull = true)
private Long something;
@ModelField(value = "active", defaultValue = "true")
private boolean flag;
@ModelField(persist = true)
@DecimalMax("500000")
private BigInteger bigValue;
@ModelField(mapping = "bigValue", persist = false, convert = "function(v, record) { return (record.raw.bigValue > 1000000);}")
private boolean aBooleanVirtual;
@ModelAssociation(value = ModelAssociationType.HAS_MANY, model = OtherBean.class, autoLoad = true)
public List<OtherBean> otherBeans;
//get/set methods
}
@Model(value = "MyApp.OtherBean")
public class OtherBean {
private int id;
private int bean_id;
@JsonIgnore
@ModelAssociation(value = ModelAssociationType.BELONGS_TO)
private Bean bean;
//get/set methods
}
Sencha Touch 2.x model objects
Ext.define("MyApp.Bean",
{
extend : "Ext.data.Model",
config : {
idProperty : "myVerySpecialId",
fields : [ {
name : "myVerySpecialId",
type : "int"
}, {
name : "firstName",
type : "string"
}, {
name : "lastName",
type : "string"
}, {
name : "someIds",
type : "auto"
}, {
name : "dateOfBirth",
type : "date",
dateFormat : "c"
}, {
name : "email",
type : "string"
}, {
name : "something",
type : "string",
useNull : true
}, {
name : "active",
type : "boolean",
defaultValue : true
}, {
name : "bigValue",
type : "int"
}, {
name : "aBooleanVirtual",
type : "boolean",
mapping : "bigValue",
persist : false,
convert : function(v, record) { return (record.raw.bigValue > 1000000);}
} ],
associations : [ {
type : "hasMany",
model : "MyApp.OtherBean",
associationKey : "otherBeans",
foreignKey : "bean_id",
primaryKey : "myVerySpecialId",
autoLoad : true,
name : "otherBeans"
} ],
validations : [ {
type : "presence",
field : "lastName"
}, {
type : "past",
field : "dateOfBirth"
}, {
type : "email",
field : "email"
}, {
type : "range",
field : "bigValue",
max : 500000
} ],
proxy : {
type : "direct",
idParam : "myVerySpecialId",
api : {
read : beanService.read,
create : beanService.create,
update : beanService.update,
destroy : beanService.destroy
},
reader : {
root : "records"
}
}
}
});
Ext.define("MyApp.OtherBean",
{
extend : "Ext.data.Model",
config : {
fields : [ {
name : "id",
type : "int"
}, {
name : "bean_id",
type : "int"
} ],
associations : [ {
type : "belongsTo",
model : "MyApp.Bean",
associationKey : "bean",
foreignKey : "bean_id",
primaryKey : "myVerySpecialId",
setterName : "setBean",
getterName : "getBean"
} ]
}
});
- Introduction
- Changelog 1.7.x
- Setup Maven
- Configuration
- Server Methods
- Model Generator
- Model Generator APT
- Development
- Links