Skip to main content

Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Scout/Tutorial/5.0/Minicrm/Add a form to edit the data

< Scout‎ | Tutorial‎ | 5.0‎ | Minicrm

The Scout documentation has been moved to https://eclipsescout.github.io/.

Note.png
Scout Tutorial
This page belongs to the Minicrm Step-by-Step Tutorial. It explains how to add a form to a table page such that you can edit the data on the database. You need to have a table page in your application in order to continue.


We need to create a form containing various fields, we need to have a process service that allows us to select, insert, and update data on the database, and we need a context menu to call the form, possibly passing along an id (the primary key) to the form if we want to edit a particular row.

What are we talking about?

The following diagram illustrates our goal: We already have our CompanyTablePage. We'll want two menus to call our CompanyForm. When editing a company, we'll start a ModifyHandler that will select data from the database and update it when we're done; when creating a new company we'll start a NewHandler that will insert it when we're done.

As you can see the framework provides for a place to run code when preparing for a new company. This is where you could set some default values to the form.

Scout Form Details.png

Create a form

We now build a form to add and edit companies. Expand the node client of your Scout project, scroll down to the node Forms, right click on it and select New Form...

Scout.3.9.minicrm.add form.menu.png

As name for the form choose Company. Check the Create Form Id checkbox. This ID is later needed to pass the primary key of the company you want to display the data for in the form.

You might also want to set a "Sub Package". To keep all your form together you might want to choose something like ui.desktop.form. Another possibility is to group your forms and searchforms by entity (CompanyForm and CompanySearchFrom in the sub package entities.company).

Scout.3.9.minicrm.add form.wizard.png

When you click Next, you'll see permissions and a process service will also be created for this form. We'll discuss these later.

Scout.3.9.minicrm.add form.class locations.png

Click Finish to create the form.

When you now expand the tree below your CompanyForm you can see a MainBox. This is the mandatory root element for every form. Inside this MainBox you can now add fields as you wish by right clicking on it and choosing New Form Field.

Scout.3.9.minicrm.add form.new field.menu.png

On the New Form Field wizard you need to choose a template for the field. First, we want to add a group box field that will hold the name and the short name field. The group box field is just needed for a nicer layout of the inner fields we will add below and does not need to have it's own label. To add this field, choose Group Box. Click Next and enter the class name of the field ("CompanyBox") and then click Finish.

Scout.4.0.minicrm.add form.new groupbox.wizard.png Scout.4.0.minicrm.add form.new groupbox.wizard2.png


Now, we add a field for the company's name to the company box. For this, right click on the group box field and choose New Form Field. Then, select String Field as the field type. Click Next and enter the name of the field ("Name") and then click Finish.

Scout.3.9.minicrm.add form.new field.wizard.png Scout.3.9.minicrm.add form.new field.wizard2.png

Now proceed in the same way and add a field for the Short Name.

You should already have an Ok button (OK Button template) and a Cancel button (Cancel Button template). The two buttons don't need a name since they'll get appropriate names from their template.

Note.png
Ampersand in Field Names
Button names usually contain an ampersand. This is used for a keyboard shortcut. Thus, &Ok means that Alt-O will activate the Ok button.


Modify the form's ProcessService

Go to the server node of your Scout project, open the node Services and expand it. Double click on the entry CompanyService in order to open it.

Scout.3.9.minicrm.add form.process service.png

public class CompanyService extends AbstractService implements ICompanyService {
 
  public CompanyFormData prepareCreate(CompanyFormData formData) throws ProcessingException{
    if(!ACCESS.check(new CreateCompanyPermission())){
      throw new VetoException(Texts.get("AuthorizationFailed"));
    }
    // TODO business logic here
    return formData;
  }
 
  public CompanyFormData create(CompanyFormData formData) throws ProcessingException{
    if(!ACCESS.check(new CreateCompanyPermission())){
      throw new VetoException(Texts.get("AuthorizationFailed"));
    }
    // TODO business logic here
    return formData;
  }
 
  public CompanyFormData load(CompanyFormData formData) throws ProcessingException{
    if(!ACCESS.check(new ReadCompanyPermission())){
      throw new VetoException(Texts.get("AuthorizationFailed"));
    }
    // TODO business logic here
    return formData;
  }
 
