Development beyond CRUD - cuba-platform/sample-workshop GitHub Wiki

6.1 Integration with IDE and Project Structure

Let’s have a look how our project looks from inside. Keep your application up and running and follow the steps:

  1. Launch IntelliJ IDEA. The IDE should be up and running to enable integration with the CUBA Studio. If you don’t have the CUBA Plugin installed, please install it, cause it is used as a communication bridge between the Studio and IDE

  2. Go to the Project properties section in Studio.

  3. Click the IDE button. The Studio will generate project files and open the project in the IDE

IDE button in Studio

  1. Move to the IDE and press Alt+1 to see the project structure. By default, any project consists of 4 modules: global, core, web, gui.
  • global - data model classes
  • core - middle tier services
  • gui - common component interfaces; screens and components for both Web and Desktop clients and
  • web - screens and components for Web client

Project modules

6.2 Customization of Existing Screens and Hot deploy

In this chapter we will polish our Orders browser and editor by adding logic into the controller and changing the user interface.

6.2.1 Initialization of a New Record in Editor

Let’s solve the first problem with an empty status of the newly creating orders. A new order should be created with the NEW order status pre-set.

  1. Go to the GENERIC UI section of the navigation panel in the CUBA Studio

  2. Select the order-edit.xml screen

  3. Click the IDE button on top of the section. Screen descriptor appears in your IDE. You can make any changes right from the source code because The Studio and an IDE has two ways synchronisation

  4. Hold Ctrl button and click on OrderEdit in class attribute of the XML descriptor to navigate to its implementation

order-edit.xml

  1. Override method initNewItem and set status OrderStatus.NEW to the passed order:
  public class OrderEdit extends AbstractEditor<Order> {
      @Override
      protected void initNewItem(Order item) {
          super.initNewItem(item);
          item.setStatus(OrderStatus.NEW);
      }
  }
  1. We haven’t stopped our application and it is still up and running in the browser. Open/Reopen Application — Orders screen

  2. Click Create

  3. We see our changes, although we haven’t restarted the server. The CUBA Studio automatically detects and the hot-deploys changes, except for the data model, which saves a lot of time while UI development and business logic development

Order editor

6.2.2 Adding Standard Excel Action to the Orders Browser

The standard screens contain Create, Edit, and Remove actions by default. Let’s add an action to export the order list to Excel, which is also a standard action you can use out of the box. You can follow the link to learn more about standard actions.

  1. Open the order-browse.xml screen in the Studio

  2. Select table component, go to properties panel

  3. Click the edit button in the actions property

  4. Add a new action row to the list

  5. Specify id as excel for this action

  6. Click OK

Adding Standard Excel Action to the Orders Browser

  1. Add a new button to the button panel (drag and drop it from the components palette into the hierarchy of components)

  2. Select ordersTable.excel action for the button using properties panel

  3. Save the screen by clicking OK in the top-right corner

Adding Standard Excel Action to the Orders Browser

  1. Let’s use the magic of hot deploy once again. Open/Reopen the Orders screen

  2. Click Excel to export your orders to an XSL file

6.2.3 Adding Custom Behaviour

Our mechanics are yelling that it’s too annoying to go to the Order editor every time they want to change the order status. They would like to click a button and set the corresponding status from the browser.

  1. Open the order-browse.xml screen in the Studio

  2. Add a new button to the button panel (drag and drop it into the hierarchy of components)

  3. Set the following properties for the button:

    • id: btnNewStatus
    • caption: Set as New
  4. Click the [>>] button on the right side of the invoke field

Adding Custom Behaviour

  1. The Studio will generate a method, that will be called on button click. Press Ctrl+I to save the changes and open the screen controller in your IDE

  2. CUBA extends the standard spring injection mechanism with the ability to inject CUBA related infrastructure and UI components. We will need to inject the datasource from our screen:

   @Inject
   private CollectionDatasource<Order, UUID> ordersDs; 
  1. Let’s implement the onBtnNewStatusClick method, that was created by the Studio:
   public void onBtnNewStatusClick(Component source) {
       Order selectedItem = ordersDs.getItem();
       if (selectedItem != null) {
           selectedItem.setStatus(OrderStatus.NEW);
           ordersDs.commit();
       }
   }
  1. Open/Reopen the Orders screen to see the mystery of hot deploy. Now our mechanics can work with maximum productivity

6.3 Business Logic Development

As the next step, let’s add business logic to our system to calculate the order price when we save it in the edit screen. The amount will be based on the spare parts price and time spent by the mechanic.

  1. To use mechanic hourly rate and prices for parts, we’ll need to load this attribute, so we need to add it to the order-view. In order to change the view switch to the Studio, open the Entities section of the Studio navigation panel, select the order-view item and click Edit

  2. Include the hourlyRate and price for parts attributes to the view

  3. Click OK to save the view. From now we will have access to the hourlyRate and price attributes from the orders screens (editor and browser)

Business Logic Development

  1. Go to the MIDDLEWARE section in the Studio

  2. Click New - Service

  3. Change the last part of Interface name to OrderService

  4. Click OK. The interface will be located in the global module, its implementation - in the core module. The service will be available for invocation for all clients that are connected to the middle tier of our application (web-client, portal, mobile clients or integration with third-party applications)

  5. Select the OrderService item in the navigation panel

  6. Click IDE

