break out of the box - part 2

Post on 10-Jan-2017

174 Views

Category:

Internet

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Break out of The Box – Part 2Integrate existing Domino Data with modern websites

Karl-Henry MartinssonDemand Better Solutions, LLC

I AM... Swede living in Dallas, TX Developer at Demand Better Solutions, LLC Programming since 1982 Web developer since 1994 Notes/Domino developer since 1996 IBM Champion since 2014

http://www.demandbettersolutions.comhttp://www.texasswede.comhttp://blog.texasswede.com

My Story Old infrastructure

• Domino 8.5.2 server and Notes 8.5.2 Basic client Company unwilling to upgrade

• Memory requirements for Standard client• Citrix environment – limited memory available• Upper management thinks Notes/Domino is legacy

Management requests• New web applications with modern look-and-feel• Future mobile support (phone/tablet)

Bootstrap and jQuery to the Rescue!

Why Not Xpages? Infrastructure not supporting XPages (Domino 8.5) No XPages development skills Tight deadline – no time to learn Better control of versions – not having to wait for IBM Access to frameworks not yet in Domino (e.g. jQuery) Use time-saving jQuery/Bootstrap plugins

Why Integrate? Limited or no migration options Desire to modernize front-end Code re-use Take advantage of the power of Domino Use existing skillset (Domino, Lotusscript) Learn valuable new skills (Javascript/jQuery/Ajax) No one will know you are using a Domino backend!

How does it work?

.NSF

HTML and CSS jQuery (Javascript) Choice of framework

• Bootstrap• jQuery UI / jQuery Mobile• and many other...

Process Ajax from browser Generate and return JSON

• Lotusscript agents• XPages agents• ?ReadViewEntries• ExtLib REST Service control

Ajax call

JSON data

Server – IBM Domino Lotusscript agents NSF databases Existing business logic Can use existing classes and script libraries

Works on Domino version 5.0 and higher

Update to recent version highly recommended

Where does everything live? HTML pages, CSS files and Javascript

• Notes page element• Notes resources • CDN (.js and .css)• Server file system – in Data/Domino/HTML• Another web server

Lotusscript agents• .NSF database on Domino server

Development Tools Domino Designer Browser with Dev Tools

• Firefox with Firebug plugin• Internet Explorer Developer Tools (built-in)• Chrome Developer Tools (built-in)

Online resources• JSONLint • Stack Overflow• Google Search

What is jQuery? Fast, small and feature rich Javascript library Simplify using Javascript on a web page Available as CDN and as downloaded local files Three code streams:

• jQuery 1.x Supporting all browsers

• jQuery 2.x Removed support for IE 8 and earlier Smaller and faster

• jQuery 3.x Continuation of jQuery 2.x Some breaking functionality – e.g. in Ajax calls Only version to get new functionality

Free to use (MIT license)

What is Bootstrap? Developed by Twitter Open source front-end framework Mainly CSS, with some jQuery Cross-browser Responsive (out-of-the-box in version 3.0+) Supports themes, color schemes and plugins Available through CDN or as local files Many 3rd party resources and plugins Current version 3.3.7, version 4 is in early testing

What does Bootstrap look like? Attractive look out of the box Themes used to modify colors and controls Override with your own CSS for customization Many free and premium add-ons available

Bootstrap 2 using the theme “United”, with additional CSS for page curls.

Bootstrap 3 using the default theme.

How do you use jQuery and Bootstrap? Link to .js and .css files in HTML header

• Download files locally• Use CDN for easy linking – my preferred approach

Start writing your code!

Tips: Use // without protocol to use HTTP or HTTPS as needed. ViewPort meta tag helps with scaling of page. Never edit the Bootstrap CSS file (even if local), always override the CSS.

<meta name="viewport" content="width=device-width, initial-scale=1"><title>Demo 1 - MWLUG 2016</title><script src="//ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script><script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script><link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"><link href="demo1.css" rel="stylesheet">

Ajax – Asynchronous Javascript and XML Asynchronous – call is processed in background Result is passed back when done, then processed Data can be returned in different formats:

• XML – original format, rarely used anymore• JSON –now more common, easy to parse in Javascript• JSONP – JSON with Padding, when calling other servers• Can also return plain text or HTML

