Working with Collections

Use Oracle JET data collection components to display data in tables, data grids, list views, or trees.

The Oracle JET data collection components include ojTable, ojDataGrid, ojTree, and ojListView, and you can use them to display records of data. ojTable and ojDataGrid both display data in rows and columns, and your choice of component depends upon your use case. Use ojTree to display hierarchical data such as a directory structure and ojListView to display a list of data. The framework also includes pagination and row expanders that display hierarchical data in a data grid or table row.

The Oracle JET Cookbook and JavaScript API Reference for Oracle® JavaScript Extension Toolkit (JET) include complete demos and examples for using the collection components, and you may also find the following tips and tricks helpful.

Topics:

Choosing a Table or Data Grid

Oracle JET provides both the ojTable and ojDataGrid components for displaying records of data in rows and columns, and this section can help you decide which component to use in your application.

The ojTable component displays records of data on a row basis. It's best used when you have simple data that can be presented as rows of fields, and it should be your first choice unless you require advanced features. A selection in the table provides you with the row of data and all of the fields in that row or record. The sizing of the table is based on the content itself. The height and width of the cells is adjusted for the content included. ojTable is based on the HTML table element. You can write templates using table elements such as tr, td, th, and so on.

The ojDataGrid is designed to provide grid functionality. It provides the ability to select individual or ranges of cells of data. It's best used when you need to display totals or tallies of data across columns or rows of data. The ojDataGrid is designed to be much more flexible in its layout and functionality than the ojTable component. It's a low-level component that you can shape in your application to how you want the data to be displayed. The overall size of the data grid is not determined by its content, and the developer specifies the exact height and width of the container. The data grid acts as a viewport to the contents, and its size doesn't affect the size of the columns and rows like a table does. ojDataGrid is based on the HTML div element, and you can host the template content inside a div DOM structure.

The table below provides a feature comparison of the ojTable and ojDataGrid components.

Feature ojTable ojDataGrid

Column/Row sizing

Controlled by content or CSS styles. Percent values supported for width and height.

Controlled by cell dimensions. Does not support percent values for width and height.

User-resizable column/row

No

Yes

Row reordering

No

Yes

Column sorting

Yes

Yes

Column/Row selection

Yes

Yes

Cell selection

No

Yes

Marquee selection

No

Yes

Row header support

No

Yes

Pagination

Page, high water mark

Page, high water mark, virtual scrolling (see note)

Custom cell templates

Yes

Yes

Custom row templates

Yes

No

Custom cell renderers

Yes

Yes

Custom row renderers

Yes

No

Row expander support

Yes

Yes

Cell stamping

Yes

Yes

Render aggregated cubic data

No

Yes

Base HTML element

table

div

Custom footer template

Yes (provides access to column data for passing to a JavaScript function)

No (cell level renderers used for column and row data manipulations)

Cell content editing

Yes

Yes

Content filtering

Yes

Yes

Note:

True virtual scrolling is available as a feature of ojDataGrid. Modern design principles should be considered and implemented before implementing virtual scrolling. It is much more desirable to present the end user with a filtered list of data that will be more useful to them, than to display thousands of rows of data and expect them to scroll through it all. True virtual scrolling means that you can perform scrolling in both horizontal and vertical directions with data being added and removed from the underlying DOM. High water mark scrolling (with lazy loading) is the preferred method of scrolling, and you should use it as a first approach.

Working with Data Grids

The ojDataGrid component displays data in cells inside a grid consisting of rows and columns. ojDataGrid is themable and supports WAI-ARIA authoring practices for accessibility. You can configure the ojDataGrid for cell selection with row and column headers or for row selection with column headers.

Note:

When you configure the data grid for row selection, the grid has a look and feel that is similar to the ojTable component. However, there are distinct differences in functionality between the ojDataGrid and ojTable components. For additional information, see Choosing a Table or Data Grid.

To create the ojDataGrid, add the HTML div element to your markup and define the ojDataGrid component on that element. Assign an id to the div and optional styling for width and height.

<div id="datagrid" style="width:202px;height:110px"
     aria-label="Data Grid Cell Based Grid Demo"
     data-bind="ojComponent: {component: 'ojDataGrid', data: dataSource}">
</div>

In this example, the aria-label is added to the div element for accessibility. ojDataGrid implicitly defines an Aria role as application, and you must add the aria-label to distinguish it from other elements defined with the Aria application role. For additional information about Oracle JET and accessibility, see Developing Accessible Applications.