  public CompanyFormData store(CompanyFormData formData) throws ProcessingException{
    if(!ACCESS.check(new UpdateCompanyPermission())){
      throw new VetoException(Texts.get("AuthorizationFailed"));
    }
    // TODO business logic here
    return formData;
  }
}

As you can see, the generated process service contains four methods. If you check from where they are called (Ctrl+Shift+G), you will notice that the callers are two form handlers: a NewHandler and a ModifyHandler.

These two handlers are inner classes of the company form. You can see them in the Scout SDK as well: expand the CompanyForm node and you'll find the Handlers node right there.

Scout.3.9.minicrm.add form.handlers.png

Form handlers are used to start a form in a particular mode. For instance, if you want to use your form to add a new company, you want to bring it up empty. On the other hand, if you want to use it to edit an already existing company, you want to bring it up already containing the company's values.

Generally speaking, we'll use execLoad and execStore for every handler. Here's how our CompanyService methods are called:

Form Handler Handler Method Process Server Method
ModifyHandler execLoad load
execStore store
NewHandler execLoad prepareCreate
execStore create


A FormHandler's methods are executed in a specified order. With that, you can control a form's processing at different moments or with the form in different state:

  1. execLoad: Loads form state before it is made visible
  2. execPostLoad: After the form is loaded, changes to fields inside this method result in marking the form as changed and thus requiring a save.
  3. execCheckFields: Executed before execValidate but after AbstractForm.execCheckFields(). Returning false cancels processing.
  4. execValidate: Executed after AbstractForm.execValidate().
  5. execStore: After this method call, the form is in the unchanged state with no save required. Executed before AbstractForm.execStored().
  6. execDiscard
  7. execFinally

Please consult the javadoc of these methods for details.

Furthermore you can see in the source code generated that Permissions are checked. These checks exist on both the client and on the server side. On the client side, these are simple convenience checks to prevent the user from changing fields if they cannot be saved. We need similar checks on the server side to handle service calls from outside the client. The process service could potentially be used by an interface, for example.

Load the data for the form

To begin with, we want to edit an existing company.

Go to the load method of the CompanyService and select the data to be displayed using the SQL method SQL.selectInto.

SQL.selectInto("" +
	"SELECT SHORT_NAME, " +
	"       NAME " +
	"FROM   COMPANY " +
	"WHERE  COMPANY_NR = :companyNr " +
	"INTO   :shortName," +
	"       :name"
    , formData);

The INTO clause of your SQL statement will refer to form fields by prefixing them with a colon. These are the bind variables. A field called ShortName will be referred to as :shortName. Don't forget the naming conventions: downcase the first character.

If your form data is up to date, you can use content assistance after the colon: press CTRL-Space to complete on the fields. If some (or all) of the fields are missing, you need to update your form data (e.g. if you have disabled the auto update mechanism of Scout SDK).

Scout.3.9.minicrm.add form.update formdata menu.png

Note.png
Update Form Data
Every form comes with a form data object which will be used to communicate between client and server. Its code can be created automatically by using the context menu on the form itself. Right click on CompanyForm and pick Update Form Data. This is only required if the automatic form data update mechanism of Scout SDK has been disabled. If it is still active, the manual update is not necessary.


In the WHERE clause you need to specify the company's primary key. This is contained in the variable companyNr. Remember, when we created the form this id was created for us as well. You can find it in the Variables section of the CompanyForm.

From Form to FormData to Form

If you want to load data from a service, you need to export data from the form into the form data (the primary key in particular). Once the call returns you need to import the data from the form data back into the form.

Forms provide an import and export method, respectively. Therefore loading data into and storing them from a form into a form data is just a single method call. This has already been automatically added by Scout in the FormHandler methods:

@Override
public void execLoad() throws ProcessingException{
  ICompanyService service = SERVICES.getService(ICompanyService.class);
  CompanyFormData formData = new CompanyFormData();
  exportFormData(formData);
  formData = service.load(formData);
  importFormData(formData);
  setEnabledPermission(new UpdateCompanyPermission());
}

Add Edit ... Menu and Start the Form

The last thing to do is to actually call the form and to open it. In order to do that we need a menu on the CompanyTablePage. Go to your CompanyTablePage expand it, go to the table, click on the Menu node, right click and choose New Menu....

Scout.3.9.minicrm.add form.new menu.menu.png