Adding service

  1. In the Intellij IDEA, we’ll see the service interface, let’s add the amount calculation method to it:
   BigDecimal calculateAmount(Order order);
  1. Go to OrderServiceBean using the green navigation icon at the left

OrderServiceBean

  1. Implement the caclulateAmount method
@Service(OrderService.NAME)
   public class OrderServiceBean implements OrderService {

       @Override
       public BigDecimal calculateAmount(Order order) {
           BigDecimal amount = new BigDecimal(0);
           if (order.getHoursSpent() != null) {
               amount = amount.add(new BigDecimal(order.getHoursSpent())
                       .multiply(order.getMechanic().getHourlyRate()));
           }
           if (order.getParts() != null) {
               for (SparePart part : order.getParts()) {
                   amount = amount.add(part.getPrice());
               }
           }
           return amount;
       }
   }
  1. Go back to the Studio

  2. Select the order-edit.xml screen in the GENERIC UI section of the navigation panel

  3. Click IDE

  4. Go to the screen controller (OrderEdit class)

  5. Add OrderService field to class and annotate it with @Inject annotation

   @Inject
   private OrderService orderService;
  1. Override the preCommit() method and invoke the calculation method of OrderService
   @Override
   protected boolean preCommit() {
       Order order = getItem();
       order.setAmount(orderService.calculateAmount(order));
       return super.preCommit();
   }
  1. Restart your application using the Run — Restart application action from the Studio

  2. Open Application — Orders from the menu

  3. Open editor screen for any order

  4. Set Hours Spent

  5. Click OK to save order

  6. We can see a newly calculated value of the amount in the table

Order amount calculated

Now we can automatically calculate the price based on spare parts used and time elapsed as it was mentioned in our functional specification!

6.4 REST API

Often enterprise systems have more than one client. In our example, we have web client for our back office staff. In the future, we could come with an idea of having a portal and/or a mobile application for our customers. For this purpose, CUBA provides developers with the generic REST API.

REST API is enabled by default in the web client, thus we can use it right now for a simple web page that will show the list of recent orders for a logged in user.

Our page will consist of login form and list of recent orders.

  1. Create orders.html and orders.js in web module: /modules/web/web/VAADIN/orders.html

  2. Add jquery-3.1.1.min.js and bootstrap.min.css prerequisites.

VAADIN folder

  1. Let’s implement our page:

Login screen

Our HTML form will consist of 2 fields: login and password. We will send login request to REST API on Submit button click:

   <!DOCTYPE html>
   <html lang="en">
       <head>
           <script type="text/javascript" src="./jquery-3.1.1.min.js"></script>
           <script type="text/javascript" src="orders.js"></script>
           <link rel="stylesheet" href="bootstrap.min.css"/>
       </head>
       <body>
           <div style="width: 300px; margin: auto;">
               <h1>Bike Workshop</h1>

               <div id="loggedInStatus" style="display: none" class="alert alert-success">
                   Logged in successfully
               </div>
               <div id="loginForm">
                   <div class="form-group">
                       <label for="loginField">Login:</label>
                       <input type="text" class="form-control" id="loginField">
                   </div>
                   <div class="form-group">
                       <label for="passwordField">Password:</label>
                       <input type="password" class="form-control" id="passwordField">
                   </div>
                   <button type="submit" class="btn btn-default" onclick="login()">Submit</button>
               </div>

               <div id="recentOrders" style="display: none">
                   <h2>Orders</h2>
                   <ul id="ordersList"></ul>
               </div>
           </div>
       </body>
   </html>

We will send login request according to OAuth protocol:

var oauthToken = null;

   function login() {
       var userLogin = $('#loginField').val();
       var userPassword = $('#passwordField').val();
       console.log ( '#someButton was clicked' );
       $.post({
           url: 'http://localhost:8080/app/rest/v2/oauth/token',
           headers: {
               'Authorization': 'Basic Y2xpZW50OnNlY3JldA==',
               'Content-Type': 'application/x-www-form-urlencoded'
           },
           dataType: 'json',
           data: {grant_type: 'password', username: userLogin, password: userPassword},
           success: function (data) {
               oauthToken = data.access_token;
               $('#loggedInStatus').show();
               $('#loginForm').hide();
               loadRecentOrders();
           }
       })
   }

If we successfully logged in, then we load the list of recent orders:

   function loadRecentOrders() {
       $.get({
           url: 'http://localhost:8080/app/rest/v2/entities/workshop$Order?view=_local',
           headers: {
               'Authorization': 'Bearer ' + oauthToken,
               'Content-Type': 'application/x-www-form-urlencoded'
           },
           success: function (data) {
               $('#recentOrders').show();
               $.each(data, function (i, order) {
                   $('#ordersList').append("<li>" + order.description + "</li>");
               });
           }
       });
   }

And if we try to log in using default login: admin and password: admin we will see our orders list:

Login successful

That was the last requirement from the functional specification. Now the system is ready for production!

finish →

⚠️ **GitHub.com Fallback** ⚠️