Building JSR-286 portlets using AngularJS and IBM Web
Transcription
Building JSR-286 portlets using AngularJS and IBM Web
Building JSR-286 portlets using AngularJS and IBM Web Experience Factory Overview This article illustrates how to build JSR-286 portlets using AngularJS framework and IBM Web Experience Factory (WEF) for WebSphere Portal Before going any further let us understand the drivers for integrating AngularJS with Web Experience Factory. AngularJS is a UI framework to build web applications. AngularJS relies on available REST / AJAX services to consume data from and build a user interface. Web Experience Factory is a rapid development tool for building web applications including portlets. WEF provides builders for end-to-end application development including both user interface and back-end services. Thus where AngularJS and Web Experience Factory complement each other are when you would want to use WEF’s capability to expose back-end services as REST services while at the same time use AngularJS framework to build the UI for the portlet. Following could be some of the drivers to integrate these technologies. Leverage WEF to expose complex existing services as REST services in an application. For e.g. expose JDBC / Web Service calls as REST service in Portal for AngularJS to consume to build the portlet UI. Leverage the XML/JSON data massaging builder/capabilities of WEF to format / aggregate data from multiple service calls in the desired output before exposing it via a single REST service. Use AngularJS to code the User Interface for a portlet that resides with other WEF portlets in a portal application. Note: If you have REST services exposed for your back-end application by some other tool and you simply want to consume these REST services to develop an AngularJS web application that you may want to expose as a portlet then consider IBM Script portlet as an option. (Refer to the article: Import Single Page Applications to a Script Portlet Instance) Target Audience This article is intended for AngularJS developers who are looking to build portlets and utilize their AngularJS knowledge while leveraging Web Experience Factory software automation. The article provides a high level overview of how to expose back-end services as REST services using WEF and build the portlet UI using AngularJS. This article is not intended to be an introduction / tutorial for either AngularJS or Web Experience Factory. Pre-requisites Web Experience Factory 8.5 or higher Script Application builder XML File Data Service Builder AngularJS Application Overview This article provides a high level overview of building a simple multi-page application / portlet using AngularJS and Web Experience Factory. The application illustrates the functionality of viewing / editing a Contact List in a portlet hosted on WebSphere Portal. As the portlet is rendered, it displays the contact list as shown in the figure below. The user can click on the contact name and can view/edit the contact information (see figure below) Building the Service Layer Create a Web Experience Factory project in the Eclipse based Designer. For the purpose of this article, assume that you have the contact list data in an xml file say contacts.xml in the following format. <contacts> <contact> <id>EMP981029</id> <name>Samantha Daryn</name> <extension>49404</extension> <location>Chicago</location> <title>Vice President</title> <photo>/images/SamanthaDaryn.jpg</photo> <email>daryn981029@xyzmail.com</email> </contact> <contact> <id>EMP981030</id> <name>Lucille Suarez</name> <extension>29184</extension> <location>Berlin</location> <title>Director</title> <photo>/images/LucilleSuarez.jpg</photo> <email>suarez981030@xyzemail.com</email> </contact> . . </contacts> Create a Model in Web Experience Factory (say ContactsProvider) and Import the xml file in the model using the Xml File Data Service builder. Make the “id” as the Key Field in the builder The XML File Data Service builder will generate CRUD service operations for the contact list. Note: XML File Data Service builder is meant only for prototyping and building quick samples. DO NOT use this builder in production applications. Building the UI Model Skeleton integrated with AngularJS Create a new Model in Web Experience Factory (say Contacts) and add the Script Application builder to the model. The Script Application builder imports the Service Provider model (ContactsProvider.model developer in previous step in this case) and generates REST Service URLs for each of the CRUD operations in the Service Provider. The builder also adds a JSON variable in the head of the generating HTML page which contains the REST Service URL that can be accessed by any JavaScript code. The Script Application builder requires a HTML page which would act as a template for the resulting application. For this Application create a HTML file (say main.html) with the following HTML code and import it in the Script Application builder by the name of contactsPage <html> <head> <title>Contacts</title> </head> <body> <div ng-app='myAngularApp' ng-controller='ContactsController'> <div ng-switch="viewState"> <div id="contactList" /> <div id="contactEdit" /> </div> </div> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular.mi n.js"></script> <span name="controller" /> </body> </html> The ng-app and ng-controller for AngularJS tag have been added to the div tag which encloses the entire portlet application. The div with id “contactList” and “contactEdit” will be superimposed with the list page and edit page respectively in the model. The ng-switch attribute for AngularJS will be used to switch between the contacts list view and contact edit view in further steps. Tip: Add the ng-app and ng-controller attributes for AngularJS to a div tag not to the html or body tag in your portlet html since those tags would get stripped out when you add the portlet on a Portal Page. Next create a JavaScript file (say controller.js) with the following code. var app = angular.module('myAngularApp', []); console.log('Entering Contoller'); app.controller('ContactsController', ['$scope', '$http', '$window', function($scope, $http, $window) { $scope.serviceRestUrls = $window.serviceRestUrls; } }); Note: The AngularJS controller adds the serviceRestUrls JSON object created by Script Application builder which contains the REST service URLs to its scope. Add the above created JavaScript file to the contactsPage using the Client JavaScript builder on the “controller” div tag. Add the Portlet Adapter builder to the model and deploy the project to WebSphere Portal. Add the portlet to a Portal Page. You will see a blank portlet as of now. The browser console log should show the “Entering Controller’ message. Creating the Contacts List View Add the following AngularJS code to controller.js file to fetch the list of contacts via a REST Query. $scope.getContacts = function() { // Set Visibility for the div $scope.viewState = 'list'; $http.get(serviceRestUrls.getContactsURL).then(function(resp) { console.log('Success', resp); // For JSON responses, resp.data contains the result $scope.contacts = resp.data.contacts.contact; }, function(err) { console.error('ERR', err); // err.status will contain the status code }); }; getContacts(); Note: The above code makes REST service call to get all the contacts in a JSON object named contacts. Also the code sets the viewState to list Create a separate html file (say list.html) which contains the html markup for the contact list section with the following html code. <div id="listView" ng-switch-when="list"> <div id="title"><h1>Contacts List</h1></div> <ul> <li name="DataContainer" ng-repeat='contact in contacts'> <table> <tr><td> <img ng-src='{{getImage(contact.photo)}}'></img> </td></tr> <tr><td align="center"> <span name="Name" class="outputData"> <a href="#" ng-click="getContactbyId(contact.id)"> <div>{{contact.name}}</div><div>{{contact.title}} </div> </a> </span> </td></tr> </table> </li> </ul> </div> Note: the above html section rendered only when viewState is set to list. The rest of the html code simply renders the JSON object contacts; on the page. The contact name is a link which calls getContactbyId(id) function that you will be adding when you build the Contact Edit View. Import the above created page in your UI model using Imported Page builder and name it as contactsListPage Insert the contactsListPage created in the previous step on the “contactList” div tag of the contactsListPage using the Inserted Page builder. This completes the creating the contacts list view. Next you will be creating the Contact Edit View section. Creating the Contact Edit View Create a separate html file (say edit.html) which contains the html markup for the contact edit section. <div ng-switch-when="detail"> <div id="title"><h1>Edit Contact Details</h1></div> <div align="center"> <form name="editContactForm" ng-submit="updateContact(editContactForm.$valid)"> <table> <tr> <td colspan="2" align="center"> <img ng-src='{{getImage(contactDetail.photo)}}'></img> </td> </tr> <tr> <td class="label">Name:</td> <td> <input name='name' ng-model='contactDetail.name' required> <p ng-show="editContactForm.name.$invalid" class="help"> Name is required. </p> </td> </tr> <tr> <td class="label">Title:</td> <td> <input name='title' ng-model='contactDetail.title'> </td> </tr> <tr> <td class="label">Extension:</td> <td> <input type='email' name='email' ng-model='contactDetail.extension> </td> </tr> <tr> <td class="label">Email:</td> <td> <input type='email' name='email' ng-model='contactDetail.email' > </td> </tr> <tr> <td class="label">Location:</td> <td> <input type='email' name='email' ng-model='contactDetail.location> </td> </tr> </table> <button ng-click="getContacts()">Cancel</button> <button type="submit" ng-disabled="editContactForm.$invalid">Save</button> </form> Note: The above html section is rendered only when viewState is set to detail. Pay attention to the AngularJS tags </div> in the html for form validation and form submission. </div> Import the above created page in your UI model using Imported Page builder and name it as contactEditPage. Insert the contactEditPage on the “contactEdit” div tag of the contactsListPage using Inserted Page builder. Next, add the following functions to controller.js file to get a single contact by id (getContactbyId) and to update an existing contact (updateContact). $scope.getContactbyId = function(id) { // Set Visibility for the div $scope.viewState = 'detail'; $http.get(serviceRestUrls.getContactURL, {params:{"id": id}}).then(function(resp) { // For JSON responses, resp.data contains the result $scope.contactDetail = resp.data.contact; }, function(err) {}); }; $scope.updateContact = function(valid) { $http({ method: 'POST', url: serviceRestUrls.updateContactURL, headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'}, params : $scope.contactDetail }); // get list of updated contacts $scope.getContacts(); }; This completes the steps required to build the Contacts List Application. About the Authors Abhishek Singh (abhisheks@us.ibm.com) is a Senior IT Architect with IBM Software Services for WebSphere. He has over 15 years of IT experience specializing in delivery of Portal and Mobile Application development with focus on SOA, Analytics, Content Management Systems, Responsive UI design and enterprise Java/ JEE applications. His experience includes designing and developing software products, service- oriented architecture, information systems, telecom applications, and client/server applications. Nischitha Rai (nyrai@us.ibm.com) is a Managing Consultant working with IBM Software Services for WebSphere. She has 9 years of experience working on several WebSphere Portal and JAVA/JEE application development engagements. Her expertise is in WebSphere Portal, WebSphere Experience Factory and Web Content Manager.