The data is defined in the dataSource object and can be one of the following:

  • oj.ArrayDataGridDataSource: Use to define data in a static array.

    The array can be a single array where each item in the array represents a row in the data grid, or a two dimensional array where each item represents a cell in the grid. For the data grid shown in this section, the data is defined as a two dimensional array.

    require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/ojdatagrid', 'ojs/ojarraydatagriddatasource'],
      function(oj, ko, $)
      {
        $(document).ready(                  
          function()
          {
            ko.applyBindings({dataSource: new oj.ArrayDataGridDataSource({
                             ['1', '2', '3'},
                             ['4', '5', '6'},
                             ['7', '8', '9'}
                             ])}, document.getElementById('datagrid'));
          }
        );
      });
    

    oj.ArrayDataGridDataSource also supports custom sort behavior through its comparator property. For details, consult the oj.ArrayDataGridDataSource API documentation.

  • oj.CollectionDataGridDataSource: Use to define data using the Oracle JET Common Model. The data grid will respond to events generated by the underlying oj.Collection.

    For more details about oj.CollectionDataGridDataSource, see the oj.CollectionDataGridDataSource API documentation.

  • oj.PagingDataGridDataSource: Use to include pagination. For additional information, see Working with Pagination.

  • oj.FlattenedTreeDataGridDataSource: Use to display hierarchical data in the data grid. The user can expand and collapse rows in the data grid. For additional information, see Working with Row Expanders.

  • oj.CubeDataGridDataSource: Use to include aggregate values on column headers, row headers, or both. For additional information, see Working with oj. CubeDataGridDataSource.

  • Custom data source: Use to provide your own data source for the data grid. The Oracle JET Cookbook contains examples for creating custom data sources, including an example with nested headers.

The Oracle JET Cookbook includes the complete example for the data grid used in this section at Data Grids. The cookbook also includes examples that show row-based data grids, editable data grids, and data grids with visualizations, end headers, custom cell renderers, and custom data sources.

Working with oj. CubeDataGridDataSource

Use oj.CubeDataGridDataSource to render aggregated cubic data in your data grid. You can aggregate values on column headers, row headers, or both column and row headers. You can also define a page axis to filter or otherwise restrict the display.

For example, you may have a collection that contains data for sales, number of units sold, and sales tax data for vehicles sold by car dealerships, and you'd like to aggregate the data to show sales, unit, and tax data by year and city. Your data also contains the type of vehicle sold, its color, and type of drive train, and you'd also like to aggregate the data to show the sales, unit, and tax data grouped by product, color, and drive train.

You can configure ojDataGrid with the oj.CubeDataGridDataSource to achieve the desired grouping. The following image shows the runtime display.

The data grid uses JSON data for its data source. The code sample below shows a portion of the JSON array.

[
  {
    "index": 0,
    "units": 80,
    "sales": 535,
    "tax": 0.0234,
    "year": "2014",
    "gender": "Male",
    "product": "Coupe",
    "city": "New York",
    "drivetrain": "FWD",
    "color": "White"
  },
  {
    "index": 1,
    "units": 95,
    "sales": 610,
    "tax": 0.0721,
    "year": "2015",
    "gender": "Male",
    "product": "Coupe",
    "city": "New York",
    "drivetrain": "FWD",
    "color": "White"
  },
  {
    "index": 2,
    "units": 27,
    "sales": 354,
    "tax": 0.0988,
    "year": "2014",
    "gender": "Female",
    "product": "Coupe",
    "city": "New York",
    "drivetrain": "FWD",
    "color": "White"
  },

]

The data also contains a column for the gender of the buyer which isn't included in the display. The totals displayed in the grid come from applying the aggregation across any JSON rows that match up on type, color, drivetrain, year, and city. For the data in this example, this has the effect of grouping the Male and Female values and applying the aggregation. For example, the units shown in the grid for the New York sales in 2014 of white FWD coupes comes from totaling the highlighted values:

80 + 27 = 107 units

The following code sample shows the markup for the data grid.

<div id="datagrid" 
         style="width:100%;height:400px;max-width:851px;"
         aria-label="Cubic Data Source Grid Demo"
         data-bind="ojComponent: {component: 'ojDataGrid', 
            data: dataSource,
            cell: {
                renderer: cellRenderer
            }}">
