Вы находитесь на странице: 1из 54

ADF

How to Bring Common UI Patterns to ADF

Luc Bors, AMIS, The Netherlands


Wednesday, June 27, 2012 ODTUG KScope 12 San Antonio, Texas, USA

UI PATTERNS

FUSION APPS PATTERNS

RECOGNIZE THESE ?

RECOGNIZE THESE ?

TABBED NAVIGATION

TABBED NAVIGATION

CLOSE ALL / CLOSE OTHERS

CLOSE ALL / CLOSE OTHERS

CLOSE ALL / CLOSE OTHERS


e

CAN YOU DO THIS IN ADF ?

CREATE THE CONTEXT MENU


<af:popup id="contextMenu" contentDelivery="lazyUncached" eventContext="launcher" launcherVar="source"> <af:menu id="men1" text="#{source.attributes.menuInvokedOnTab}"> <af:commandMenuItem id="cmi1" text="close actionListener="#{viewScope.jhsDynTabContext.closeThis}"> <af:setPropertyListener from="#{source.attributes.menuInvokedOnTab}" to="#{viewScope.jhsDynTabContext.menuInvokedOnTab}" type="action"/> </af:commandMenuItem> <af:commandMenuItem id="cmi2" text="close others actionListener="#{viewScope.jhsDynTabContext.closeOthers}"> . </af:commandMenuItem> <af:commandMenuItem id="cmi3" text="close all actionListener="#{viewScope.jhsDynTabContext.closeAll}"> .. </af:commandMenuItem> </af:menu> </af:popup>

INVOKING THE CONTEXT MENU


<af:navigationPane ...........> <af:commandNavigationItem ...............> <f:attribute name="tabId" value="#{tab.id}"/> <af:clientListener method="showMenu" type="contextMenu"/> <af:clientAttribute name="menuInvokedOnTab" value="#{tab.id}"/> </af:commandNavigationItem> </af:navigationPane >

function showMenu(evt) { var popup = AdfPage.PAGE.findComponent("pt:contextMenu"); .. popup.show(hints); evt.cancel(); }

USE EXISTING FUNCTIONALITY

CLOSE THIS TAB


public void closeThis(ActionEvent action) { String id = getMenuInvokedOnTab(); List<String> tabsToRemove = new ArrayList(); for (DynTab t : getActiveTabList()) { String x = t.getId(); if (id == x) { tabsToRemove.add(x); } } for (String t : tabsToRemove) { removeTab(t); } }

CLOSE OTHER TABS


public void closeOthers(ActionEvent action) { String id = getMenuInvokedOnTab(); List<String> tabsToRemove = new ArrayList(); for (DynTab t : getActiveTabList()) { String x = t.getId(); if (id != x) { tabsToRemove.add(x); } } for (String t : tabsToRemove) { removeTab(t); } }

