# DocumentList Component for Angular 2

<p>
  <a title='Build Status Travis' href="https://travis-ci.org/Alfresco/alfresco-ng2-components">
    <img src='https://travis-ci.org/Alfresco/alfresco-ng2-components.svg?branch=master'  alt='travis
    Status' />
  </a>
  <a title='Build Status AppVeyor' href="https://ci.appveyor.com/project/alfresco/alfresco-ng2-components">
    <img src='https://ci.appveyor.com/api/projects/status/github/Alfresco/alfresco-ng2-components'  alt='travis
    Status' />
  </a>
  <a href='https://codecov.io/gh/Alfresco/alfresco-ng2-components'>
    <img src='https://img.shields.io/codecov/c/github/Alfresco/alfresco-ng2-components/master.svg?maxAge=2592000' alt='Coverage Status' />
  </a>
  <a href='https://www.npmjs.com/package/ng2-alfresco-documentlist'>
    <img src='https://img.shields.io/npm/dt/ng2-alfresco-documentlist.svg' alt='npm downloads' />
  </a>
  <a href='https://github.com/Alfresco/alfresco-ng2-components/blob/master/LICENSE'>
     <img src='https://img.shields.io/hexpm/l/plug.svg' alt='license' />
  </a>
  <a href='https://www.alfresco.com/'>
     <img src='https://img.shields.io/badge/style-component-green.svg?label=alfresco' alt='alfresco component' />
  </a>
  <a href='https://angular.io/'>
     <img src='https://img.shields.io/badge/style-2-red.svg?label=angular' alt='angular 2' />
  </a>
  <a href='https://www.typescriptlang.org/docs/tutorial.html'>
     <img src='https://img.shields.io/badge/style-lang-blue.svg?label=typescript' alt='typescript' />
  </a>
  <a href='https://www.alfresco.com/'>
     <img src='https://img.shields.io/badge/style-%3E5.0.0-blue.svg?label=node%20version' alt='node version' />
  </a>
</p>

## Prerequisites