</div>

In this example, the datasource is defined in the application's main script.

require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/ojmodel', 
     'promise', 'ojs/ojcube', 'ojs/ojdatagrid'],
function(oj, ko, $)
{
    function generateCube(dataArr, axes) {
        return new oj.DataValueAttributeCube(dataArr, axes,
                [{attribute:'units',aggregation:oj.CubeAggType['SUM']},
                    {attribute:'sales'},
                    {attribute:'tax',aggregation:oj.CubeAggType['AVERAGE']}]);
    };
    
    function dataGridModel(collection) {
        var vm = this;
        
        var dataArr = collection.map(function(model) {
            return model.attributes;
        });
        var axes = [
            {axis: 0, levels: [
                    {attribute: 'city'},
                    {attribute: 'year'},
                    {dataValue: true}]},
            {axis: 1, levels: [
                    {attribute: 'product'},
                    {attribute: 'color'},
                    {attribute: 'drivetrain'}]}];
 
        this.dataSource = new oj.CubeDataGridDataSource(
                generateCube(dataArr, axes));
       
        
    };
    
    var collection = new oj.Collection(null, {
        url: 'cookbook/dataCollections/dataGrid/cubeGrid/cubedata.json'
    });
 
    collection.fetch({success:function() {
        ko.applyBindings(new dataGridModel(collection), 
            document.getElementById('datagrid'));
    }});
});

The oj.CubeDataGridDataSource parameter is instantiated with the return value of the generateCube() function, which is an oj.Cube object. The oj.Cube class provides functions for the oj.DataValueAttributeCube class which creates the object to convert the row set data into grouped, cubic data. The oj.DataValueAttributeCube constructor takes the following parameters:

  • rowset: An array of objects containing name-value pairs

    In this example, the JSON data is mapped to the dataArr object which is defined as an oj.Collection object.

    var dataArr = collection.map(function(model) {
                  return model.attributes;
    });
    var collection = new oj.Collection(null, {
            url: 'cookbook/dataCollections/dataGrid/cubeGrid/cubedata.json'
    });
    
  • layout: An array of objects that contains the axis number and levels to use for the aggregation

    The axis number indicates where you want the aggregated data displayed: 0 for column headers, and 1 for row headers. The levels tell the cube which values to aggregate and the order to display them. The code sample below shows the layout used in this example. The dataValue property indicates which level to display for the units, sales, and tax aggregated values.

    var axes = [
                {axis: 0, levels: [
                        {attribute: 'city'},
                        {attribute: 'year'},
                        {dataValue: true}]},
                {axis: 1, levels: [
                        {attribute: 'product'},
                        {attribute: 'color'},
                        {attribute: 'drivetrain'}]}];
    

    You can configure an additional axis that you can use to filter or otherwise restrict the display of data. For example, you can add a third axis which includes the color and drivetrain attributes, as shown in the following code sample:

    var axes = [
                {axis: 0, levels: [
                        {attribute: 'city'},
                        {attribute: 'year'},
                        {dataValue: true}]},
                {axis: 1, levels: [
                        {attribute: 'product'}]},
                {axis: 2, levels:[
                        {attribute: 'color'},
                        {attribute: 'drivetrain'}]}];
    

    When you add a third axis to the page, the axis will not be visible. However, you can use the setPage() method to use the axis as a page axis to filter the display. For example, setting color and drivetrain on the third axis has the effect of restricting the display to aggregate only those values that match both the color and drivetrain attributes.

    In this example, you have six page combinations: White FWD, White AWD, Black FWD, Black AWD, Red FWD, and Red AWD products. If you set the color attribute to White and the drivetrain attribute to 4WD as shown in the following image, when you render the page only the values for White, 4WD products (Coupe, Sedan, Wagon, SUV, Van, and Truck) display on the screen.

  • dataValues: An array of objects that contains the name of the attribute in the row set that represents the data, an optional label, and the aggregation type. The aggregation type is also optional and defaults to SUM. You can also set it to one of the aggregation types shown in the following table.

    Aggregation Type Description

    AVERAGE

    Average the values.

    COUNT

    Count the number of values.

    CUSTOM

    Specify a custom callback function to do the aggregation.

    FIRST

    Substitute the first value encountered in the collection.

    MAX

    Calculate the maximum of the values.

    MIN

    Calculate the minimum of the values.

    NONE

    Substitute a null for the value.

    STDDEV

    Calculate the standard deviation of the values.

    SUM

    Total the values.

    VARIANCE

    Calculate the variance of the values.

    The code sample below shows the function that defines the dataValues for the cube used in this section. The cube will sum the units and sales data and will average the sales tax data.

    function generateCube(dataArr, axes) {
            return new oj.DataValueAttributeCube(dataArr, axes,
                    [{attribute:'units',aggregation:oj.CubeAggType['SUM']},
                        {attribute:'sales'},
                        {attribute:'tax',aggregation:oj.CubeAggType['AVERAGE']}]);
        };
    