CLOSE ALL
public void closeAll(ActionEvent action) {

List<String> tabsToRemove = new ArrayList(); for (DynTab t : getActiveTabList()) { tabsToRemove.add(t.getId()); } for (String t : tabsToRemove) { removeTab(t); }

CLOSE ALL / CLOSE OTHERS

MOST RECENTLY USED (..HISTORY)

IMPLEMENTATION
Record activities Use setPropertyListeners Historybean (session scope) that manages the collection of navigation events Display Label, Entity Type and Primary Key bean calls BusinessService to record event in database for this user (Optional) Also remove events for deleted records!

CREATE THE BEAN


Create History bean <managed-bean id=1"> <managed-bean-name id=2"> recentHistoryBean</managed-bean-name> <managed-bean-class id=3"> nl.amis.jsf.history.beans.RecentHistoryBean </managed-bean-class> <managed-bean-scope id=3">session</managed-bean-scope> </managed-bean>

INTERCEPT ACTIVITY
<af:commandLink id="ot3" text="#{row.LastName}" action="edit" actionListener="#{recentHistoryBean.add}"> <af:setPropertyListener from="#{row.EmployeeId} to="#{recentHistoryBean.entityInstanceIdentifier}" type="action"/> <af:setPropertyListener from="#{row.LastName} to="#{recentHistoryBean.entityInstanceDisplayLabel}" type="action"/> <af:setPropertyListener from="#{'EMP'}" to="#{recentHistoryBean.entityType}" type="action"/> </af:commandLink>

RECORD ACTIVITY
public void add(ActionEvent actionEvent) { // Add event code here...

recentHistory.add(new HistoryEvent(new oracle.jbo.domain.Date() , entityType , entityInstanceIdentifier , entityInstanceDisplayLabel)); HRServiceImpl hrAppMod = (HRServiceImpl) pageTemplateBc.getDataControl().getApplicationModule(); hrAppMod.recordAndPersistHistoryEntry(recentHistory.get(0));

PERSIST ACTIVITY
public void recordAndPersistHistoryEntry(HistoryEvent event) { String statement = "RECENT_HISTORY_MANAGER.RECORD_AND_PERSIST_ENTRY(?,?,?,?)";

callStoredProcedure(statement, new Object[] {event.getEntityType() , event.getKey().toString() , event.getDisplayLabel(), null}); }

THE RESULT

DO YOU RECOGNIZE THIS ??

GOOGLE SEARCH

ADF QUERY COMPONENT

PREPARE THE DATABASE

Make sure that the HR user is allowed to use the ctxsys.ctx_ddl package

grant EXECUTE on CTXSYS.CTX_DDL to HR

CREATE A SEARCH PACKAGE

function get_emp_search_item ( p_rowid in rowid ) return varchar2 as begin for b in (select e.first_name , e.last_name , e.email , e.phone_number , j.job_title from employees e left join jobs j using (job_id) where e.rowid = p_rowid) loop return b.first_name || ' ' || b.last_name || ' (' || b.email || ', ' || b.phone_number || ', ' || b.job_title || ')'; end loop; end get_emp_search_item;

CREATE THE ORACLE TEXT INDICES

-- Configure preferences... ctx_ddl.create_preference('emp_datastore', 'user_datastore'); ctx_ddl.set_attribute('emp_datastore', 'procedure' , 'ot_search.create_emp_search_item');

-- Create the indices... execute immediate 'create index emp_search_index on employees(last_name) indextype is ctxsys.context parameters (''datastore emp_datastore wordlist wordlist lexer lexer stoplist stoplist sync (on commit)'')';

THE BASE CLASSES

OracleTextSearchSupport: Converts the given user input (the search command): <code>searchValue</code> to an Oracle Text search-string. BaseViewObjectImpl overrides getCriteriaItemClause(ViewCriteriaItem vci) BaseViewDefImpl implementation to provide the following custom properties on ViewObjects:
ORACLE_TEXT_SEARCH_ATTRIBUTE: To mark the column in which the seach info is queried ORACLE_TEXT_INDEX_ATTRIBUTE: To mark the database column on which the index was defined in the database.

CREATE THE MODEL PROJECT

CREATE THE MODEL PROJECT

VIEWCONTROLLER : A BEAN
<managed-bean-name id="1">departmentsQuickSearch</managed-bean-name> <managed-bean-class id="4"> adfplus.quicksearch.controller.bean.QuickSearchBean </managed-bean-class> <managed-bean-scope id="2">pageFlow</managed-bean-scope> <managed-property id=8"> <property-name id=10">iteratorBindingName</property-name> <property-class>java.lang.String</property-class> <value id="9">DepartmentsVO1Iterator</value> </managed-property> <managed-property id="11"> <property-name id="13">searchAttribute</property-name> <property-class>java.lang.String</property-class> <value id="12">DepartmentSearchString</value> </managed-property> <managed-property id="14"> <property-name id="15">searchIteratorBindingName</property-name> <property-class>java.lang.String</property-class> <value id="16">DepartmentsVO1IteratorQuickSearch</value> </managed-property>

VIEWCONTROLLER : A SUBFORM
<af:subform id="s1" defaultCommand="cb7"> <af:panelGroupLayout id="pgl4" layout="horizontal" inlineStyle="margin:10px;"> <af:inputText label="Search" id="it2 value="#{pageFlowScope.departmentsQuickSearch.searchValue}"> <af:autoSuggestBehavior suggestItems= "#{pageFlowScope.departmentsQuickSearch.suggestItems}" maxSuggestedItems="10"/> </af:inputText>

<af:commandButton text="Search" id="cb7" action="#{pageFlowScope.departmentsQuickSearch.go}" partialSubmit="true"/> </af:panelGroupLayout> </af:subform>

VIEWCONTROLLER : SEARCH METHOD


private synchronized List<SelectItem> search(String searchValue) { DCIteratorBinding iter = getSearchIteratorBinding(); applySearchCriteria(iter, searchAttribute, searchValue); translations.clear(); lastSuggestList = new ArrayList<SelectItem>(); lastSearchValue = searchValue; Row[] rows = iter.getAllRowsInRange(); for (Row row : rows) { String description = (String)row.getAttribute(searchAttribute); lastSuggestList.add(new SelectItem(description)); translations.put(description, row.getKey()); } return lastSuggestList; }

THE RESULT

REAL TIME UPDATES

THE CONCEPT

THE IMPLEMENTATION; THE DATABASE

grant change notification to <user>;

REGISTER FOR DBQRCN (STEP 1)

public void startChangeNotification(){ DatabaseChangeRegistration dcr = null; String query = "SELECT * from DEPARTMENTS"; Properties prop = new Properties(); prop.setProperty(OracleConnection.DCN_NOTIFY_ROWIDS ,"true"); prop.setProperty(OracleConnection.DCN_QUERY_CHANGE_NOTIFICATION ,"true"); try { dcr = conn.registerDatabaseChangeNotification(prop); RdbmsChangeEventListener listener = new RdbmsChangeEventListener(this); dcr.addListener(listener); ..

REGISTER FOR DBQRCN (STEP 2)

// second step: add objects in the registration: Statement stmt = conn.createStatement(); ((OracleStatement)stmt).setDatabaseChangeRegistration(dcr); ResultSet rs = stmt.executeQuery(query); while (rs.next()){} rs.close(); stmt.close();

WHATS NEXT .

SETUP ACTIVE DATA COMPONENT


public void setupActiveData() { ActiveModelContext context = ActiveModelContext.getActiveModelContext(); Object[] keyPath = new String[0]; context.addActiveModelInfo( this , keyPath , "activemessage"); System.out.println("add active bean as listener"); databaseNotificationProcessor.registerAsListener(this); }

SETUP THE ACTUAL UPDATE


public void triggerDataUpdate(String message) { this.message = message; counter.incrementAndGet(); ActiveDataUpdateEvent event = ActiveDataEventUtil.buildActiveDataUpdateEvent( ActiveDataEntry.ChangeType.UPDATE, counter.get(), new String[0], null, new String[] { "activemessage" }, new Object[] { message }); System.out.println("fireActiveDataUpdate"); fireActiveDataUpdate(event); }

IMPLEMENTATION IN THE PAGE


<af:activeOutputText value="#{pageFlowScope.trackChangesBean.updates}" id="aot1" visible="false"> <af:clientListener method="activeDataCallback" type="propertyChange"/> </af:activeOutputText>

<af:resource type="javascript"> activeDataCallback = function (event) { var button = AdfPage.PAGE.findComponentByAbsoluteId("pt1:r1:0:cb1"); button.setVisible(true); } </af:resource>

THE RESULT

PATTERNS UNDER INVESTIGATION


Grouping Tabs Drag and Drop Tabs in UI Shell dragSource and dropTarget

Duplicating Tabs Restarting a new instance of a taskflow

PATTERNS UNDER INVESTIGATION


Adding Sticky Notes dragSource and dropTarget Contextual events

Concept : http://technology.amis.nl
Search for: adf-11g-dragn-drop-and-contextual-events/

RESOURCES

RESOURCES

SUMMARY

ADF
How to Bring Common UI Patterns to ADF

Luc Bors, AMIS, The Netherlands Luc.Bors@amis.nl LucBors@gmail.com Follow me on Twitter : @lucb_

Wednesday, June 27, 2012 ODTUG KScope 12 San Antonio, Texas, USA

Вам также может понравиться