Before you start using this development framework, make sure you have installed all required software and done all the
necessary configuration [prerequisites](https://github.com/Alfresco/alfresco-ng2-components/blob/master/PREREQUISITES.md).

## See also

- [Walkthrough: adding indicators to clearly highlight information about a node](docs/metadata-indicators.md)

## Install

Follow the 3 steps below:

1. Npm

    ```sh
    npm install ng2-alfresco-documentlist --save
    ```

2. Html

    Include these dependencies in your index.html page:

    ```html

      <!-- Google Material Design Lite -->
      <link rel="stylesheet" href="node_modules/material-design-lite/material.min.css">
      <script src="node_modules/material-design-lite/material.min.js"></script>
      <link rel="stylesheet" href="node_modules/material-design-icons/iconfont/material-icons.css">

      <!-- Load the Angular Material 2 stylesheet -->
      <link href="node_modules/@angular/material/core/theming/prebuilt/deeppurple-amber.css" rel="stylesheet">

      <!-- Polyfill(s) for Safari (pre-10.x) -->
      <script src="node_modules/intl/dist/Intl.min.js"></script>
      <script src="node_modules/intl/locale-data/jsonp/en.js"></script>

      <!-- Polyfill(s) for older browsers -->
      <script src="node_modules/core-js/client/shim.min.js"></script>
      <script src="//cdnjs.cloudflare.com/ajax/libs/dom4/1.8.3/dom4.js"></script>
      <script src="node_modules/element.scrollintoviewifneeded-polyfill/index.js"></script>

      <!-- Polyfill(s) for dialogs -->
      <script src="node_modules/dialog-polyfill/dialog-polyfill.js"></script>
      <link rel="stylesheet" type="text/css" href="node_modules/dialog-polyfill/dialog-polyfill.css" />
      <style>._dialog_overlay { position: static !important; } </style>

      <!-- Modules  -->
      <script src="node_modules/zone.js/dist/zone.js"></script>
      <script src="node_modules/reflect-metadata/Reflect.js"></script>
      <script src="node_modules/systemjs/dist/system.src.js"></script>

    ```

3. SystemJs

    Add the following components to your systemjs.config.js file:

    - ng2-translate
    - alfresco-js-api
    - ng2-alfresco-core
    - ng2-alfresco-datatable
    - ng2-alfresco-documentlist

    Please refer to the following example file: [systemjs.config.js](demo/systemjs.config.js) .


## Basic usage

```html
<alfresco-document-list
    #documentList
    [currentFolderId]="'-my-'"
    [contextMenuActions]="true"
    [contentActions]="true"
    [creationMenuActions]="true">
</alfresco-document-list>
```

Usage example of this component :

**main.ts**

```ts
import { NgModule, Component, ViewChild } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { CoreModule } from 'ng2-alfresco-core';
import { DocumentListModule, DocumentList } from 'ng2-alfresco-documentlist';
import { AlfrescoSettingsService, AlfrescoAuthenticationService } from 'ng2-alfresco-core';

@Component({
    selector: 'alfresco-app-demo',
    template: `
        <alfresco-document-list
            #documentList
            [currentFolderId]="'-my-'"
            [contextMenuActions]="true"
            [contentActions]="true"
            [creationMenuActions]="true">
        </alfresco-document-list>
    `
})
class DocumentListDemo {

    @ViewChild(DocumentList)
    documentList: DocumentListComponent;

    constructor(private authService: AlfrescoAuthenticationService, 
                private settingsService: AlfrescoSettingsService) {
        settingsService.ecmHost = 'http://localhost:8080';

        this.authService.login('admin', 'admin').subscribe(
            ticket => {
                console.log(ticket);
                this.documentList.reload();
            },
            error => {
                console.log(error);
            });
    }
}

@NgModule({
    imports: [
        BrowserModule,
        CoreModule.forRoot(),
        DocumentListModule.forRoot()
    ],
    declarations: [DocumentListDemo],
    bootstrap: [DocumentListDemo]
})
export class AppModule {
}

platformBrowserDynamic().bootstrapModule(AppModule);
```

### Properties

The properties currentFolderId, folderNode and node are the entry initialization properties of the document list. They can not be used together, choose the one that suites more your use case.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `selectionMode` | string | 'single' | Row selection mode. Can be none, `single` or `multiple`. For `multiple` mode you can use Cmd (macOS) or Ctrl (Win) modifier key to toggle selection for multiple rows.  |
| `rowStyle` | string | | The inline style to apply to every row, see [NgStyle](https://angular.io/docs/ts/latest/api/common/index/NgStyle-directive.html) docs for more details and usage examples |
| `rowStyleClass` | string | | The CSS class to apply to every row |
| `currentFolderId` | string | null | Initial node ID of displayed folder. Can be `-root-`, `-shared-`, `-my-`, or a fixed node ID  |
| `folderNode` | `MinimalNodeEntryEntity` | null | Currently displayed folder node | 
| `node` | `NodePaging` | null | Document list will show all the node contained in the NodePaging entity  | 
| `navigate` | boolean | true | Toggles navigation to folder content or file preview |
| `navigationMode` | string (click,dblclick) | dblclick | User interaction for folder navigation or file preview |
| `thumbnails` | boolean | false | Show document thumbnails rather than icons |
| `fallbackThumbnail` | string |  | Path to fallback image to use if the row thumbnail is missing |
| `multiselect` | boolean | false | Toggles multiselect mode |
| `contentActions` | boolean | false | Toggles content actions for each row |
| `contentActionsPosition` | string (left\|right) | right | Position of the content actions dropdown menu. |
| `contextMenuActions` | boolean | false | Toggles context menus for each row |
| `enablePagination` | boolean | true | Shows pagination |
| `creationMenuActions` | boolean | true | Toggles the creation menu actions |
| `rowFilter` | `RowFilter` | | Custom row filter, [see more](#custom-row-filter). |
| `imageResolver` | `ImageResolver` | | Custom image resolver, [see more](#custom-image-resolver). |
| `allowDropFiles` | boolean | false | Toggle file drop support for rows (see **ng2-alfresco-core/UploadDirective** for more details) |
| `sorting` | string[] | | Defines default sorting. The format is an array of 2 strings `[key, direction]` i.e. `['name', 'desc']` or `['name', 'asc']`. Set this value only if you want to override default sorting detected by the component based on columns. |

### Events

| Name | Description |
| --- | --- |
| `nodeClick` | Emitted when user clicks the node |
| `nodeDblClick` | Emitted when user double-clicks the node |
| `folderChange` | Emitted upon display folder changed |
| `preview` | Emitted when document preview is requested either with single or double click |

_For a complete example source code please refer to 
[DocumentList Demo](https://github.com/Alfresco/alfresco-ng2-components/tree/master/ng2-components/ng2-alfresco-documentlist/demo) 
repository._

### DOM Events

Below are the DOM events the DocumentList component emits. 
All of them are `bubbling`, meaning you can handle them in any component up the parent hierarchy, even if DocumentList is wrapped by another component(s).

| Name | Description |
| --- | --- |
| node-click | Raised when user clicks the node |
| node-dblclick | Raised when user double-clicks the node |

Every event is represented by a [CustomEvent](https://developer.mozilla.org/en/docs/Web/API/CustomEvent) instance, having at least the following properties as part of the `Event.detail` property value:

```ts
{
    sender: DocumentListComponent,
    node: MinimalNodeEntity
}
```

#### Handling DOM events

Here's a basic example on handling DOM events in the parent elements:

```html
<div (node-click)="onNodeClicked($event)" 
     (node-dblclick)="onNodeDblClicked($event)">
    <div>
        <alfresco-upload-drag-area ...>
             <alfresco-document-list ...>
                ...
             </alfresco-document-list>
        </alfresco-upload-drag-area>
    </div>
</div>
```

### Setting default folder

You can set current folder path by assigning a value for `currentFolderId` property. 
It can be either one of the well-known locations as **-root-**, **-shared-** or **-my-** or a node ID (guid).

There may be scenarios when it is needed to set default path based on relative string value rather than node ID.
For example when folder name or path is static but it's underlying ID is not (i.e. created manually by admin).
In this case you can use `alfresco-js-api` to get node details based on it's relative path.

Let's try setting default folder to `/Sites/swsdp/documentLibrary` without knowing it's ID beforehand.
For the sake of simplicity example below shows only main points you may need paying attention to:
 
```ts
import { ChangeDetectorRef } from '@angular/core';
import { AlfrescoApiService } from 'ng2-alfresco-core';

export class FilesComponent implements OnInit {

    currentFolderId: string = '-my-';

    constructor(private apiService: AlfrescoApiService,
                private changeDetector: ChangeDetectorRef) {
        // ...
    }

    ngOnInit() {
        let nodes: any = this.apiService.getInstance().nodes;
        nodes.getNodeInfo('-root-', {
            includeSource: true,
            include: ['path', 'properties'],
            relativePath: '/Sites/swsdp/documentLibrary'
        })
        .then(node => {
            console.log(node);
            this.currentFolderId = node.id;
            this.changeDetector.detectChanges();
        });
    }
}
```

We've added `console.log(node)` for the `getNodeInfo` callback just for study and debug purposes. 
It helps examining other valuable information you can have access to if needed:

![documentLibrary](docs/assets/documentLibrary.png)

**Important note**: for this particular scenario you must also trigger `changeDetector.detectChanges()` as in the example above. 

### Calling DocumentList api directly

Typically you will be binding DocumentList properties to your application/component class properties:

```html
<alfresco-document-list [currentFolderId]="myStartFolder"></alfresco-document-list>
```

with the underlying class being implemented similar to the following one:

```ts
@Component(...)
export class MyAppComponent {

    myStartFolder: string = '-my-';
    
}
```

However there may scenarios that require you direct access to DocumentList apis. 
You can get reference to the DocumentList instance by means of Angular **Component Interaction** API.
See more details in [Parent calls a ViewChild](https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#parent-to-view-child) 
section of the official docs.

Here's an example of getting reference:

```html
<alfresco-document-list 
    #documentList
    [currentFolderId]="myStartFolder">
</alfresco-document-list>
```

Note the `#documentList` ID we've just added to be able referencing this component later on.

```ts
import { ViewChild, AfterViewInit } from '@angular/core';
import { DocumentListComponent } from 'ng2-alfresco-documentlist';

@Component(...)
export class MyAppComponent implements AfterViewInit {

    myStartFolder: string = '-my-';
    
    @ViewChild(DocumentListComponent)
    documentList: DocumentListComponent;

    ngAfterViewInit() {
        console.log(this.documentList);
    }
}
```

Example above should produce the following browser console output:

![view-child](docs/assets/viewchild.png)

Now you are able accessing DocumentList properties or calling methods directly.

```ts
// print currently displayed folder node object to console
console.log(documentList.folderNode);
```

**Important note**:  
It is important accessing child components at least at the `AfterViewInit` state. 
Any UI click (buttons, links, etc.) event handlers are absolutely fine. This cannot be `ngOnInit` event though.
You can get more details in [Component lifecycle hooks](https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html) article.

### Breadcrumb Component

DocumentList provides simple breadcrumb element to indicate the current position within a navigation hierarchy.

```html
<alfresco-document-list-breadcrumb
    [target]="documentList"
    [folderNode]="documentList.folderNode">
</alfresco-document-list-breadcrumb>
```

![Breadcrumb](docs/assets/breadcrumb.png)

#### Properties

| Name | Type | Description |
| --- | --- | --- |
| `target` | DocumentListComponent | DocumentList component to operate with. Upon clicks will instruct the given component to update. |
| `folderNode` | MinimalNodeEntryEntity | Active node, builds UI based on `folderNode.path.elements` collection. |

### Creation Menu Action

DocumentList provides simple creation menu actions that provide the action to create a new folder.

```html
<alfresco-document-menu-action 
    [folderId]="folderId">
</alfresco-document-menu-action>
```

![Creation Menu Action](docs/assets/document-list-creation-menu-actions-1.png)

When the "New Folder" button is pressed the dialog appears.

![Creation Menu Action](docs/assets/document-list-creation-menu-actions-2.png)


### Custom columns

It is possible to reorder, extend or completely redefine data columns displayed by the component.
By default special `$thumbnail` and `displayName` columns are rendered.

A custom set of columns can look like the following:

```html
<alfresco-document-list ...>
    <content-columns>
        <content-column key="$thumbnail" type="image"></content-column>
        <content-column 
            title="Name" 
            key="name" 
            sortable="true"
            class="full-width ellipsis-cell">
        </content-column>
        <content-column 
            title="Created By" 
            key="createdByUser.displayName"
            sortable="true"
            class="desktop-only">
        </content-column>
        <content-column 
            title="Created On" 
            key="createdAt" 
            type="date" 
            format="medium"
            sortable="true"
            class="desktop-only">
        </content-column>
    </content-columns>
</alfresco-document-list>
```

![Custom columns](docs/assets/custom-columns.png)

You can also use HTML-based schema declaration used by DataTable, TaskList and other components:

```html
<alfresco-datatable [data]="data" ...>
    <data-columns>
        <data-column type="image" key="icon" [sortable]="false"></data-column>
        <data-column key="id" title="Id"></data-column>
        <data-column key="createdOn" title="Created"></data-column>
        <data-column key="name" title="Name" class="full-width name-column"></data-column>
        <data-column key="createdBy.name" title="Created By"></data-column>
    </data-columns>
</alfresco-datatable>
```

#### DataColumn Properties

Here's the list of available properties you can define for a Data Column definition.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| key | string | | Data source key, can be either column/property key like `title` or property path like `createdBy.name` |
| type | string (text\|image\|date) | text | Value type |
| format | string | | Value format (if supported by components), for example format of the date |
| sortable | boolean | true | Toggles ability to sort by this column, for example by clicking the column header |
| title | string | | Display title of the column, typically used for column headers |
| template | `TemplateRef` | | Custom column template |
| sr-title | string | | Screen reader title, used for accessibility purposes |
| class | string | | Additional CSS class to be applied to column (header and cells) |

DocumentList component assigns an instance of `MinimalNode` type (`alfresco-js-api`) as a data context of each row.

```js
export interface MinimalNode {
    id: string;
    parentId: string;
    name: string;
    nodeType: string;
    isFolder: boolean;
    isFile: boolean;
    modifiedAt: Date;
    modifiedByUser: UserInfo;
    createdAt: Date;
    createdByUser: UserInfo;
    content: ContentInfo;
    path: PathInfoEntity;
    properties: NodeProperties;
}
```

_See more details in [alfresco-js-api](https://github.com/Alfresco/alfresco-js-api/blob/master/index.d.ts) repository._

Binding to nested properties is also supported. You can define a column key as a property path similar to the following:

```text
createdByUser.displayName
```

Here's a short example:

```html
<alfresco-document-list ...>
    <content-columns>
        <content-column key="$thumbnail" type="image"></content-column>
        <content-column title="Name" key="name" class="full-width ellipsis-cell"></content-column>
        <content-column 
            title="Created By" 
            key="createdByUser.displayName">
        </content-column>
    </content-columns>
</alfresco-document-list>
```

### Column definition

Properties:

| Name | Type | Default | Description
| --- | --- | --- | --- |
| `title` | string | | Column title |
| `sr-title` | string | | Screen reader title, used only when `title` is empty |
| `key` | string | | Column source key, example: `createdByUser.displayName` |
| `sortable` | boolean | false | Toggle sorting ability via column header clicks |
| `class` | string | | CSS class list, example: `full-width ellipsis-cell` |
| `type` | string | text | Column type, text\|date\|number |
| `format` | string | | Value format pattern |
| `template` | `TemplateRef<any>` | | Column template |

For `date` column type the [DatePipe](https://angular.io/docs/ts/latest/api/common/DatePipe-class.html) formatting is used.
For a full list of available `format` values please refer to [DatePipe](https://angular.io/docs/ts/latest/api/common/DatePipe-class.html) documentation.

#### Column Template

It is possible providing custom column/cell template that may contain other Angular components or HTML elmements:

Every cell in the DataTable component is bound to the dynamic data context containing the following properties:

| Name | Type | Description |
| --- | --- | --- |
| data | [DataTableAdapter](https://github.com/Alfresco/alfresco-ng2-components/tree/master/ng2-components/ng2-alfresco-datatable#data-sources) | Data adapter instance. |
| row | [DataRow](https://github.com/Alfresco/alfresco-ng2-components/tree/master/ng2-components/ng2-alfresco-datatable#data-sources) | Current data row instance.  |
| col | [DataColumn](https://github.com/Alfresco/alfresco-ng2-components/tree/master/ng2-components/ng2-alfresco-datatable#data-sources) | Current data column instance. |

You can use all three properties to gain full access to underlying data from within your custom templates. 
In order to wire HTML templates with the data context you will need defining a variable that is bound to `$implicit` like shown below:

```html
<template let-context="$implicit">
    <!-- template body -->
</template>
```

The format of naming is `let-VARIABLE_NAME="$implicit"` where `VARIABLE_NAME` is the name of the variable you want binding template data context to.

Getting a cell value from the underlying DataTableAdapter:

```ts
context.data.getValue(entry.row, entry.col);
```

You can retrieve all property values for underlying node, including nested properties (via property paths):

```ts
context.row.getValue('name')
context.row.getValue('createdByUser.displayName')
```

You may want using **row** api to get raw value access.

```html
<content-column title="Name" key="name" sortable="true" class="full-width ellipsis-cell">
    <template let-context="$implicit">
        <span>Hi! {{context.row.getValue('createdByUser.displayName')}}</span>
        <span>Hi! {{context.row.getValue('name')}}</span>
    </template>
</content-column>
```

Use **data** api to get values with post-processing, like datetime/icon conversion._

In the Example below will prepend `Hi!` to each file and folder name in the list: 

```html
<content-column title="Name" key="name" sortable="true" class="full-width ellipsis-cell">
    <template let-entry="$implicit">
        <span>Hi! {{entry.data.getValue(entry.row, entry.col)}}</span>
    </template>
</content-column>
```

In the Example below will add the [ng2-alfresco-tag](https://www.npmjs.com/package/ng2-alfresco-tag) component is integrate in the document list.

```html
<content-column
    title="{{'DOCUMENT_LIST.COLUMNS.TAG' | translate}}"
    key="id"
    sortable="true"
    class="full-width ellipsis-cell">
    <template let-entry="$implicit">
        <alfresco-tag-node-list  [nodeId]="entry.data.getValue(entry.row, entry.col)"></alfresco-tag-node-list>
    </template>
</content-column>
```

![Tag component in document List](docs/assets/document-list-tag-template.png)

### Actions

DocumentList supports declarative actions for Documents and Folders.
Each action can be bound to either default out-of-box handler or a custom behavior.
You can define both folder and document actions at the same time.

#### Menu actions

```html
<alfresco-document-list ...>
    <content-actions>

        <content-action
            target="document"
            title="System action"
            handler="system2">
        </content-action>

        <content-action
            target="document"
            title="Custom action"
            (execute)="myCustomAction1($event)">
        </content-action>

    </content-actions>
</alfresco-document-list>
```

```ts
export class MyView {
    // ...

    myCustomAction1(event) {
        let entry = event.value.entry;
        alert(`Custom document action for ${entry.name}`);
    }
}
```

All document actions are rendered as a dropdown menu as on the picture below:

![Document Actions](docs/assets/document-actions.png)


#### Default action handlers

The following action handlers are provided out-of-box:

- **Download** (document)
- **Delete** (document, folder)

All system handler names are case-insensitive, `handler="download"` and `handler="DOWNLOAD"`
will trigger the same `download` action.

##### Delete - Show notification message with no permission
You can show a notification error when the user don't have the right permission to perform the action.
The ContentActionComponent provides the event permissionEvent that is raised when the permission specified in the permission property is missing
You can subscribe to this event from your component and use the NotificationService to show a message.

```html
<alfresco-document-list ...>
    <content-actions>

        <content-action
            target="document"
            title="Delete"
            permission="delete"
            (permissionEvent)="onPermissionsFailed($event)"
            handler="delete">
        </content-action>

    </content-actions>
</alfresco-document-list>


export class MyComponent {

onPermissionsFailed(event: any) {
    this.notificationService.openSnackMessage(`you don't have the ${event.permission} permission to ${event.action} the ${event.type} `, 4000);
}

}
```

![Delete show notification message](docs/assets/content-action-notification-message.png)

##### Delete - Disable button checking the permission
You can easily disable a button when the user doesn't own the permission to perform the action related to the button.
The ContentActionComponent provides the property permission that must contain the permission to check and a property disableWithNoPermission that can be true if
 you want see the button disabled.

```html
<alfresco-document-list ...>
    <content-actions>

        <content-action
            target="document"
            title="Delete"
            permission="delete"
            disableWithNoPermission="true"
            handler="delete">
        </content-action>

    </content-actions>
</alfresco-document-list>
```

![Delete disable action button](docs/assets/content-action-disable-delete-button.png)

##### Download

Initiates download of the corresponding document file.

```html
<alfresco-document-list ...>
    <content-actions>

        <content-action
            target="document"
            title="Download"
            handler="download">
        </content-action>

    </content-actions>
</alfresco-document-list>
```

![Download document action](docs/assets/document-action-download.png)

#### Folder actions

Folder actions have the same declaration as document actions except ```taget="folder"``` attribute value.

```html
<alfresco-document-list ...>
    <content-actions>

        <content-action
            target="folder"
            title="Default folder action 1"
            handler="system1">
        </content-action>

        <content-action
            target="folder"
            title="Custom folder action"
            (execute)="myFolderAction1($event)">
        </content-action>

    </content-actions>
</alfresco-document-list>
```

```ts
export class MyView {
    // ...

    myFolderAction1(event) {
        let entry = event.value.entry;
        alert(`Custom folder action for ${entry.name}`);
    }
}
```

![Folder Actions](docs/assets/folder-actions.png)

### Context Menu

DocumentList also provide integration for 'Context Menu Service' from the 
[ng2-alfresco-core](https://www.npmjs.com/package/ng2-alfresco-core) library.

You can automatically turn all menu actions (for the files and folders) 
into context menu items like shown below:

![Folder context menu](docs/assets/folder-context-menu.png)

Enabling context menu is very simple:

```ts
@Component({
    selector: 'my-view',
    template: `
        <alfresco-document-list>...</alfresco-document-list>
        <context-menu-holder></context-menu-holder>
    `
})
export class MyView {
}
```

This enables context menu items for documents and folders.

### Navigation mode

By default DocumentList component uses 'double-click' mode for navigation.
That means user will see the contents of the folder by double-clicking its name
or icon (similar to Google Drive behaviour). However it is possible switching to 
other modes, like single-click navigation for example.
 
The following navigation modes are supported:

- **click**
- **dblclick**

The following example switches navigation to single clicks:

```html
<alfresco-document-list [navigationMode]="'click'">
</alfresco-document-list>
```

### Events

DocumentList emits the following events:

| Name | Description |
| --- | --- |
| `nodeClick` | emitted when user clicks a list node |
| `nodeDblClick` | emitted when user double-clicks list node |
| `folderChange` | emitted once current display folder has changed |
| `preview` | emitted when user acts upon files with either single or double click (depends on `navigation-mode`), recommended for Viewer components integration  |
| `permissionError` | emitted when user is attempting to create a folder via action menu but it doesn't have the permission to do it |

## Advanced usage and customization

### Custom row filter

You can create a custom row filter implementation that returns `true` if row should be displayed or `false` to hide it.
Typical row filter implementation is a function that receives `ShareDataRow` as parameter:

```ts
myFilter(row: ShareDataRow): boolean {
    return true;
}
```

_Note that for the sake of simplicity the example code below was reduced to the main points of interest only._

**View1.component.html**
```html
<alfresco-document-list 
    [rowFilter]="folderFilter">
</alfresco-document-list>
```

**View1.component.ts**
```ts

import { RowFilter, ShareDataRow } from 'ng2-alfresco-documentlist';

export class View1 {

    folderFilter: RowFilter;

    constructor() {
    
        // This filter will make document list show only folders
        
        this.folderFilter = (row: ShareDataRow) => {
            let node = row.node.entry;
            
            if (node && node.isFolder) {
                return true;
            }
            
            return false;
        };
    }
}
```

### Custom image resolver

You can create a custom image resolver implementation and take full control over how folder/file icons and thumbnails 
are resolved and what document list should display. 

**Image resolvers are executed only for columns of the `image` type.**

Typical image resolver implementation is a function that receives `DataRow` and `DataColumn` as parameters:

```ts
myImageResolver(row: DataRow, col: DataColumn): string {
    return '/path/to/image';
}
```

Your function can return `null` or `false` values to fallback to default image resolving behavior.

_Note that for the sake of simplicity the example code below was reduced to the main points of interest only._

**View1.component.html**
```html
<alfresco-document-list 
    [imageResolver]="folderImageResolver">
    
    <content-columns>
        <content-column key="name" type="image"></content-column>
    </content-columns>
    
    
</alfresco-document-list>
```

**View1.component.ts**
```ts
import { DataColumn, DataRow } from 'ng2-alfresco-datatable';
import { ImageResolver } from 'ng2-alfresco-documentlist';

export class View1 {

    folderImageResolver: ImageResolver;
    
    constructor() {
        
        // Customize folder icons, leave file icons untouched
        
        this.folderImageResolver = (row: DataRow, col: DataColumn) => {
            let isFolder = <boolean> row.getValue('isFolder');
            if (isFolder) {
                
                // (optional) You may want dynamically getting the column value
                let name = row.getValue(col.key);
                
                // Format image url
                return `http://<my custom path to folder icon>/${name}`;
            }
            
            // For the rest of the cases just fallback to default behaviour.
            return null;
        };
        
    }

}
```

### Hiding columns on small screens

You can hide columns on small screens by means of custom CSS rules:

```css
@media all and (max-width: 768px) {

    alfresco-document-list >>> th.desktop-only .cell-value {
        display: none;
    }

    alfresco-document-list >>> td.desktop-only .cell-value {
        display: none;
    }
}
```

Now you can declare columns and assign `desktop-only` class where needed:

```html
<alfresco-document-list ...>
    <content-columns>
        
        <!-- always visible columns -->
        
        <content-column key="$thumbnail" type="image"></content-column>
        <content-column 
                title="Name" 
                key="name" 
                class="full-width ellipsis-cell">
        </content-column>
        
        <!-- desktop-only columns -->
        
        <content-column
                title="Created by"
                key="createdByUser.displayName"
                class="desktop-only">
        </content-column>
        <content-column
                title="Created on"
                key="createdAt"
                type="date"
                format="medium"
                class="desktop-only">
        </content-column>
    </content-columns>
</alfresco-document-list>
```

**Desktop View**

![Responsive Desktop](docs/assets/responsive-desktop.png)

**Mobile View**

![Responsive Mobile](docs/assets/responsive-mobile.png)

### Custom 'empty folder' template

By default DocumentList provides the following content for the empty folder:

![Default empty folder](docs/assets/empty-folder-template-default.png)

This can be changed by means of the custom html template:

```html
<alfresco-document-list ...>
    <empty-folder-content>
        <template>
            <h1>Sorry, no content here</h1>
        </template>
    </empty-folder-content>
</alfresco-document-list>
```

That will give the following output:

![Custom empty folder](docs/assets/empty-folder-template-custom.png)

### Customizing default actions

It is possible extending or replacing the list of available system actions for documents and folders.
Actions for the documents and folders can be accessed via the following services:

- `DocumentActionsService`, document action menu and quick document actions
- `FolderActionsService`, folder action menu and quick folder actions

Example below demonstrates how a new action handler can be registered with the
`DocumentActionsService`.

```html
<alfresco-document-list ...>
    <content-actions>

        <content-action
            target="document"
            title="My action"
            handler="my-handler">
        </content-action>

    </content-actions>
</alfresco-document-list>
```

You register custom handler called `my-handler` that will be executing `myDocumentActionHandler`
function each time upon being invoked.

```ts
import { DocumentActionsService } from 'ng2-alfresco-documentlist';

export class MyView {

    constructor(documentActions: DocumentActionsService) {
        documentActions.setHandler(
            'my-handler',
            this.myDocumentActionHandler.bind(this)
        );
    }

    myDocumentActionHandler(obj: any) {
        window.alert('my custom action handler');
    }
}
```

The same approach allows changing the way out-of-box action handlers behave.
Registering custom action with the name `download` replaces default one:

```ts
export class MyView {

    constructor(documentActions: DocumentActionsService) {
        documentActions.setHandler(
            'download',
            this.customDownloadBehavior.bind(this)
        );
    }

    customDownloadBehavior(obj: any) {
        window.alert('my custom download behavior');
    }
}
```

Typically you may want populating all your custom actions at the application root level or
by means of custom application service.

## Build from sources

Alternatively you can build component from sources with the following commands:


```sh
npm install
npm run build
```

## Running unit tests

```sh
npm test
```

### Running unit tests in browser

```sh
npm test-browser
```

This task rebuilds all the code, runs tslint, license checks and other quality check tools
before performing unit testing.

### Code coverage

```sh
npm run coverage
```

## Demo

If you want have a demo of how the component works, please check the demo folder :

```sh
cd demo
npm install
npm start
```

## NPM scripts

| Command | Description |
| --- | --- |
| npm run build | Build component |
| npm run test | Run unit tests in the console |
| npm run test-browser | Run unit tests in the browser
| npm run coverage | Run unit tests and display code coverage report |

## License

[Apache Version 2.0](https://github.com/Alfresco/alfresco-ng2-components/blob/master/LICENSE)