The Oracle JET Cookbook demos at Data Grids (Cubic Data Source) contain the complete code for the example used in this section, including the complete code for setting the page axis and rendering the cell content. The oj.CubeDataGridDataSource API documentation also contains additional detail and methods that you can use for working with the cube.

Working with List Views

The Oracle JET ojListView component enhances the HTML list (ul) element to provide a themable, WAI-ARIA compliant component that displays a list of data. ojListView supports single and multiple selection, high water mark scrolling when working with table data, and hierarchical content.

Topics:

Understanding Data Requirements for List Views

The data source for the ojListView component can be one of the following:

  • Flat or hierarchical static HTML

    The following image shows examples of list views that display static content, one with flat content and one using a nested list for hierarchical content.

    The following code sample shows a portion of the markup used to create the ojListView component with hierarchical static content and the Oracle JET component binding. The component allows multiple selection and expands both list item groups A and B upon initial display.

    <ul id="listview" aria-label="list with static hierarchical data" 
                                 data-bind="ojComponent: {component: 'ojListView',
                                 selectionMode: 'multiple',
                                 expanded: ['a', 'b', 'c']}",
                                 item: {selectable: itemOnly}}">
       <li id="a">
        <span>A</span>
        <ul>
          <li id="amybartlet">
            <span class="avatar" style="background-image: url('images/Amy.png')"></span>
            <div class="container">
              <div>
                <span class="name">Amy Bartlet</span>
              </div>
              <div>
                <span class="oj-text-xs oj-text-secondary-color">Vice President</span>
              </div>
            </div>
          </li>
          ... contents omitted
        </ul>
      </li>
      <li id="b">
        <span>B</span>
        <ul>
        ... contents omitted
        </ul>
      </li>
    </ul>
    

    Tip:

    ojListView uses the children of the root ul element for its content. If you have an existing HTML list in your application, you can add the ojComponent binding as shown above and take advantage of ojListView features.

    The code to apply the binding is shown below.

    require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'promise', 'ojs/ojlistview'],
    function(oj, ko, $)
    {
      $(document).ready(
        function() 
          {
            ko.applyBindings({itemOnly: function(context)
                    {
                        return context['leaf'];
                    }
                }, document.getElementById('listview'));
          }
        );
    });     
    

    See List View Using Static Data for the complete example to create ojListView components using static data and static hierarchical data.

  • Oracle JET TreeDataSource, including oj.JsonTreeDataSource and oj.CollectionTreeDataSource.

    Typically, you use one of the Oracle JET TreeDataSource components when your list data contains groups, or you want to create an index. To use a TreeDataSource, you specify the method that returns the tree data in the data option for the component.

    data-bind="ojComponent: {component: 'ojListView', data: datasource,
                             selectionMode: 'multiple',
                             expanded: ['a', 'b']}">
    

    The Oracle JET Cookbook contains the complete example for creating a list view with an oj.JsonTreeDataSource at List View Using Hierarchical JSON Data. You can also find an example for creating a list view with an oj.JsonTreeDataSource that also contains an index at Indexer.

  • Oracle JET TableDataSource, including oj.ArrayTableDataSource, oj.CollectionTableDataSource, and oj.PagingTableDataSource

    Use the TableDataSource data option when you have flat data or want to add scrolling or pagination to your ojListView. The following image shows a list view using an oj.CollectionTableDataSource object for its data.

    In this example, high water mark scrolling is enabled through the component's root attributes scrollPolicy.

    data-bind="ojComponent: {component: 'ojListView', data: dataSource,
                             selectionMode: 'single',
                             rootAttributes: {style: 'width:100%;height:300px;overflow:auto'},
                             scrollPolicy: 'loadMoreOnScroll'}">
    

    For the complete example, including the script that creates the oj.CollectionTableDataSource object, see List View Using oj.Collection.

    You can also find cookbook examples that add Pull to Refresh and Swipe to Reveal touch capability to an ojListView created with the oj.CollectionTableDataSource object.

