Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Scout/Tutorial/3.7/Minicrm Step-by-Step

< Scout‎ | Tutorial‎ | 3.7
Revision as of 10:03, 7 October 2010 by Asc.bsiag.com (Talk | contribs) (Setup a new EclipseScout project: Move Setup a New Eclipse Scout Project to a new page.)

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

Prerequisites

Know the Eclipse Scout client component model

Here you can have a look at the EclipseScout Client Component Model: Main Concepts

Installation / Set Up

You need to get the Scout SDK and a demo database to work through this tutorial. The Scout documentation has been moved to https://eclipsescout.github.io/.

Setup a New Eclipse Scout Project

Switch to the Scout Perspective and create a new Scout Project. The Scout documentation has been moved to https://eclipsescout.github.io/.

Setup a SqlService

Create a new AbstractDerbySqlService

Go to the server node and open the tree, drill down until Sql Services below Common Services and right click in order to open the wizard to create a new SQL service.
SqlServiceWizard.jpg


Name your new service DerbySqlService and choose AbstractDerbySqlService from the combobox Super Type then click finish.
Newsqlservice.jpg


If the Super Type AbstractDerbySqlService doesn't appear within the combobox, you need to organize your server project's plugin.xml. Go to the Java Perspective, double click on the server project, open the plugin.xml and in the box Required Plug-Ins add the plug-in org.eclipse.scout.rt.jdbc.derby. If the Super Type still does not appear, make sure that the derby.jar is in the lib folder of the Java installation. You can get this library directly from Apache Derby.
Serverplugin.jpg


Change default configuration settings

Adjust the default settings in your DerbySqlService by editing the Jdbc Mapping Name, this should point to the folder where you unzipped the DerbyDb to. As username/password use minicrm/minicrm.
Derbydb.jpg

Your first page

Add an Outline first

Since a page can only be contained within an Outline, you need to define the Outline first. Therefore open the client node in Eclipse Scout and expand the tree until you get to the Desktop node. Do a right click on that node and choose New Outline....
Newoutline.jpg


As name choose Standard and make sure the checkbox Add to Desktop is ticked. Then click finish.
Newoutlinewizard.jpg


When you expand the Desktop furthermore now, you should see the StandardOutline below it.
Standardoutline.jpg

Add a page to the Outline

Now you can add a new page to your StandardOutline. Right click on the Child Pages node of your StandardOutline and then choose New Page....
Newpage.jpg


From the dropdown list choose AbstractPageWithTable then click next.
Newtablepage.jpg


Now enter the name for the page which in our case is Company. If this label text is missing, there will appear a contextmenu New translated text... in the Name field. If this is the case, enter there the data for the new translated text and click ok.
Newtext.jpg


Companytext.jpg


This will create an entry in the files Texts.java and the to the language corresponding properties file (e.g. Texts.properties for the default language, Texts_de.properties for German, and so on). If you expand the shared node in your Eclipse Scout project, activate Texts and click on open nls editor in the Scout Object properties view, you can manage all your application's texts.
Nls.jpg


Now you can click finish, this will create your CompanyTablePage.
Companytablepage.jpg


When you expand now the Child Pages folder below your StandardOutline, you'll find your CompanyTablePage. When you expand the node Table, you'll find a folder Menus and Columns. Below Columns we will now add the columns that are needed to display the company data.
Finishedcompanytablepage.jpg

Manage the table of your page

Now we want to add columns to the page. For each database field we want to have in a page's table row we need to add a column. Therefore we add one for the primary key (CompanyNrColumn), one for a company's shortname (ShortNameColumn) and one for a company's name (NameColumn). The contextmenu for creating a new table column you find on the node Columns below the page's table.
Newcolumn.jpg


First, you have to choose a template for your column, depending of the datatype the data displayed in that column will have. Therefore for the first column choose Long Column, and for the other two choose String Column.
Columntemplate.jpg


When you have to choose a name for your column you can leave that empty for the CompanyNrColumn and just enter the Type Name. For the other two columns you may want to choose a name as this name will be later displayed in the table header.
Newcolumnname.jpg

Change the default configuration settings

If you now restart your client, you will see that the table layout is not yet optimal. The CompanyNrColumn for instance shouldn't be displayed at all since it holds the primary key which is not of interest for users. The width of the columns isn't optimal as well. Therefore you need to change the following settings: On the table, there is a property Auto resize columns while on each one of the columns, there is a property Width. That means that you either specify a higher width for all of your columns or you just tell your table to auto resize all columns which divides the available width equally for all columns.
Tableprops.jpg


Columnprops.jpg