Enter the name of the menu: Edit Company... (and add a new translated text). Specify CompanyForm as the Form to start, choose ModifyHandler as the Form handler, and click Finish.

Scout.3.9.minicrm.add form.new menu.wizard.png

Note.png
Use of Ellipsis
A menu name should end with an ellipsis (three dots: ...) if it opens a form. It should not end with an ellipsis if the menu performs an action immediately (without user interaction).


Scout.3.9.minicrm.add form.new menu.text entry.png

Update the company with the new values

Now we need to edit the store method in the CompanyService and add the sql statement for the update.

@Override
public CompanyFormData store(CompanyFormData formData)  throws ProcessingException { 
 if(!ACCESS.check(new UpdateCompanyPermission())){
    throw new VetoException(TEXTS.get("AuthorizationFailed"));
 }
 
 SQL.update(
     "UPDATE COMPANY SET" +
     "       SHORT_NAME = :shortName, " +
     "       NAME = :name " +
     "WHERE  COMPANY_NR = :companyNr", formData);
 
 return formData;
}

Make sure that you don't end up with code like this:

     "       NAME = :name" +
     "WHERE  COMPANY_NR = :companyNr", formData);

This would result in ...":nameWHERE"...

Pass the primary key and reload page

If you open the generated EditCompanyMenu you see that code for the execAction method has been generated for you. This code opens the form and calls the handler.


The companyNr is available in the CompanyNrColumn of the table. We just need to find the value in said column for the selected row. The auto generated code should look like this:

@Override
public void execAction()  throws ProcessingException {
  CompanyForm form = new CompanyForm();
  // Notice the following line
  form.setCompanyNr(getCompanyNrColumn().getSelectedValue());
  form.startModify();
  form.waitFor();
 
  if (form.isFormStored()){
    reloadPage();
  }
}

The method startModify starts the modify handler. The waitFor interrupts the execution at this point, until the form is closed again. With isFormStored we check wether the user has closed the form by pressing the OK-Button (form values are stored) or by pressing the Cancel-Button (form values are discarded). In the first case we reload the current table to see the changes instantly.

Warning2.png
Restart the Server
If you've been following along, you might think that changes are not being saved. In that case, consider whether you have restarted the server. If you change code on the client side, such as adding code to the store method, you might need to restart the server. If you're running the server in debug mode, Eclipse will attempt to hot swap the code for you, but it won't always work.


Scout.3.9.minicrm.add form.client swt.png

Add Row Action to open the Company Form with a double click on the Table Row

With the Edit Company ... menu a user may open the company form to edit the company fields. Intuitively, a user expects the company form to open by a double click on the corresponding row or by pressing the Enter key.

To implement such behaviour, Scout provides the method Exec Row Action on the table class inside of the table page. Just navigate to the table element as shown below and click on the green plus icon to add this method to the company table.

Scout.4.0.minicrm.add row action.png

As we already have implemented the code to open the company edit form for the selected row, we can simply reuse the code as follows.

@Override
protected void execRowAction(ITableRow row) throws ProcessingException {
  getMenu(EditCompanyMenu.class).doAction();
}

Can you create new companies?

Can you create a second menu to create new companies? Sure you can!

Here are some hints:

You need a NewCompanyMenu. It needs an execAction that runs the New Handler.

Note.png
Auto generated
If you choose NewHandler in the new menu dialog the following code should already be generated automatically
CompanyForm form = new CompanyForm();
form.startNew();
form.waitFor();
 
if (form.isFormStored()) {
  reloadPage();
}

The new menu should not appear on existing rows. Instead, it should appear when no row has been selected.

You already have a NewHandler so the only thing you need to do is write appropriate code for the create method of the CompanyService. There's currently nothing to do before the actual INSERT statement, thus we do not need to write anything for the prepareCreate method.

Unfortunately, the COMPANY_NR column in the sample DB has not been declared as an auto increment column (GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1)). For the purpose of this tutorial, we need to work around this within our application. (Yes, this is a terrible excuse...)

Here's what we do for create:

SQL.selectInto("" +
    "SELECT MAX(COMPANY_NR)+1 " +
    "FROM   COMPANY " +
    "INTO   :companyNr"
    , formData);
SQL.insert("" +
	"INSERT INTO COMPANY (COMPANY_NR, SHORT_NAME, NAME) " +
	"VALUES (:companyNr, :shortName, :name)"
	, formData);

Back to the top