Note:

If you do not specify a data source in the list view component's data option, Oracle JET will examine the child elements inside the root element and use it for static content. If the root element has no children, Oracle JET will render an empty list.

Working with List Views and Knockout Templates

You can use a Knockout template to contain the markup for your list item content and reference the name of the template in the ojListView component's item option.

The code sample below shows a portion of the markup and template for a list view using an oj.ArrayTableDataSource object for its data. In this example, the template is named server_template.

<ul id="listview" aria-label="list using array"
    data-bind="ojComponent: {component: 'ojListView', data: dataSource, item: {template: 'server_template'}, selectionMode: 'single'}">
</ul>
 
<script type="text/html" id="server_template">
  <li data-bind="attr: {id: $data['name']}">
    <div class="oj-flex">
      <div class="oj-lg-2 oj-md-2 oj-sm-12" style="max-width:70px;padding-bottom:5px">
        <img data-bind="attr: {src: 'images/listView/'+ name +'.png', alt: name}"></img>
      </div>
      <div class="oj-lg-10 oj-md-10 oj-sm-12">
        <div class="oj-flex row">
          <div class="oj-lg-12 ">
            <span class="header" data-bind="text: 'MyWebLogic '+name"></span>
          </div>
        </div>
        ... content omitted
      </div>
    </div>
  </li>
</script>

The template uses the Oracle JET responsive grid classes to change the display from one column for smaller displays to multiple columns for larger displays.

You can find the complete code sample for this list view at: List Views.

For additional information about the Oracle JET responsive grid, see Designing Responsive Applications. For additional information about working with Knockout templates, see Using Knockout.js Templates and the ojModule Binding.

Working with Pagination

Use the ojPagingControl component to add pagination to the ojTable and ojDataGrid components or the HTML list element. Pagination displays the number of pages and rows in the table or grid, and the user can use pagination to move between pages, jump to a specific page, or navigate to the first or last page of data.

In the following image, ojPagingControl is added to the ojTable component and initialized with the default display.

To add pagination to a table, define the table's data as a PagingTableDataSource object, and add the ojPagingControl component using the same PagingTableDataSource object for its data option. Specify the number of rows to display in the ojPagingControl component's pageSize option. The code sample below shows the markup defining the table and pagination components. In this example, pageSize is set to 10.

<div id="pagingControlDemo" style="width: 500px;">
  <table id="table" summary="Department List" aria-label="Departments Table"
         data-bind="ojComponent: {component: 'ojTable', data: pagingDatasource, columns:
          [{headerText: 'Department Id', field: 'DepartmentId'},
           {headerText: 'Department Name', field: 'DepartmentName'},
           {headerText: 'Location Id', field: 'LocationId'},
           {headerText: 'Manager Id', field: 'ManagerId'}],
         rootAttributes: {'style':'width: 100%;'}}">
  </table>
  <div class="oj-table-panel-bottom">
    <div id="paging" data-bind="ojComponent: {component: 'ojPagingControl', data: pagingDatasource, pageSize: 10}">
    </div>
  </div>
</div>

The script that populates the pagingDatasource with data and completes the Knockout binding is shown below. In this example, the table's data is defined in an ArrayTableDataSource object, and the pagingDatasource defines the PagingTableDataSource as a new ArrayTableDataSource object.

require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/ojtable', 'ojs/ojpagingcontrol', 'ojs/ojpagingtabledatasource', 'ojs/ojarraytabledatasource'],
function(oj, ko, $)
{
  function viewModel()
  {
    var self = this;
    var deptArray = [{DepartmentId: 10015, DepartmentName: 'ADFPM 1001 neverending', LocationId: 200, ManagerId: 300},
        {DepartmentId: 556, DepartmentName: 'BB', LocationId: 200, ManagerId: 300},
        {DepartmentId: 10, DepartmentName: 'Administration', LocationId: 200, ManagerId: 300},
        {DepartmentId: 20, DepartmentName: 'Marketing', LocationId: 200, ManagerId: 300},
        ... contents omitted
        {DepartmentId: 13022, DepartmentName: 'Human Resources15', LocationId: 200, ManagerId: 300}];
    self.pagingDatasource = new oj.PagingTableDataSource(new oj.ArrayTableDataSource(deptArray, {idAttribute: 'DepartmentId'}));
  }
  var vm = new viewModel;
 
  $(document).ready
  (
    function()
    {
      ko.applyBindings(vm, document.getElementById('pagingControlDemo'));
    }
  );
});

