Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
former_member186879
Active Participant
Note: Not all capabilities described in this blog post are available in SAP Web IDE Full-Stack. Some are still in development and will be available in future releases.

I’d like to help illustrate the new capabilities of the SAP Web IDE, full-stack version to build full stack applications for Cloud Foundry. Here’s how you would develop a simple book store application -- the app is simple, but it illustrates many of the powerful new features of SAP Web IDE.

Main Topics


In describing how to build the bookstore app, and I’ll cover the following technical topics:

  • Use SAP Web IDE to develop the entire application.

  • Use HANA CDS views to create the data model for the application.

  • Use HDBTableData to populate the tables with mock data.

  • Use XSJS XSOData to expose the data model as an OData service.

  • Use Node.js and SQL to add a new book to the database.

  • Use Fiori Master-Detail to create the UI for the application.

  • Use the Web IDE Layout Editor to enhance the UI of the application.

  • Test and debug the application in SAP Web IDE.

  • Deploy the finished application to Cloud Foundry.


The Application


The application we’ll build is designed for the owner of a book store who needs to manage the store’s inventory. The application should look something like this:



The app should do the following:

  • List all books by author.

  • In the author details field, show the books from that author. For each book, it should show the title, ISBN, price, and number of copies available in the store.

  • Add a new book to the store’s inventory.


The user should be able to add books and update existing book details, and to update the inventory when a book is sold.

You can download the entire application source-code from GitHub.

Step 1: Create MTA Project


We’ll build the app as an MTA project, which enables full-stack development.

Go to File --> New --> Project from Template and select the Multi-Target Application Project template.



  • Project Name: BookStore.


Click Finish.

Step 2: Add a HANA Database module to the MTA project


We’ll need a database for the app, so we’ll add an SAP HANA database module to the project.

Name the module booksdb, and complete the wizard.

Add CDS to the DB module by right-clicking the src folder located under the booksdb module and select New > CDS Artifact. Name it store and click Create.

Now that you have a CDS artifact, add the Author and Book.



Now let’s add some mock-data for testing purposes. Add the following files to the src folder of your HDB module:

File name: authors.hdbtabledata

File content:









{

"format_version": 1,

"imports":

[ {

"target_table" : "bookstore.db::store.Author",

"source_data" : { "data_type" : "CSV", "file_name" : "bookstore.db::authors.csv", "has_header" : true },

"import_settings" : { "import_columns" : ["authorId", "authorName", "numberOfBooks" ] },

"column_mappings" : {"authorId": 1, "authorName" : 2, "numberOfBooks" : 3 }

}

]

}


File name: books.hdbtabledata

File content:









{

"format_version": 1,

"imports":

[ {

"target_table": "bookstore.db::store.Book",

"source_data" : { "data_type" : "CSV", "file_name" : "bookstore.db::books.csv", "has_header" : true },

"import_settings" : { "import_columns" : [ "bookId", "bookName", "authorId","isbn","price","priceCurrency" ] },

"column_mappings" : { "bookId" : 1, "bookName" : 2, "authorId": 3, "isbn" : 4, "price": 5, "priceCurrency" : 6 }

}

]

}


File name: authors.csv

File content:









authorId, authorName, numberOfBooks

1, F. Scott Fitzgerald, 3

2, George Orwell, 2

3, J.D. Salinger, 1

4, Kurt Vonnegut, 4


File name: books.csv

File content:









bookId, bookName, authorId, isbn, price,priceCurrency

110, The Great Gatsby, 1, 0743273567, 11, USD

111, Tender Is the Night, 1, 1853260975, 9, USD

112, This Side of Paradise, 1, 0486289990, 5, USD

120, 1984, 2, 0451524934, 7, USD

121, Animal Farm, 2, 812911612X, 14, USD

130, The Catcher in the Rye, 3, 0316769487, 5, USD

140, Slaughterhouse-Five, 4, 0385333846, 10, USD

141, Cat's Cradle, 4, 038533348X, 10, USD

142, Breakfast of Champions, 4, 0385334206, 10, USD

143, The Sirens of Titan, 4, 0385333498, 9, USD


File name: authorSequence.hdbsequence

File content:









SEQUENCE "bookstore.db::authorseq"

INCREMENT BY 1

START WITH 1000

MINVALUE 1000

MAXVALUE 1999999999


File name: bookSequence.hdbsequence

File content:









SEQUENCE "bookstore.db::bookseq"

INCREMENT BY 1

START WITH 1000

MINVALUE 1000

MAXVALUE 1999999999


Save your work.