. As a next thing, you may want to make the CompanyNrColumn disappear. You can do that by unticking the property Visible on the column. To make it completely invisible, you will have to untick Displayable as well. Only this property will prevent the CompanyNrColumn from appearing in the Organize columns dialog you can call by right clicking on a table header as well. On this dialog the user can change the order of a table's columns or hide different column.
Organize.jpg

Create an Outline Service

Now we want to collect the data to display in the CompanyTablePage. Therefore we need to create an OutlineService as we use it to display data in an Outline. Go to the server node in your Eclipse Scout project, expand it, go to Outline Services, right click and choose New Outline Service....
Newoutlineservice.jpg


As name choose StandardOutlineService since this OutlineService goes together with your StandardOutline (hence all the TablePages that will in your StandardOutline in the end will call a service operation in the StandardOutlineService at the end).
Standardoutlineservice.jpg


If you click next, you will see that there will be a Service Proxy Registration in the client plugin of your project. This will support for calling this service's operation from within the client as well.
Servicereg.jpg


Click on finish in order to create the new service. If you expand now the node Outline Services you will see a new node StandardOutlineService. Right click on it and choose New Service operation.
Newserviceop.jpg


Choose getCompanyTableData as a name, and as return name enter a double object array Object[][] and click finish.
Getcompanytabledata.jpg


It is a good Scout practice to name service operations which collect data to be displayed in a TablePage in a way corresponding to get*TableData.
Now open the implementation of getCompanyTableData either by expanding the node StandardOutlineService and double clicking on getCompanyTableData or by opening the class StandardOutlineService directly (CTRL-Shift-T) and scrolling down to getCompanyTableData. This is the place where we have to add an SQL statement which collects the needed data.
The DB you've downloaded contains the following tables:

Company.jpg

Person.jpg

As described in the Scout Overview, Scout provides for a base service SQL. In order to access the DB and select data therefore you need to call SQL.select(...). When writing your SELECT statement pay attention to specify the db columns in exactly the same order as the order of your table columns. The second parameter, the Bind Bases, are needed to add a constraint to your SELECT statement. For the moment we leave that empty.
Sql.jpg

Load the data for your Page

The last thing to do is now to add the call to the getCompanyTableData service operation, for this we need to overwrite the method AbstractPageWithTable.execLoadTableData(SearchFilter). Go back to your CompanyTablePage, in the lower part of the properties view exec click on Exec Load Table Data in order to create this method in your tablepage. When it asks you wether to create the create the method, click on Yes.
Execload.jpg

Now you can use the convenience accessor class SERVICES to lookup your IStandardOutlineService class and then call the method getCompanyTableData to load the data in your TablePage. Now restart your application and enjoy :-)
Services.jpg

Add a search form to the table page

Now since we've created a complete TablePage and implemented the service call to get the table data we can easily create a SearchForm for the CompanyTablePage using the Scout SDK (otherwise you would have to do that by hand). Go to the CompanyTablePage, right click and choose Create Search Form. The created Search Form will contain a field for each column of the table based on who it is created.
Newsearchform.jpg

.For the name enter Company. When you click next, you will see that a CompanySearchFormData class will be created in the shared plugin. This is the data transfer object corresponding to the new form CompanySearchForm which will be holding all values entered in the CompanySearchForm.
Newsearchformwizard.jpg

The search form that is generated out of our table page is already finished. It is also already set as SearchForm of the CompaniesTablePage (getConfiguredSearchForm). If a search is started, a SearchFilter is generated out of the SearchForm and is available in the execLoadTableData method. The Search filter contains the FormData of the SearchForm.

Adapt the outline service operation getCompanyTableData

In order to constrain the SQL statement for the Company Outline we have to adapt the service operation getCompanyTableData and add a parameter searchFilter of the type CompanySearchFormData to the method. Do that for both the service interface and service implementation (IStandardOutlineService and StandardOutlineService).
Now simply change the SQL statement in the following way:
public Object[][] getCompanyTableData(CompanySearchFormData filter) throws ProcessingException {
  StringBuilder statement = new StringBuilder();
  statement.append(
    "SELECT COMPANY_NR, SHORT_NAME, NAME " +
    "FROM   COMPANY " +
    "WHERE  1=1 ");
  if(!StringUtility.isNullOrEmpty(filter.getShortname().getValue())){
    statement.append("AND UPPER(SHORT_NAME) LIKE UPPER(:shortname || '%')");
  }
  if(!StringUtility.isNullOrEmpty(filter.getName().getValue())){
    statement.append("AND UPPER(NAME) LIKE UPPER(:name || '%')");
  }
  return SQL.select(statement.toString(), filter);
}

You can use :<name of field in formdata> to directly access the FormData object and the values that are held in its inner classes (corresponding to the field on the Form associated to the FormData). You don't need to surround the value by ' as it would have been necessary in a common SQL statement as this is already done by Scout itself.