To add a paging control to ojDataGrid, define the data grid's data as a oj.PagingDataGridDataSource object, and add the ojPagingControl component using the same oj.PagingDataGridDataSource object for its data option. Set the pageSize option equal to the fetch size for the data collection, if creating the data grid from an oj.CollectionDataGridDataSource object.

The Oracle JET Cookbook contains complete examples for adding pagination to ojTable, ojDataGrid, and HTML lists at Pagination. You can also find the link to the ojPagingControl API documentation as well as examples that show different options for customizing the paging display.

For additional information about working with the ojTable component, see Working with Tables. For more information about working with the ojDataGrid component, see Working with Data Grids.

Working with Row Expanders

Use the Oracle JET ojRowExpander component to expand or collapse rows in a data grid or table to display hierarchical data. ojRowExpander renders the expand/collapse icon with the appropriate indentation and works directly with the flattened data source. In the following image, the row expander is used with an ojTable component, and the user can expand the tasks to display subtasks and dates.

To use ojRowExpander with ojTable, add the ojRowExpander component to the HTML markup, and specify the table's data in an oj.FlattenedTreeTableDataSource object. In the code sample below, the ojRowExpander is defined in the table's row template. The context option references the object obtained from the table's column renderer.

<table id="table" summary="Task List" aria-label="Tasks Table"
       data-bind="ojComponent:
       {
           component: 'ojTable',
           data: datasource,
           rowTemplate: 'row_template',
           columns:
               [{headerText: 'Task Name', sortProperty: 'name'},
               {headerText: 'Resource', sortProperty: 'resource'},
               {headerText: 'Start Date', sortProperty: 'start'},
               {headerText: 'End Date', sortProperty: 'end'}]}">
</table>
 
<script type="text/html" id="row_template">
    <tr>
      <td>
        <div data-bind="ojComponent: {
                        component: 'ojRowExpander',
                        context: $rowContext}"></div>
        <span data-bind="text: name"></span>
      </td>
      <td>
        <span data-bind="text: resource"></span>
      </td>
      <td>
        <span data-bind="text: start"></span>
      </td>
      <td>
        <span data-bind="text: end"></span>
      </td>
    </tr>
</script>

The data for the oj.FlattenedTreeTableDataSource object can come from local or fetched JSON, or an oj.Collection object. In the example in this section, the data is read from a JSON file. The code sample below shows a portion of the JSON.

[
    {"attr": {"id": "t1",
              "name": "Task 1",
              "resource": "Larry",
              "start": "1/1/2014",
              "end": "10/1/2014"
             },
     "children": [
                     {"attr": {"id": "t1:1",
                               "name": "Task 1-1",
                               "resource": "Larry",
                               "start": "1/1/2014",
                               "end": "3/1/2014"
                              },
                      "children": [
                                      {"attr": {"id": "t1:1:1",
                                                "name": "Task 1-1-1",
                                                "resource": "Larry",
                                                "start": "1/1/2014",
                                                "end": "2/1/2014"
                                               }
                                      },
                                      {"attr": {"id": "t1:1:2",
                                                "name": "Task 1-1-2",
                                                "resource": "Larry",
                                                "start": "2/1/2014",
                                                "end": "3/1/2014"
                                               }
                                      }
                                  ]
                     },
                     ... contents omitted
                 ]
    },
    ... contents omitted
    {"attr": {"id": "t4",
              "name": "Task 4",
              "resource": "Larry",
              "start": "11/1/2014",
              "end": "12/1/2014"
             }
    }
]

The script that reads the JSON file and defines the datasource object as an oj.FlattenedTreeTableDataSource is shown below.

