Vue.js is a popular framework for developing rich client web applications, leveraging browsers for all they are worth. Vue.js has attracted a large number of developers that together have produced a large number of quite interesting reusable components. ADF Faces is itself a quite mature framework for the development of rich web applications. It was born over 10 years ago. It has evolved over the years and adopted quite a few browser enhancements over the years. However, ADF Faces is still – and will stay – a server side framework that provides only piecemeal support for HTML5 APIs. When developing in ADF Faces, it feels a bit as if your missing out on all those rapid, cool, buzzing developments that take place on the client side.
Oracle strongly recommends you to stay inside the boundaries of the framework. To use JavaScript only sparingly. To not mess with the DOM as that may confuse Partial Page Rendering, one of the cornerstones of ADF Faces 11g and 12c. And while I heed these recommendations and warnings, I do not want to miss out on all the goodness that is available to me.
So we tread carefully. Follow the guidelines for doing JavaScript things in ADF Faces. Try to keep the worlds of ADF Faces en Vue.js apart except for when they need to come into contact.
In this article, I will discuss how the simplest of Vue.js application code can be integrated in a ‘normal’ ADF Faces web application. Nothing fancy yet, no interaction between ADF Faces client components and Vue.js, no exchange of events or even data. Just a hybrid page that contains ADF Faces content (largely server side rendered) and Vue.js content (HTML based and heavily post processed in JavaScript as is normally the case with Vue.js).
The steps we have to go through:
- Create new ADF Faces Web Application with main page
- Import Vue.js JavaScript library into ADF Faces web application main page
- Create HTML document with Vue.js application content – HTML tags, custom tags, data bound attributes; potentially import 3rd party Vue.js components
- Create JavaScript module with initialization of Vue.js application content (function VueInit() – data structure, methods, custom components, … (see: https://vuejs.org/v2/guide/instance.html)
- Create a container in the ADF Faces main page to load the Vue.js content into
- Import HTML document with Vue.js content into browser and add to main page DOM
- Import custom Vue.js JavaScript module into main page; upon ADF Faces page load event, call VueInit()
When these steps are complete, the application can be run. The browser will bring up a page with ADF Faces content as well as Vue.js content. A first step towards a truly hybrid application with mutually integrated components. Or at least some rich Vue.js components enriching the ADF Faces application. Such as the time picker (https://www.npmjs.com/package/vue-clock-picker), the Google Charts integrator (https://github.com/haydenbbickerton/vue-charts) and many more.
The source code described in this article is in GitHub: https://github.com/lucasjellema/ADFplusVueJS.
A brief overview of the steps and code is provided below. The biggest challenge probably was to get HTML into the ADF Faces page that could not be parsed by the ADF Faces framework (that does not allow the notation used by Vue.js such as :value=”expression” and @click=”function”. Using link for an HTML document is a workaround, followed by a little DOM manipulation. At this moment, this approach is only supported in Chrome browser. For Firefox there is a polyfill available and perhaps an approach based on XMLHttpRequest is viable (see this article).
Create new ADF Faces Web Application with main page
Use the wizard to create the new application. Then create a new page: main.jsf. Also create a JavaScript module: main.js and import it into the main page:
<af:resource type=”javascript” source=”resources/js/main.js”/>
Import Vue.js JavaScript library into ADF Faces web application main page
Add an af:resource tag that references the online resource https://unpkg.com/vue for the Vue.js 2 framework library.
<af:resource type=”javascript” source=”https://unpkg.com/vue”/>
Create HTML document with Vue.js application content
Just create a new HTML document in the application – for example VueContent.html. Add some Vue.js specific content using data bound syntax with : and {{}} notation. Use a third party component – for example the 3D carousel: https://wlada.github.io/vue-carousel-3d/examples/.
The final HTML tags are in VueContent.html as is an import of the 3D carousel component (straight JavaScript reference). Some local custom components are defined in VueContent.js; that is also where the data is prepared that is leveraged in this document.
Create JavaScript module with initialization of Vue.js application content
Create JavaScript module VueContent.js with a function VueInit() that will do the Vue.js application initialization and set up data structure, methods, … (see: https://vuejs.org/v2/guide/instance.html).
In this library, local custom components are defined – such as app-menu, app-menu-list, update, updates-list, status-replies, post-reply – and third party components are registered – carousel-3d and slide.
The VueInit() function does the familiar hard Vue.js work:
function initVue() { console.log("Initialize Vue in VueContent.js"); new Vue({ el: '#app', data: { greeting: 'Welcome to your hybrid ADF and Vue.js app!', docsURL: 'http://vuejs.org/guide/', message: 'Hello Vue!', value:'Welcome to the tutorial <small>which is all about Vue.js</small>', viewed:true, updates:updates, showReplyModal: false, slides: 7 }, methods: { humanizeURL: function (url) { return url .replace(/^https?:\/\//, '') .replace(/\/$/, '') } }, components: { 'carousel-3d': Carousel3d.Carousel3d, 'slide': Carousel3d.Slide } }) /* new Vue */ }
Create a container in the ADF Faces main page to load the Vue.js content into
The Vue.js content can be loaded in the ADF page into a DIV element. Such an element can best be created into an ADF Faces web page by using a af:panelGroupLayout with layout set to vertical (says Duncan Mills):
<af:panelGroupLayout id=”app” layout=”vertical”>
Import HTML document with Vue.js content into browser and add to main page DOM
JSF 2 allows us to embed HTML in our JSF pages – XHTML and Facelet, jspx and jsff – although as it happens there are more than a few server side parser limitations that make this not so easy. Perhaps this is only for our own good: it forces us to strictly separate the (client side) HTML that Vue.js will work against and the server side files that are parsed and rendered by ADF Faces. We do need a link between these two of course: the document rendered in the browser from the JSF source needs to somehow import the HTML and JavaScript resources.
The Vue.js content is in a separate HTML document called VueContent.html. To add the content of this document – or at least everything inside a DIV with id=”content” – to the main page, add a <link> element (as described in this article ) and have it refer to the HTML document. Also specify an onload listener to process the content after it has been loaded. Note: this event will fire before the page load event fires.
<link id=”VueContentImport” rel=”import” href=”VueContent.html” onload=”handleLoad(event)” onerror=”handleError(event)”/>
Implement the function handleLoad(event) in the main.js JavaScript module. Have it get hold of the just loaded document and deep clone it into the DOM, inside the DIV with the app id (the DIV rendered from the panelGroupLayout component).
Import custom Vue.js JavaScript module into main page and call upon Page Load Event
Import JavaScript module:
<af:resource type=”javascript” source=”resources/js/VueContent.js”/>
Add a clientListener component to execute function init() in main.js that will call VueInit() in VueContent.js :
<af:clientListener method=”init” type=”load”/>
In function init(), call VueInit() – the function that is loaded from VueContent.js – the JavaScript module that constitutes the Vue.js application together with VueContent.html. In VueInit() the real Vue.js initialization is performed and the data bound content inside DIV app is prepared.
The overall set up and flow is depicted in this figure:
And the application looks like this in JDeveloper:
When running, this is what we see in the browser (note: only Chrome supports this code at the moment); the blue rectangle indicates the Vue.js content:
And at the bottom of the page, we see the 3D Carousel:
Next steps would have us exchange data and events between ADF Faces components and Vue.js content. But as stated at the beginning – we tread carefully, stick to the ADF framework as much as possible.
Resources
Vue 2 – Introduction Guide – https://vuejs.org/v2/guide/
Vue Clock Picker component – Compare · DomonJi/vue-clock-picker
Google Charts plugin for Vue – Google Charts Plugin For Vue.js – Vue.js Script
How to include HTML in HTML (W3 Schools) – https://www.w3schools.com/howto/howto_html_include.asp
HTML Imports in Firefox – https://developer.mozilla.org/en-US/docs/Web/Web_Components/HTML_Imports
Chrome – HTML5 Imports: Embedding an HTML File Inside Another HTML File – https://onextrapixel.com/html5-imports-embedding-an-html-file-inside-another-html-file/
Me, Myself and JavaScript – Working with JavaScript in an ADF World, Duncan Mills, DOAG 2015 – https://www.doag.org/formes/pubfiles/7495804/docs/Konferenz/2015/vortraege/Development/2015-K-DEV-Duncan_Mills-Me,_Myself_and_JavaScript-Praesentation.pdf
What about HTML5 audio ?
http://forums.mozillazine.org/viewtopic.php?f=23&t=3032395
Hi Michael,
Would you care to elaborate? I do not understand your comment.
kind regards
Lucas
Hi Lucas,
Great direction of study!
What I can’t understand is how you then propagate the data from Vue.js components back to ADF (server side layer)
Hi Florin, I will write an article on that in the near future. I have some ideas, and some preliminary code. You will find a repo on my Github account dedicated to ADF and JavaScript. That may provide some inspiration. Kind regards, Lucas