This is what the DB module should look like after you’re done:



Build the DB module.

Step 3: Add a Node.js module to the MTA project


We’ll need a node.js module to expose the data as an OData service.

Add a Node.js module to the project.



  • Module Name: booksjs.

  • Enable XSJS Support: Select this checkbox.


Complete the wizard.

Now add a new folder under the lib folder, and name it xsodata, and then create a new file in this folder as follows:

File name: service.xsodata

File content:









service {

"bookstore.db::store.Author" as "Author" navigates ("AuthorBooks" as "books");

"bookstore.db::store.Book" as "Book" create using "xsjs:bookCreateMethod.xsjslib::createBook";

 

"bookstore.db::BestSeller" as "BestSeller" keys generate local "objectId";

association "AuthorBooks" principal "Author"("authorId") multiplicity "1" dependent "Book"("authorId") multiplicity "*";

}


In order to support CRUD operations (Create, Read, Update and Delete), we need to implement an XSJS extension. We do this by creating a new file here: booksjs module > lib > xsjs.

File name: bookCreateMethod.xsjslib

File content:









"use strict";

 

// get the entry that was sent by the client

function getEntry(afterTableName, connection) {

var query = connection.prepareStatement("select * from \"" + afterTableName + "\"");

var queryResult = query.executeQuery();

query.close();

var record = null;

while (queryResult.next()) {

record = queryResult;

}

return record;

}

 

function findAuthorByName(authorName, connection) {

// TODO: change LIKE to equal

var query = connection.prepareStatement("SELECT * FROM \"bookstore.db::store.Author\" WHERE \"authorName\" LIKE" + "'%" + authorName +

"%'");

var queryResult = query.executeQuery();

query.close();

while (queryResult.next()) {

return queryResult;

}

 

return null;

}

 

function findAuthorById(authorId, connection) {

// TODO: change LIKE to equal

var query = connection.prepareStatement("SELECT * FROM \"bookstore.db::store.Author\" WHERE \"authorId\" =" + authorId);

var queryResult = query.executeQuery();

query.close();

while (queryResult.next()) {

return queryResult;

}

 

return null;

}

 

/**

* This function will execute a query for adding a new author to the database

*

* */

function insertNewAuthor(authorName, connection) {

var currentAuthorIdQuery = connection.prepareStatement("SELECT \"bookstore.db::authorseq\".NEXTVAL FROM DUMMY");

var currentAuthorIdQueryResult = currentAuthorIdQuery.executeQuery();

 

var nextAuthorId = 0;

 

while (currentAuthorIdQueryResult.next()) {

nextAuthorId = currentAuthorIdQueryResult.getInt(1);

break;

}

 

var query = connection.prepareStatement("insert into \"bookstore.db::store.Author\" values(?,?,?,?)");

query.setInt(1, nextAuthorId);

query.setString(2, authorName);

query.setInt(3, 0);

query.setInt(4, 0);

query.executeUpdate();

query.close();

 

return nextAuthorId;

}

 

/**

* This function will execute a query for adding a new book to the database

* bookId property will be generated via the database sequence

* after adding a new book we need to update the author number of books column

* */

function insertNewBook(bookName, authorId,isbn,price, connection) {

 

var currentBookIdQuery = connection.prepareStatement("SELECT \"bookstore.db::bookseq\".NEXTVAL FROM DUMMY");

var currentBookIdQueryResult = currentBookIdQuery.executeQuery();

 

var nextBookId = 0;

 

while (currentBookIdQueryResult.next()) {

nextBookId = currentBookIdQueryResult.getInt(1);

break;

}

 

var query = connection.prepareStatement("INSERT INTO \"bookstore.db::store.Book\" VALUES(?,?,?,?,?,?,?)");

 

query.setInt(1, nextBookId);

query.setInt(2, authorId);

query.setString(3, isbn);

query.setString(4, bookName);

query.setInt(5, price);

query.setString(6,"USD");

query.setString(7,"");

query.executeUpdate();

query.close();

 

incrementNumberOfBooksForAuthorWithId(authorId, connection);

}

 

function incrementNumberOfBooksForAuthorWithId(authorId, connection) {

// 1. Get the author by id from the database

 

var authorRecord = findAuthorById(authorId, connection);

if (authorRecord) {

var numberOfBooks = authorRecord.getInt(3);

numberOfBooks = numberOfBooks + 1;

var query = connection.prepareStatement("UPDATE \"bookstore.db::store.Author\" SET \"numberOfBooks\" = ? WHERE \"authorId\" = ?");

query.setInt(1, numberOfBooks);

query.setInt(2, authorId);

query.executeUpdate();

query.close();

}

}

 