require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/ojtable', 'ojs/ojrowexpander', 'ojs/ojflattenedtreedatagriddatasource', 'ojs/ojjsontreedatasource'],
  function(oj, ko, $)
  {
    $(document).ready(
      function()
      {
        $.getJSON("cookbook/dataCollections/rowExpander/tableRowExpander/projectData.json",
        function(data)
        {
          var options = [];
          var datasource =
            new oj.FlattenedTreeTableDataSource(new oj.FlattenedTreeDataSource(new oj.JsonTreeDataSource(data), options));
            ko.applyBindings({datasource:datasource}, document.getElementById('table'));
        });
      }
    );
  });

To use the row expander with ojDataGrid, add the ojRowExpander component to the HTML markup, and specify the data grid's data in an oj.FlattenedTreeDataGridDataSource object. The Oracle JET Cookbook at Row Expanders contains an example that uses the row expander with ojDataGrid. The cookbook also contains the complete code for the example in this section and a link to the API documentation for ojRowExpander. In addition, you can find examples that use an oj.Collection object for the table's data and initialize the row expander with one or more rows expanded.

For additional information about working with the ojTable component, see Working with Tables. For more information about working with the ojDataGrid component, see Working with Data Grids.

Working with Tables

The Oracle JET ojTable component enhances the HTML table element to provide support for accessibility, custom cell and row templates and renderers, theming, row expansion, pagination, and editable array and collection tables.

You can define the table's data in an array using the oj.ArrayTableDataSource object or in a Collection using the oj.CollectionTableDataSource object. The code sample below shows the markup for the ojTable component shown in this example.

<table id="table" summary="Department List" aria-label="Departments Table" 
data-bind="ojComponent: {component: 'ojTable', data: datasource, columnsDefault: {sortable: 'none'}, columns:
       [{headerText: 'Department Id', field: 'DepartmentId'},
       {headerText: 'Department Name', field: 'DepartmentName'},
       {headerText: 'Location Id', field: 'LocationId'},
       {headerText: 'Manager Id', field: 'ManagerId'}]}">
</table>

Note:

The summary attribute on the table element describes the purpose of the table and is an accessibility requirement. Alternatively, you can add the caption child tag to the table element. See the ojTable API documentation for details about accessibility and supported keystrokes and gestures.

The script that defines the datasource object and completes the component binding is shown below. For the sake of brevity, some table data is omitted.

require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'promise', 'ojs/ojtable', 'ojs/ojarraytabledatasource'],
function(oj, ko, $)
{   
  function viewModel()
  {
    var self = this;
 
    var deptArray = [{DepartmentId: 1001, DepartmentName: 'ADFPM 1001 neverending', LocationId: 200, ManagerId: 300},
        {DepartmentId: 556, DepartmentName: 'BB', LocationId: 200, ManagerId: 300},
        {DepartmentId: 10, DepartmentName: 'Administration', LocationId: 200, ManagerId: 300},
        {DepartmentId: 20, DepartmentName: 'Marketing', LocationId: 200, ManagerId: 300},
        ... contents omitted
        {DepartmentId: 130, DepartmentName: 'Human Resources15', LocationId: 200, ManagerId: 300}];
    self.datasource = new oj.ArrayTableDataSource(deptArray, {idAttribute: 'DepartmentId'});
  }
  var vm = new viewModel;
  
  $(document).ready
  (
    function()
    {
      ko.applyBindings(vm, document.getElementById('table'));
    }
  );
});     

The Tables demos in the Oracle JET Cookbook include the complete example for this table and a link to the ojTable API documentation. The cookbook also includes an example that creates the ojTable component using data defined in an oj.CollectionTableDataSource object and examples that show tables with custom row and cell templates, selection, sorting, reordering, scrolling, custom cell renderers, and drag and drop support.

Understanding ojTable and Sorting

ojTable enables single column sorting by default if the underlying data supports sorting. Using the sortable attribute of the ojTable component's columnDefaults option, you can control sorting for the entire table, or you can use the sortable attribute of the columns option to control sorting on specific columns.

When you configure a column for sorting, the column header displays arrow indicators to indicate that the column is sortable when the user hovers over the column header. In the following image, the Department ID is sortable, and the sort indicator is showing a down arrow to indicate that the sort is currently descending. The user can select the down arrow to change the sort back to ascending.

The sortable attribute supports the following options:

  • auto: Sort the table or indicated column if the underlying data supports sorting.

  • none: Disable sorting on the table or indicated column.

  • enabled: Enable sorting on the entire table or indicated column.