Using only a few lines of jQuery code Supported as data source directly in many plugins

Ajax call using jQuery

Arguments passed as JSON cache: false – “cache buster”

$.ajax({ url: '/websites/example.nsf/ajax_GetUserDetails?OpenAgent', data: {name: userName}, cache: false}).done(function(data) { // Process returned data here}).fail(function(e) { // Process failed call here}).always(function() { // This code will always execute});

or$.ajax({ url: '/websites/example.nsf/ajax_GetUserDetails?OpenAgent', data: {name: userName}, cache: false}).success(function(data) { // Process returned data here});

JSON – JavaScript Object Notation Describe data as Javascript objects Preferred to XML in web applications

• Less “chatty” (less bandwidth)• Very easy to access values directly in Javascript

Supports all Javascript data types Can contain arrays of objects Can be very complex if needed Use JSONLint to validate!

JSON Examples

Generate JSON on Domino ?ReadViewEntries&OutputFormat=JSON

• Available in Domino 7.0.2+• Can be hard to parse

Formula in Notes view• http://www.eknori.de/2011-07-23/formula-magic/

Lotusscript agent• Generate your own JSON• Test at JSONLint.com• Use JSON classes

Class.JSON (available in demo database) JSON Lotusscript Classes by Troy Reimer (on OpenNTF.org)

Generate JSON on Domino – Xpages Style XPages agent (SSJS XAgent)

• Domino 8.5.2 and XPages knowledge• Better performance than Lotusscript• http://www.wissel.net/blog/d6plinks/shwl-7mgfbn

REST Services control from Extension Library• Domino 8.5.2 and ExtLib on server

Demo 1Table using bootstrap-table plugin

Bootstrap-table plugin Plugin by @wenzhixin Get it at http://bootstrap-table.wenzhixin.net.cn CDN hosted version at CloudFlare.com Minimal HTML markup Javascript mainly to define columns and settings

Bootstrap-table – Add to the webpageHTML<div id="tableToolbar">

<div class="toolbarText">My Contacts</div></div><table id="ContactTable"></table>

jQuery (partial)$("#ContactTable").bootstrapTable({ url: 'ajax_Demo7_GetAllContacts?OpenAgent', search: true, showRefresh: true, pagination: true, pageSize: 25, classes: "table-condensed table-hover table-striped tableContent", toolbar: "#tableToolbar", columns: [{ field: 'FirstName', title: 'First Name', width: 80, sortable: true }, { field: 'LastName', title: 'Last Name', width: 90, sortable: true }, { … … …

Bootstrap-table – Generate JSON dataLotusscript code (partial)'*** Get all documents in view to processSet db = session.CurrentDatabaseSet view = db.GetView("(LookupContactsByLastName)")Set col = view.AllEntries'*** Start of JSON stringjsonString = “”'*** Loop through all entries and build JSON to returnSet entry = col.GetFirstEntryDo Until entry Is Nothing '*** Build JSON for each entry and add to string Set json = New JSONData() Call json.SetValue("LastName", CStr(entry.ColumnValues(0))) Call json.SetValue("FirstName", CStr(entry.ColumnValues(1))) Call json.SetValue("Company", CStr(entry.ColumnValues(2))) Call json.SetValue("Address", CStr(entry.ColumnValues(3))) Call json.SetValue("City", CStr(entry.ColumnValues(4))) Call json.SetValue("State", CStr(entry.ColumnValues(5))) Call json.SetValue("ZIP", CStr(entry.ColumnValues(6))) Call json.SetValue("DocUNID", CStr(entry.ColumnValues(9))) '*** Add new JSON to existing JSON string jsonString = jsonString + json.GetJSON() + "," + Chr$(13) Set entry = col.GetNextEntry(entry)Loop'*** Remove the trailing comma and line break if we have dataIf Len(jsonString) > 0 then jsonString = Left$(jsonString,Len(jsonString)-2)End If'*** Add brackets for arrayjsonString = "[ " + Chr$(13) + jsonString + Chr$(13) + “ ]“'*** MIME Header to tell browser what kind of data we will sendPrint "content-type: application/json"'*** Send JSON back to browserPrint jsonString

Bootstrap-table – Examine returned JSON

Bootstrap-table – The Result

Demo 2Contact database using Bootstrap

Contact database - Overview Display contacts – use Demo 1 as base Click on user in table to display details Buttons

• Edit • Save • New • Delete

Add refresh/reload of contacts when updated

Contact database – Add elements Lotusscript agents

• ajax_GetAllContacts• ajax_GetContactDetails• ajax_SaveContact

If DocUNID is blank, create new contact Otherwise update existing contact

• ajax_DeleteContact HTML page changes

• Add section for contact details• Detect click on row to display details• Add buttons and jQuery code to call Lotusscript agents

HTML – buttons<button class="btn btn-sm btn-primary" id="btnNewContact">New</button><button class="btn btn-sm btn-primary" id="btnEditContact">Edit</button><button class="btn btn-sm btn-success" id="btnSaveContact">Save</button><button class="btn btn-sm btn-danger pull-right" id="btnDeleteContact">Delete</button>

jQuery – Edit and New buttons//*** Button actions$("#btnEditContact").on("click", function(e) { setEditMode();});

$("#btnNewContact").on("click", function() { setEditMode(); // Empty all input fields $('input[data-notesfield]').each( function() { $(this).val(""); }); // Empty hidden DocUNID field $("#docUNID").attr("data-UNID",""); // Hide ‘Delete’ button $("#btnDeleteContact").hide();});

Contact database – New buttons

Contact database – New buttonsjQuery – Save button$("#btnSaveContact").on("click", function() { $("#btnSaveContact").hide(); var json = new Object(); // Store field values in JSON object var docunid = $("#docUNID").attr("data-UNID"); json["DocUNID"] = docunid; $('input[data-notesfield]').each( function() { var id = $(this).attr("id"); var notesfield = $(this).attr("data-notesfield"); json[notesfield] = $(this).val(); }); // Perform a call to the server to save values $.ajax({ url: "ajax_Demo8_SaveContact?OpenAgent", type: "POST", data: json }).done(function(data) { if (data.status=="error") { alert("Failure: " + data.msg); } else if (data.status=="success") { setReadMode(); // Convert INPUT back to DIV $("#contactTable").bootstrapTable("refresh", {silent: true}); }).fail( function(e) { alert("Failure!","Failed to save contact. Error: " + e.errorThrown); }); $("#btnEditContact").show(); });

Contact database – New buttonsjQuery – Delete button$("#btnDeleteContact").on("click", function(e) { var docunid = $("#docUNID").attr("data-UNID"); $.ajax({ url: "ajax_Demo8_DeleteContact?OpenAgent", type: "POST", data: {DocUNID: docunid } }).done(function(data) { if (data.status=="error") { alert("Failure: " + data.msg); } else if (data.status=="success") { $("#contactTable").bootstrapTable("refresh", {silent: true}); // Empty all input fields $('input[data-notesfield]').each( function() { $(this).val(""); }); // Empty all div with Notes data $('div[data-notesfield]').each( function() { $(this).html(""); }); // Empty hidden DocUNID storage $("#docUNID").attr("data-UNID","") $("#btnDeleteContact").hide(); $("#btnEditContact").hide(); } }).fail( function(e) { alert("Failure!","Failed to delete contact. Error: " + e.errorThrown); });});

Contact database – Detect click on rowjQuery – Detect click on table row// Detect click on row in table$("#contactTable").on('click-row.bs.table', function (e, row, $element) { // Convert INPUT fields back to DIV just in case setReadMode(); // Hide save button if visible $("#btnSaveContact").hide(); // Get DocUNID value in table and load corresponding values from server var unid = row.DocUNID; displayDetails(unid);});

Contact database – Load detailsjQuery – Load contact details from server and display on page// Get contact details from Domino server and populate fields// using the DocUIND value as lookup keyfunction displayDetails(docunid) { $.ajax({ url: 'ajax_Demo8_GetContactDetails?OpenAgent', data: {DocUNID: docunid}, cache: false }).success(function(data) { if(data.status=="success") { // For each element with data-notesfield attribute $('div[data-notesfield]').each( function() { notesfield = $(this).attr("data-notesfield"); if (data[notesfield]!=null) { fieldvalue = data[notesfield]; $(this).html(fieldvalue); } }); // Store DocUNID in enmpty div for later use $("#docUNID").attr("data-UNID",data.DocUNID); // Display previously hidden editand delete buttons $("#btnEditContact").show(); $("#btnDeleteContact").show(); } });}

Contact database – Fancy stuffjQuery – Switch between DIV (plan text) and INPUT (editable)// Put contact details into edit mode by changing DIV to INPUTfunction setEditMode() { $("#btnEditContact").hide(); // Change all div with Notes data to input $('div[data-notesfield]').each( function() { var id = $(this).attr("id"); var notesfield = $(this).attr("data-notesfield"); var input = "<input class='jsonData inputNotesField form-control input-sm' id='" + id input = input + "' data-notesfield='" + notesfield + "' value='" + $(this).html() + "'></input>"; $(this).replaceWith(input) }); $("#btnSaveContact").show(); $("#btnEditContact").hide(); }

// Put contact details into read mode by changing INPUT to DIVfunction setReadMode() { $('input[data-notesfield]').each( function() { var id = $(this).attr("id"); var notesfield = $(this).attr("data-notesfield"); var div = "<div class='jsonData displayNotesField' id='" + id div = div + "' data-notesfield='" + notesfield + "'>" + $(this).val() + "</div>"; $(this).replaceWith(div) });}

Contact database

Demo 3Contact database using jQuery Mobile

jQuery UI and jQuery Mobile UI Frameworks jQuery UI – alternative to Bootstrap

• Set of components Widgets Effects Interactions

• Built on top of jQuery jQuery Mobile – emulates native mobile app look

• Header/footer• Grid layout• Buttons• Listview widget• Forms• …and much more!

Contact database – jQuery Mobile Reuse agents from previous demo New HTML code

• Multi page application with two pages Contact list using Listview widget Contact details using form elements

New Javascript/jQuery• Some code can be reused!

Notification overlay when saved• Using notifIt! plugin• Can use other plugins, many available!

Contact database – Android, iOS, WinMobile Create native apps using PhoneGap One source, multiple targets Create folder structure on disk

• .html, .css and .js• config.xml in root folder

Create ZIP file Upload to PhoneGap Build Download finished apps

• Android• iOS• Windows Mobile

Demo 4Calendar using FullCalendar plugin

FullCalendar plugin Get it at http://fullcalendar.io Lotusscript agents

• ajax_Calendar_GetAllEvents Returning events between specific days Calendar automatically sends start and end date

• ajax_Calendar_GetEventDetails• ajax_Calendar_UpdateEvent

Triggered when moving event or changing duration Arguments: DocUNID, start date and end date

FullCalendar plugin Calendar points to JSON of event data Automatically sends start and end dates Agent returns only events within date range

jQuery – Display calendar and load JSON of event data from server var eventSource = 'ajax_Calendar_GetAllEvents?OpenAgent';$("#notesCalendar").fullCalendar({ events: eventSource});

FullCalendar pluginLotusscript agent ajax_Calendar_GetAllEvents'*** Local variables to hold arguments passed from URLDim startdate As StringDim enddate As String'*** Other local variablesDim jsontext As String

'*** Create new URLData objectSet url = New URLData()'*** Create new JSONData objectSet json = New JSONData()

'*** Check start date and convert from ISO to US date formatIf url.IsValue("start") Then startdate = ISOtoUS(url.GetValue("start"))Else startdate = "01/01/1980"End If

'*** Check end date and convert to US date formatIf url.IsValue("end") Then enddate = ISOtoUS(url.GetValue("end"))Else enddate = "12/31/2199"End If

FullCalendar pluginLotusscript agent ajax_Calendar_GetAllEvents (continued)'*** Send MIME header to browserPrint "content-type: application/json"jsontext = ""Set db = session.CurrentDatabaseSet view = db.GetView("Events")Set col = view.AllEntriesSet entry = col.GetFirstEntry()Do Until entry Is Nothing If CDat(entry.ColumnValues(0))>=CDat(startdate) Then If CDat(entry.ColumnValues(0))<=CDat(enddate) Then Call json.SetValue("id", CStr(entry.ColumnValues(5))) Call json.SetValue("title",CStr(entry.ColumnValues(3))) Call json.SetValue("start", Format$(CDat(entry.ColumnValues(0)),"mm/dd/yyyy hh:nn ampm")) Call json.SetValue("end", Format$(entry.ColumnValues(1),"mm/dd/yyyy hh:nn ampm")) '*** Make the entry editable in calendar (allow changing date/time) Call json.SetBoolean("editable", True) End If End If jsontext = jsontext + json.GetJSON() + "," + Chr$(13) Set entry = col.GetNextEntry(entry)LoopIf Len(jsontext)>4 Then jsontext = Left$(jsontext,Len(jsontext)-2)End IfPrint "[ " + jsontext + " ]"

FullCalendar pluginFullCalendar plugin – Events triggered on click, resize and drop/move…eventClick: function(calEvent, jsEvent, view) { var unid = calEvent.id; displayEventDetails(unid);},

eventResize: function(event, delta, revertFunc) { if (!confirm(event.title + " will now end at " + event.end.format("h:mm a") + "\nAre you sure?")) { revertFunc(); } else { var unid = event.id; updateEvent(unid,event.start.format("MM/DD/YYYY hh:mm a"),event.end.format("MM/DD/YYYY hh:mm a")); displayEventDetails(unid) }},

eventDrop: function(event, delta, revertFunc) { var prompt = event.title + "<br>was moved to " + event.start.format("MM/DD/YYYY") prompt = prompt + " at " + event.start.format("h:mm a"); bootbox.confirm(prompt + "<br>Are you sure you want to do that?", function(result) { if(result==true) { var unid = event.id; updateEvent(unid,event.start.format("MM/DD/YYYY hh:mm a"),event.end.format("MM/DD/YYYY hh:mm a")); displayEventDetails(unid) } else { revertFunc(); } }); }…

FullCalendar pluginJavascript code – Send event update to serverfunction updateEvent(docunid,startDT,endDT) { var json = new Object(); json["DocUNID"] = docunid; json["EventStart"] = startDT; json["EventEnd"] = endDT; // Perform a call to the server to save new event date/time $.ajax({ url: "ajax_Calendar_UpdateEvent?OpenAgent", type: "POST", data: json }).done(function(data) { if (data.status=="error") { bootstrapAlert(data.msg,"danger"); } else if (data.status=="success") { bootstrapAlert(data.msg,"success"); } }).fail( function(e) { bootstrapAlert("Failed to create progress note. Error: " + e.errorThrown,"danger"); });}

FullCalendar pluginLotusscript agent – ajax_Calendar_UpdateEvent'--- Local variablesDim startDate As StringDim endDate As String

'*** Get documentSet db = session.CurrentDatabaseIf url.GetValue("DocUNID")<>"" Then Set doc = db.GetDocumentByUNID(url.GetValue("DocUNID"))End If'*** Check that we found a document, otherwise exitIf doc Is Nothing Then Set json = New JSONData() json.success = False json.SetErrorMsg("Failed to locate document '" & url.GetValue("DocUNID")) Call json.SendToBrowser() Exit Sub End IfCall doc.ReplaceItemValue("EventStart",CDat(url.GetValue("EventStart")))Call doc.ReplaceItemValue("EventEnd",CDat(url.GetValue("EventEnd")))Call doc.Save(True,False)Set json = New JSONData()json.success = Truejson.SetMsg("Updated '" & doc.GetItemValue("EventTitle")(0) & "' with new date/time")Call json.SendToBrowser()

FullCalendar – End Result

Summary

What have we seen? Ajax/JSON – efficient way to access Domino data CRUD using server based agents (Lotusscript) jQuery and Bootstrap to speed up development

Why learn this? Quickly create attractive and modern web applications Still access existing Domino data with ease Acquire some new and easy-to-learn skills Those skills are beneficial on other platforms as well Many plugins available for free – less work for you! Your customers and/or boss will love you!

Questions?

Thank you!

Karl-Henry MartinssonEmail: texasswede@gmail.comTwitter, Skype: @texasswedehttp://www.linkedin.com/in/texasswedehttp://blog.texasswede.com

top related