Now we need to forward the CompanySearchFormData to the server. Therefore go to the method execLoadTableData in the CompaniesTablePage and add a new parameter to the service call according to its interface definition. The needed CompanySearchFormData is held in the parameter filter of type SearchFilter which is passed to the method execLoadTableData.

return SERVICES.getService(IStandardOutlineService.class).getCompanyTableData((CompanySearchFormData)filter.getFormData());

Edit companies: Build a form

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...
Newform.jpg


As name for the form choose Company. As you can see on the New Form Wizard a form ID called CompanyNr will automatically be created. This ID is later needed to pass the primary key of the company you want to display the data for in the form.
Newformwizard.jpg


When you click next, you will see that there are also permissions and a process service for this form created, both we will discuss later.

InnerFormClasses.jpg


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.
Newformfield.jpg

On the New Form Field wizard you first have to choose a template for the field. First we want to add a Field for the company's name, therefor choose String Field. Click next and enter the name of the field (Name) and then click finish.
Fieldtemplates.jpg

Now proceed in the same way and add a field for the Short Name and a Ok Button (template: OK Button) and a Cancel Button (template: Cancel Button). For the two buttons you can leave the names empty since they are already provided by their templates. Buttonnames usually have an ampersand. This is used for the keyboard shortcut.

Modify the form's ProcessService

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

public class CompanyProcessService extends AbstractService implements ICompanyProcessService{
 
  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 ProcessService contains four methods. If you check from where they are called, you will notice that the callers are a two FormHandlers: A NewHandler and a ModifyHandler, two inner classes of CompanyForm of type AbstractFormHandler. You can see them as well in the Scout project explorer: If you open the node CompanyForm, there is a node Handlers.

FormHandlers are used to start a form in a different 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.

A FormHandler's methods are 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
  2. execPostLoad: After the form was loaded, changes to fields inside this method results in marking the form as Save needed / Changed
  3. execCheckFields: Executed before execValidate but after AbstractForm.execCheckFields(). Returning false cancels the processing.
  4. execValidate: Executed after AbstractForm.execValidate().
  5. execStore: After this method call, the form is in the state Saved / Unchanged. Executed before AbstractForm.execStored().
  6. execDiscard
  7. execFinally

Please consult the javadoc of these methods to lookup what they are used for. If you check which methods of the CompanyProcessService class are called from within the generated FormHandlers inside the CompanyForm, you will see that the ModifyHandler calls the method load to load the data in the form and store when the form is closed and its (modified) data is to be stored (e.g. the Ok-Button has been pressed). While the NewHandler calls the method prepareCreate before showing the form and create when the form is stored in order to insert a new Company entry.
Furthermore you can see here that the beforehand generated Permissions are now automatically checked here. These checks have been added in the same manner on the client side as well in order to guarantee that a form is disabled when it is opened by someone that doesn't have the permission to update a company entry.

Load the data for the form

As a first thing you may want to enable the editing of an existing company entry. Therefore go to the load method and select the data to be displayed in the form using the SQL method SQL.selectInto. In the INTO part of your SQL statement you can specify the the corresponding form data fields by typing :<name of formdata field> (as before when you added constraints to your SELECT statement). You'll notice that when you start typing : and then press CTRL-Space, there is a Content assistance which provides for the available form data fields. If you find that there is a form data field missing, maybe your FormData is not up to date. You can update a FormData anytime by calling Update Form Data by right clicking on the corresponding Form. In the where-part of your statement you have to specify the company's primary key. This is contained in a field companyNr, as this was the name of the Form ID we specified before and is now contained in the FormData object as well.
Updateformdata.jpg

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

From Form to FormData to 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{
      ICompanyProcessService service = SERVICES.getService(ICompanyProcessService.class);
      CompanyFormData formData = new CompanyFormData();
      exportFormData(formData);
      formData = service.load(formData);
      importFormData(formData);
      setEnabledPermission(new UpdateCompanyPermission());
    }

Start the Form

Now 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 twice, click on the node Menu, right click and choose New Menu....
Newmenu.jpg

Enter a name for your form, and in the field Form to start specify your CompanyForm and click finish.
Editcompanymenu.jpg

Update the company with the new values

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

      @Override
      public void 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;
      }

Pass the primary key and reload page

If you open the generated EditCompanyMenu you see that there has been generated code inside a method execAction. This code actually opens the form. What is missing here is the forwarding of the companyNr. The companyNr is inside the table in the CompanyNrColumn in the row the user currently selected. To get that value simply rewrite the code like this:

      @Override
      public void execAction()  throws ProcessingException {
        CompanyForm form = new CompanyForm();
        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 the see the changes instantly.


Back to the top