To enable sorting on specific columns:

  • Set the sortable attribute to none on the table's columnDefaults option to remove the auto default behavior.

  • Set the sortable attribute to enabled on the columns that you want to sort.

The following code sample shows the markup to create the department table shown in this section. In this example, the table is configured to enable sorting on only the Department Id column.

<table id="table" summary="Department List" aria-label="Departments Table"
data-bind="ojComponent: {component: 'ojTable', data: datasource, columnsDefault: {sortable: 'none'}, columns:
       [{headerText: 'Department Id', field: 'DepartmentId', sortable: 'enabled'},
       {headerText: 'Department Name', field: 'DepartmentName'},
       {headerText: 'Location Id', field: 'LocationId'},
       {headerText: 'Manager Id', field: 'ManagerId'}]}">
</table>

ojTable sorting uses standard JavaScript array sorting. If your application requires custom sorting, you can use oj.ArrayTableDataSource and its comparator property. For an example, see Tables - Custom Sort.

For additional information about ojTable and sorting options, see the ojTable API documentation.

For examples in the Oracle JET Cookbook that implement tables and table sorting, see Tables.

Working with Trees

The ojTree component displays hierarchical data, such as hierarchical directory structures or categorical data. Each element in the tree is called a node, and the top levels of the hierarchy are referred to as the root nodes. The descendents of the root nodes are its children, and each child node can also contain children. Users select a node to display its children.

In this example, the expanded Links node is a root node, and the Oracle, IBM, and Microsoft nodes are its children. The Microsoft child node contains the USA, Europe, and Asia nodes which also contain child nodes. The USA node is expanded to show its North, South, East, and West child nodes.

To create the ojTree, add the HTML div element to your markup and define the ojTree component on that element. Assign an id to the div and optional styling for width and height.

<div id="tree" style="width: 180px;height: auto;"
               data-bind="ojComponent:
               {
                 component: 'ojTree',
                 selectionMode: 'multiple',
                 data:{data: getJson}
               }">
</div>

To define the data for the tree, you can use standard HTML list elements (ul, li) or provide the data in JSON format. In this example, the tree uses JSON data which is defined in the getJson() function, shown below. The sample code also shows the Knockout applyBindings() call to complete the component binding.

require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/ojtree'],
  function(oj, ko, $)
  {
    $(document).ready(
      function()
      {
        ko.applyBindings(null, document.getElementById('tree'));
      }
    );
  });   

function getJson(node, fn) // get local json
{
  var data = [
             { 
               "title": "News",
               "attr": {"id": "news"}
             },
             { 
               "title": "Blogs",
               "attr": {"id": "blogs"},
               "children": [ { "title": "Today",
                               "attr": {"id": "today"}
                             },
                             { "title": "Yesterday",
                               "attr": {"id": "yesterday"}
                             },
                             { "title": "Archive",
                               "attr": {"id": "archive"}
                             }
                           ]
             },
             {
               "title": "Links", 
               "attr": {"id": "links"},
               "children": [ { "title": "Oracle",
                               "attr": {"id": "oracle"}
                             },
                             { "title": "IBM",
                               "attr": {"id": "ibm"}
                             },
                             { "title": "Microsoft",
                               "attr": {"id": "ms"},
                               "children": [ { "title": "USA",
                                               "attr": {"id": "msusa"},
                                               "children": [ { "title": "North",
                                                               "attr": {"id": "msusanorth"}
                                                             },
                                                             { "title": "South",
                                                               "attr": {"id": "msusasouth"}
                                                             },
                                                             { "title": "East",
                                                               "attr": {"id": "msusaeast"}
                                                             },
                                                             { "title": "West",
                                                               "attr": {"id": "msusawest"}
                                                             }
                                                           ]
                                             },
                                             { "title": "Europe",
                                               "attr": {"id": "msuerope"}
                                             },
                                             { "title": "Asia",
                                               "attr": {"id": "msasia"},
                                     ... contents omitted
             ];
  fn(data) ;  // pass to ojTree using supplied function
};

The Oracle JET Cookbook contains the complete recipe and code for the sample used in this section at Trees. The cookbook also includes a link to the ojTree API documentation and examples for creating a tree using the HTML list elements, lazy loading a tree for performance, adding and removing nodes dynamically, moving and copying nodes between trees, customizing the node icons, displaying a context menu, and creating a tree using JSON data defined in a JsonTreeDataSource object.