Skip to content
This repository has been archived by the owner on Feb 22, 2019. It is now read-only.

cdo.form_and_model_generators

Pavel Vlasov edited this page May 19, 2015 · 16 revisions

Form and model generators

org.nasdanika.cdo bundle provides several classes for generating Bootstrap/AngularJS/KnockoutJS forms and models from EClass and EOperation metadata and annotations:

Annotations

Annotations used by the generators are:

  • org.nasdanika.cdo.web.html.form-control for EClass features - attributes and references (by default controls are not generated for references) and for EOperation parameters. This annotation supports the following details keys:
    • attribute:<attribute name> - Allows to specify control attribute.
    • group-attribute:<attribute name> - Allows to specify group attribute.
    • control-id - Control ID, defaults to <feature|parameter name>_control.
    • default - Default value. If not set then attribute default value is used for attributes.
    • help-text - Help text. No default. In AngularJS generators help text is used to display control-level validation messages.
      • inline - Inline checkbox control if set to true.
    • input-type - One of InputType values. Default value is computed from feature/parameter type by getInputType() methods which can be overriden.
    • label - Control label. Defaults to feature/parameter name split by camel case with the first word capitalized and the rest uncapitalized. E.g. a label for userName parameter or feature would be "User name".
    • placeholder - Control placeholder for controls which support placeholders. Default is the same as for label.
    • private - If true then feature/parameter is not included into the generated form.
    • required - Marks the generated control as required if set to true.
    • style:<style name> - Allows to customize control style. E.g. style:background -> yellow
    • group-style:<style name> - Allows to customize group style.
    • validator - Control validator used by AngularJS and KnockoutJS generators. The value of the validator details key shall be a JavaScript function body returning validation message or a promise for a validation message. If the return value is falsey, e.g. undefined or an empty string, then validation is successful, otherwise the return value is displayed as a control-level error message. The function body has access to the control value through value parameter and to the whole model through this.
  • org.nasdanika.cdo.web.html.form for EClasses and EOperations. This annotation supports the following details keys:
    • model - Object declarations to add to the model definition, e.g. helper functions.
    • validator is used by AngularJS and KnockoutJS generators in generation of validate() model function. The value of the validator details key shall be a JavaScript function body returning validation message or a promise for a validation message. If the return value is falsey, e.g. undefined or an empty string, then validation is successful, otherwise the return value is displayed as a form-level error message. The function body has access to the model data through value parameter and to the whole model through this.

AngularJS model

AngularJS generators have generateModel() method which returns JavaScript object definition with the following entries:

  • data - an object which is either empty or contains default values for features/parameters. Form controls are bound to this object's entries. For existing object replace the generated data entry with the JavaScript API object.
  • createData() - a function returning a fresh data object either empty or with default values.
  • clear() - this function sets model data to a fresh object and empties validation results.
  • validationResults - an empty object to hold validation messages for controls.
  • validate() - a function which invokes validators for controls and the form and returns a promise of boolean value. If the promise is resolved with true then the form is valid.
  • apply(target) - this function is generated by AngularJsEOperationFormGenerator. It invokes the target passing model data as arguments in the order in which they are defined in the ECore model. If target is an object with <operation name> function, then that function is invoked, otherwise target assumed to be a function to be invoked.
  • validateAndApply(target) - this function is generated by AngularJsEOperationFormGenerator. It invokes validate(). If validation is successful, then it invokes apply(target). validateAndApply returns a promise resolved with return value of apply() or rejected with { validationFailed: true[, validationResults: ...] } if client side or server side validation failed, or { targetInvocationError: <target rejection reason> }. This function expects target function to return a promise, which is the case for JavaScript API functions generated for model objects' EOperations.

KnockoutJS model