function createBook(param) {

 

 

var entityToCreate = getEntry(param.afterTableName, param.connection);

if (!entityToCreate) {

throw "Invalid entry";

}

// TODO: Change to more generic solution

var isbn = entityToCreate.getString(3);

var bookName = entityToCreate.getString(4);

var price = entityToCreate.getString(5);

var authorName = entityToCreate.getString(7);

 

if (!authorName || !bookName || !isbn || !price) {

throw "Bad Request";

}

 

var authorId = -1;

 

var authorRecord = findAuthorByName(authorName, param.connection);

if (!authorRecord) {

authorId = insertNewAuthor(authorName, param.connection);

} else {

authorId = authorRecord.getInt(1);

}

 

insertNewBook(bookName, authorId, isbn,price, param.connection);

}


Run the Node.js application.

Open the Run Console to see the progress.



Click the application URL in the run console to validate it’s running correctly.

The Node application will open a new browser tab.

This is what the node project should look like:


Step 4: Add a UI module to the MTA project


Of course, we’ll want a user interface, so let’s add a SAP Fiori Master-Detail module to the project.



Name the module booksui.

Select the Node.js service as the OData source of the UI module.



In the Template Customization step, select the Author as the Object Collection and books as the Line Item Collection.



Click Finish to generate the UI module.

Run the UI application.

Step 5: Enable adding book records


Now let’s add the ability to add a new book to the database.

Open the Master.view.xml using the SAP Web IDE Layout editor.

Add a button to the master page footer, and give it the following properties:

  • Active Icon: sap-icon://add

  • Icon: sap-icon://add

  • Icon First: True


Add an event handler to the button Press event.









onCreateNewBook: function() {

this._oCreateBookDialog = sap.ui.xmlfragment("com.sap.demo.view.createBook", this);

this.getView().addDependent(this._oCreateBookDialog);

var oModel = new sap.ui.model.json.JSONModel({

bookName: "",

authorName: "",

isbn: "",

price: 0,

authorNamePlaceholder: "Enter the author name",

bookNamePlaceholder: "Enter the book name",

isbnPlaceholder: "ISBN",

pricePlaceholder: "Book Price (Numbers only)",

doneButtonActive: true

});

 

this._oCreateBookDialog.setModel(oModel);

this._oCreateBookDialog.open();

},

onCancelBookCreation: function(oEvent) {

this._oCreateBookDialog.close();

},

onCreateBook: function(oEvent) {

// get the JSON model

var oModel = oEvent.getSource().getModel();

 

var payload = {

"bookId": 0,

"authorId": 0,

"isbn" : oModel.oData.isbn,

"bookName": oModel.oData.bookName,

"authorName": oModel.oData.authorName,

"price" : oModel.oData.price

};

 

var self = this;

this.getView().getModel().create("/Book",payload,{

success: function() {

self.getView().getModel().refresh();

}

});

 

this._oCreateBookDialog.close();

}

});


Add a new fragment to the UI module by creating a new file here: storeui --> resources --> webapp --> view. Call the file createBook.fragment.xml.

File name: createBook.fragment.xml

File content:









<core:FragmentDefinition xmlns="sap.m" xmlns:core="sap.ui.core">

<Dialog title="New Book">

<beginButton>

<Button text="Cancel" press="onCancelBookCreation"/>

</beginButton>

<endButton>

<Button text="Done" type="Emphasized" press="onCreateBook" enabled="{/doneButtonActive}"/>

</endButton>

<content>

<VBox width="100%" direction="Column" displayInline="true">

<items>

<Input width="100%" value="{/bookName}" placeholder="{/bookNamePlaceholder}"/>

<Input width="100%" value="{/authorName}" placeholder="{/authorNamePlaceholder}"/>

<Input width="100%" value="{/isbn}" placeholder="{/isbnPlaceholder}"/>

<Input width="100%" value="{/price}" placeholder="{/pricePlaceholder}"/>

</items>

</VBox>

</content>

</Dialog>

</core:FragmentDefinition>


Run the UI application and test the Add function.

Step 6: Deploy to CF


Build the MTA project.

The result of the MTA build is an MTA archive file (mtar).

Right-click the .mtar file and deploy it to Cloud Foundry.

 

 

For more information please read this great blog post about SAP Web IDE Full-Stack.

 

If you have any questions, ask our community , check out our documentation ,or contact us.

Register here to get the latest news and updates from SAP Web IDE.
9 Comments