KnockoutJS generators have generateModel() method which returns JavaScript constructor function definition with the following entries:

  • data - a property with getter and setter. The setter copies entries from the value to observables, the getter returns an object with its properties having getters and setters bound to observabled.
  • observableData - an object with observables linked to data entries. Form controls are bound to this object's entries.
  • clear() - this function sets model data undefined/default values and empties validation results.
  • validationResults - a property similar to data setting and returning validation results.
  • observableValidationResults - an object with observables to which form error messages are bound.
  • validate() - a function which invokes validators for controls and the form and returns a promise of boolean value. If the promise is resolved with true then the form is valid.
  • apply(applyTarget)
    • For EOperations it invokes applyTarget passing model data as arguments in the order in which they are defined in the ECore model. If applyTarget is an object with <operation name> function, then that function is invoked, otherwise applyTarget assumed to be a function to be invoked.
    • For EClasses it sets applyTarget properties to data entries, then invokes applyTarget.$store function, and returns its return value.
  • validateAndApply(applyTarget) - Invokes validate(). If validation is successful, then it invokes apply(). validateAndApply returns a promise resolved with return value of apply() or rejected with { validationFailed: true[, validationResults: ...] } if client side or server side validation failed, or { targetInvocationError: <target rejection reason> }. This function expects applyTarget function to return a promise, which is the case for JavaScript API functions generated for model objects' EOperations and $store function.
  • loadModel(source) is generated for EClasses only. It loads data from source to observables and can be invoked to refresh views.

Case study

AngularJS

Nasdanika WebTest Hub user management application consists of a table listing existing users, Add button to create users, and a dialog for creating/updating users. WebTest Hub User Management

The create/update dialog has a different title depending on whether it shown to create or update a user. In update mode login text input is disabled. If authentication is "Password" then "Password" and "Password confirmation" fields are displayed and are required in create mode.

Create user dialog Update user dialog

The dialog model validates that a login for a new user does not already exists and that password and password confirmation match in the authentication mode is "Password".

In this application the dialog and the model are generated from a createOrUpdateUser() EOperation metadata and annotations: createOrUpdateUser.

The application template is generated by the usersApp() method, which uses AngularJsEOperationFormGenerator (line 725) to generate a form (line 732) and a model, which is passed to the controller generator (line 745).

When the controller is loaded data into the table is retrieved from userList hub property generated from userList operation with getter annotation:

userList

After that the table is updated with data returned by deleteUser() and createOrUpdateUser() functions.

In the controller template the form model is injected into $scope at line 41.

The controller's createOrUpdateUser() function (line 57) is invoked on form submission.

$scope.createOrUpdateUser = function() {
	showFormOverlay();
					
	$scope.userModel.validateAndApply(hub).then(
		function(userList) {
			$scope.$apply(function($scope) {
				$scope.userList = userList;
				hideFormOverlay();
				jQuery("#createUpdateUserFormModal").modal('hide');								
			});						
		},
		function(reason) {
			if (reason.validationFailed) {
				$scope.$apply();
			} else {
				console.trace(reason);
				alert(reason.targetInvocationError ? reason.targetInvocationError : reason);						
			}					
			hideFormOverlay();
		}).done();
}

The function displays an overlay div over the form to provide a visual cue that there is a server interaction in progress and to prevent user interaction with the dialog. Then it invokes generated validateAndApply() function with hub argument. validateAndApply returns a promise. If the promise is resolved, $scope.userList is set to the promise fulfillment value. If the promise is rejected due to validation problems, then $scope.$apply() is invoked to display error messages. Otherwise, an alert is displayed.

validateAndApply() invokes (also generated) hub.createOrUpdateUser(), which in turn invokes Hub.createOrUpdateUser() operation on the server.

The pattern

  • Create a route method to generate the application template (usersApp).
  • Create data retrieval operation(s), if required. If parameterless, annotate them as getters. In the hub user management example the data retrieval operation is used because isAdmin and authentication are computed properties. Otherwise hub.users reference could have been used.
  • Create data modification operations. These operations may return the same data as data retrieval operation(s).
  • Create a controller template. Controller methods shall delegate validation and data submission to the generated functions.
  • Add forms generated from the operations into the application template, and models into the controller.
Clone this wiki locally