This article is a deep dive into how an ADF application is configured, built, and run. Specifically for ADF version 2.0.0. It's very much a one-time-only type of read that you would do in the beginning of your first ADF project. It will mostly likely benefit you a lot in the long run to know what's in this article. When you are on top of the stuff in this article you would most likely use generators, or clone a GitHub project, when starting a new ADF project.
This article is part of series of articles covering ADF 2.0:
In this section we will look at what ADF really is.
The Alfresco Application Development Framework, referred to as ADF, is built on top of the Angular 5 JavaScript framework. You can think of ADF as a library of Alfresco web components that can be used to build a content management web application and/or a process management web application.
There are a number of web components that you can use to integrate your web application with Alfresco Content Services (ACS). Here are some of these components:
And for integrating with Alfresco Process Services (APS) you have the following components:
There are also a number of generic components that are used with both ACS and APS:
These ADF components don’t talk directly to the ACS and APS backend services. There are some layers between them that are worth knowing about before you start hacking. The ADF components talk to ADF services, which in turn talks to the Alfresco JS API, which internally calls ACS and APS via their respective ReST APIs. You could use the both the ADF services and the Alfresco JS API directly from your application if there is no ADF component available to do what you want. In fact, you will quite frequently have to use the ADF services in your application to fetch content nodes, process instances, task instances etc.
The following picture illustrates the architecture of an ADF solution:
The ADF components and services are implemented in Angular, which in turn is implemented in TypeScript. The Alfresco JavaScript library is pure JavaScript and could be used with any other JavaScript framework.
Before we move on it is worth mentioning that there is an ADF application generator that can be very useful if you just want to quickly get going with an ADF project, such as for a demo or proof-of-concept scenario. It covers use cases for both ACS and APS. It can be used to generate the following types of ADF applications:
On the other hand, if you want to know how to create an Angular CLI application from scratch, and how to set it up to work with ADF version 2.0.0. And you want to be able to pick and choose what ADF components to use, building your custom content and/or process management interface, then read on.
Using the App Generator is simple. Install the Yeoman tool. Then install the App Generator as follows:
$ sudo npm install generator-alfresco-adf-app -g
Password:
+ generator-alfresco-adf-app@2.0.0
added 201 packages in 5.805s
Running the generator is easy:
$ yo
? 'Allo Martin! What would you like to do? (Use arrow keys)
Run a generator
❯ Alfresco Adf App
──────────────
Update your generators
Install a generator
Find some help
Clear global config
Select the 'Alfresco Adf App' generator and follow instructions.
Make sure you use the latest 2.0.0 version of the ADF App Generator as older ones will generate an application that maybe has more features than you would like when starting out. If you see the following:
martins-macbook-pro:ADF mbergljung$ yo
? 'Allo Martin! What would you like to do? (Use arrow keys)
Run a generator
❯ Ng2 Alfresco App
Alfresco Adf App
──────────────
Update your generators
Install a generator
Find some help
Then that means you got two ADF app generators installed, the new one (i.e. Alfresco Adf App) and the old one (Ng2 Alfresco App). Remove the old one as follows:
$ sudo npm uninstall -g generator-ng2-alfresco-app
This article is written with the assumption that you are starting out from scratch with your ADF project and that you are new to ADF development. If you have worked with Angular before that would be good, but it is not absolutely necessary.
The associated source code for this article can be found in this GitHub project. It is highly recommended that you have it available when walking through this tutorial. If for some reason you cannot get the code from this article working, then you can always go to the GitHub project for the source of truth.
In this section we will discuss how to install both ACS and APS so that we have them ready when working with any of the ADF components. You will need Alfresco Content Services version 5.2 or higher, Enterprise or Community edition. For APS you will need an Enterprise version 1.6 or higher (Activiti community edition is not yet supported). The reason you need these new versions is because the Alfresco JavaScript API wraps ReST APIs that require these versions.
If you intend to build a client that will only talk to an ACS server, then don’t install APS, and vice versa.
If you are on a Mac then download Community installer from Download Alfresco Community ECM Now | Alfresco
The easiest way to get going with APS is to start it up via Docker. However we will also look at how to install it with the out-of-the-box installer.
If you don't have Docker CE, download and follow official installation guides here.
When you got Docker installed it is easy to get up and running with APS 1.7.0:
$ docker container run -d -p 9080:8080 --name aps170 alfresco/process-services:1.7.0
This runs a new Docker container with the name aps164 containing APS version 1.6.4 that you can access on http://localhost:9080/activiti-app. The application that you will create in this article is preconfigured to expect APS to be running on port 9080. If you want to use a different port change the port number in the app file proxy.conf.json.
APS requires a license and when you first access it there will be a red information box about uploading a license. You can actually go through and request a trial license as described below, and then upload this license.
If you want to know what APS Docker Images that are available, then have a look here.
If your ADF application is only going to talk to ACS, then you don’t have to complete this part, just use the ACS credentials as per installation (e.g. admin/admin).
For the ADF Login component to work properly with both services they need to have the same username and password available. It is also important to make sure that the user has proper permissions within the ACS repository, so files and folders can be managed from ADF applications. The same thing is true for the APS user, which needs to have access to the process applications that should be accessed from the ADF application.
It is common to use admin/admin as username/password for ACS. In APS we will have admin@app.activiti.com/admin set up by default. In APS the email address for a user is also used as the username. It is not possible to create a username such as admin in APS.
So let’s use admin@app.activiti.com/admin as username/password in both services, which means we need to set up a new user in ACS:
Alfresco Application Development Framework (ADF) uses the Node.js JavaScript runtime and the npm package manager. So we need to make sure we have them installed with the correct versions. ADF version 2.0.0 requires Node.js version 8. When setting up an Angular project for ADF development it is recommended to use Angular CLI, so we will install that toolkit as well.
Martins-MacBook-Pro:~ martin$ node -v
v8.6.0
Martins-MacBook-Pro:~ martin$ npm -v
5.4.2
Angular CLI is a Command Line Interface (CLI) to automate your development workflow when working with Angular applications. It allows you to:
To install Angular CLI, run:
Angular CLI version 1.5.x need to be installed/used, such as 1.5.0 or 1.5.4. Version 1.6.0 does not work.
Martins-MacBook-Pro:~ martin$ npm install -g @angular/cli@1.5.4
Which will install the ng command globally on your system.
On Mac/Linux you might have to run as root with sudo npm install...
On Windows you might have to open a Command prompt and run as Administrator.
If you already have an earlier version of Angular CLI installed, then you can update to version 1.5.4 with:
$ npm uninstall -g @angular/cli
$ npm install -g @angular/cli@1.5.4
Be prepared that installing Angular CLI might take some time...
To verify whether your installation completed successfully, you can run ng version:
Martins-MacBook-Pro:~ martin$ ng version
_ _ ____ _ ___
/ \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
/ △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | |
/ ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
|___/
@angular/cli: 1.5.4
node: 8.6.0
os: darwin x64
Angular:
...
Now that you have Angular CLI installed, let’s use it to create a new application that we can prepare for ADF development.
To create a new app you use the ng new <app-name> command. It will create a new directory with the app-name and generate all the needed files in it.
The app should support routing between different components (call them pages if you like), so we will add the --routing option. This creates a separate file src/app/app-routing.module.ts. The file includes an empty Routes object that you can fill with routes to different components (i.e. pages). Routing capabilities will come in handy when you start developing a more realistic application that contains multiple pages.
We will also set up the project so it is prepared to use SASS. You will get style files ending in .scss (instead of .css) and in the .angular-cli.json configuration file it will set style extension as follows:
"defaults": {
"styleExt": "scss",
"component": {}
}
While executing the command stand in the directory where you want to create a subdirectory with the new application:
Martins-MacBook-Pro:ADF mbergljung$ ng new adf-workbench20 --routing --style=scss
create adf-workbench20/README.md (1030 bytes)
create adf-workbench20/.angular-cli.json (1252 bytes)
create adf-workbench20/.editorconfig (245 bytes)
create adf-workbench20/.gitignore (516 bytes)
create adf-workbench20/src/assets/.gitkeep (0 bytes)
create adf-workbench20/src/environments/environment.prod.ts (51 bytes)
create adf-workbench20/src/environments/environment.ts (387 bytes)
create adf-workbench20/src/favicon.ico (5430 bytes)
create adf-workbench20/src/index.html (301 bytes)
create adf-workbench20/src/main.ts (370 bytes)
create adf-workbench20/src/polyfills.ts (2667 bytes)
create adf-workbench20/src/styles.scss (80 bytes)
create adf-workbench20/src/test.ts (1085 bytes)
create adf-workbench20/src/tsconfig.app.json (211 bytes)
create adf-workbench20/src/tsconfig.spec.json (304 bytes)
create adf-workbench20/src/typings.d.ts (104 bytes)
create adf-workbench20/e2e/app.e2e-spec.ts (297 bytes)
create adf-workbench20/e2e/app.po.ts (208 bytes)
create adf-workbench20/e2e/tsconfig.e2e.json (235 bytes)
create adf-workbench20/karma.conf.js (923 bytes)
create adf-workbench20/package.json (1320 bytes)
create adf-workbench20/protractor.conf.js (722 bytes)
create adf-workbench20/tsconfig.json (363 bytes)
create adf-workbench20/tslint.json (3044 bytes)
create adf-workbench20/src/app/app-routing.module.ts (245 bytes)
create adf-workbench20/src/app/app.module.ts (395 bytes)
create adf-workbench20/src/app/app.component.scss (0 bytes)
create adf-workbench20/src/app/app.component.html (1173 bytes)
create adf-workbench20/src/app/app.component.spec.ts (1103 bytes)
create adf-workbench20/src/app/app.component.ts (208 bytes)
Installing packages for tooling via npm.
Installed packages for tooling via npm.
Successfully initialized git.
Project 'adf-workbench20' successfully created.
Be prepared that the 'Installing packages for tooling via npm' might take some time..., don't Ctrl-C out of it.
It is quite fast now though with node.js 8. It will populate the node_modules directory.
You can already see what files that have been created for the adf-workbench20 app. You can also list the directory to see what’s there:
Martins-MacBook-Pro:ADF mbergljung$ cd adf-workbench20/
Martins-MacBook-Pro:adf-workbench20 mbergljung$ ls -l
total 704
-rw-r--r-- 1 mbergljung staff 1030 28 Nov 13:59 README.md
drwxr-xr-x 5 mbergljung staff 170 28 Nov 13:59 e2e
-rw-r--r-- 1 mbergljung staff 923 28 Nov 13:59 karma.conf.js
drwxr-xr-x 810 mbergljung staff 27540 28 Nov 13:59 node_modules
-rw-r--r-- 1 mbergljung staff 335611 28 Nov 13:59 package-lock.json
-rw-r--r-- 1 mbergljung staff 1320 28 Nov 13:59 package.json
-rw-r--r-- 1 mbergljung staff 722 28 Nov 13:59 protractor.conf.js
drwxr-xr-x 14 mbergljung staff 476 28 Nov 13:59 src
-rw-r--r-- 1 mbergljung staff 363 28 Nov 13:59 tsconfig.json
-rw-r--r-- 1 mbergljung staff 3044 28 Nov 13:59 tslint.json
Behind the scenes, the following happens:
Let's quickly run through and explain what most of these directories and files mean:
It is preferred to work with the Angular application from a JavaScript IDE such as Visual Studio Code (Open Source) or WebStorm (Commercial):
The basic app module and app component have been generated, so you should now be able to run the app from the adf-workbench20 directory with the ng serve command:
Martins-MacBook-Pro:adf-workbench20 mbergljung$ ng serve
** NG Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **
Date: 2017-11-28T14:09:31.012Z
Hash: cc39d16e4e8227c5a775
Time: 5489ms
chunk {inline} inline.bundle.js (inline) 5.79 kB [entry] [rendered]
chunk {main} main.bundle.js (main) 24.4 kB [initial] [rendered]
chunk {polyfills} polyfills.bundle.js (polyfills) 557 kB [initial] [rendered]
chunk {styles} styles.bundle.js (styles) 35.3 kB [initial] [rendered]
chunk {vendor} vendor.bundle.js (vendor) 8.04 MB [initial] [rendered]
webpack: Compiled successfully.
Access the app from a browser via http://localhost:4200:
Not much to brag about, but it is a starting point!
Behind the scenes, the following happens:
Notice that the ng serve command does not exit and return to your terminal prompt after step 3. Instead, because it includes LiveReload support, the process actively watches your src directory for file changes. When a file change is detected, step 2 is repeated and a notification is sent to your browser so it can refresh automatically.
This also means that if you make any changes to stuff outside the src/ directory, such as .angular-cli.json, then you need to restart the server for them to take effect.
To stop the process and return to your prompt, press ctrl-c.
Angular CLI is tightly integrated with Webpack, so you will for example not see a webpack.config.js file in the same directory as package.json. Instead all config for things like webapp assets are done in Angular CLI’s config file, .angular-cli.json.
You might be wondering what Webpack is. It's an open-source module bundler for JavaScript applications. When Webpack processes your application, it recursively builds a dependency graph that includes every module your application needs, then packages all of those modules into a small number of bundles to be loaded by the browser.
Webpack is a very powerful module bundler. A bundle is a JavaScript file that incorporates assets that belong together and should be served to the client in a response to a single file request. A bundle can include JavaScript, CSS styles, HTML, and almost any other kind of file.
Webpack scans your application source code, looking for import statements (e.g. import { Component } from '@angular/core';), building a dependency graph, and emitting one or more bundles. With plugins and rules, Webpack can preprocess and minify different non-JavaScript files such as TypeScript, SASS, and LESS files.
You determine what Webpack does and how it does it with a JavaScript configuration file, webpack.config.js. Angular CLI uses Webpack under the hood, and all configuration for it is instead in .angular-cli.json. You will not see a webpack.config.js in an Angular CLI app.
You can see the bundles that are generated by Webpack for our app when we started it above:
The Angular CLI configuration is done in the .angular-cli.json file and it is important to understand the different parts of this file before moving on. We have already mentioned this file a couple of times and we know that the Webpack configuration is done via this file too, there is no webpack.config.json.
The following list explains the most important parts of the file:
As we can see, everything is controlled via this file.
Every ADF based application will of course need a lot of libraries and resources to run. To add these we update the following files in the adf-workbench20 application:
It is very important that the libraries we set up as dependencies for the application matches the library versions that the ADF Components expects to use. We will be using ADF version 2.0.0 so we need to find out what Angular version it expects to use, what Angular Material components version, etc. We can do this by using a utility called npm-remote-ls.
Install it like this:
Martins-MacBook-Pro:adf-workbench20 martin$ npm install -g npm-remote-ls
This installs this utility globally on your machine (-g), and not in the node_modules directory for the adf-workbench20 app. To find out the dependencies that ADF version 2.0.0 uses you can pick one of its libraries, such as @alfresco/adf-core, and run the tool on it:
Martins-MacBook-Pro:adf-workbench20 martin$ npm-remote-ls @alfresco/adf-core@2.0.0
└─ @alfresco/adf-core@2.0.0
├─ @angular/animations@5.0.0
│ └─ tslib@1.8.0
├─ @angular/compiler@5.0.0
│ └─ tslib@1.8.0
├─ @angular/http@5.0.0
│ └─ tslib@1.8.0
├─ @angular/cdk@5.0.0-rc0
│ └─ tslib@1.8.0
├─ @angular/flex-layout@2.0.0-beta.10
│ └─ tslib@1.8.0
├─ @angular/common@5.0.0
│ └─ tslib@1.8.0
├─ @angular/forms@5.0.0
│ └─ tslib@1.8.0
├─ @angular/core@5.0.0
│ └─ tslib@1.8.0
├─ @angular/material-moment-adapter@5.0.0-rc0
│ └─ tslib@1.8.0
├─ alfresco-js-api@2.0.0
│ ├─ event-emitter@0.3.4
│ │ ├─ d@0.1.1
│ │ │ └─ es5-ext@0.10.37
│ │ └─ es5-ext@0.10.37
│ │ ├─ es6-iterator@2.0.3
│ │ │ ├─ d@1.0.0
│ │ │ │ └─ es5-ext@0.10.37
│ │ │ ├─ es6-symbol@3.1.1
│ │ │ └─ es5-ext@0.10.37
│ │ └─ es6-symbol@3.1.1
│ │ ├─ d@1.0.0
│ │ └─ es5-ext@0.10.37
│ └─ superagent@3.4.1
│ ├─ extend@3.0.1
│ ├─ mime@1.6.0
│ ├─ form-data@2.3.1
│ │ ├─ combined-stream@1.0.5
│ │ │ └─ delayed-stream@1.0.0
│ │ ├─ asynckit@0.4.0
│ │ └─ mime-types@2.1.17
│ │ └─ mime-db@1.30.0
│ ├─ debug@2.6.9
│ │ └─ ms@2.0.0
│ ├─ methods@1.1.2
│ ├─ cookiejar@2.1.1
│ ├─ formidable@1.1.1
│ ├─ qs@6.5.1
│ ├─ component-emitter@1.2.1
│ └─ readable-stream@2.3.3
│ ├─ inherits@2.0.3
│ ├─ safe-buffer@5.1.1
│ ├─ core-util-is@1.0.2
│ ├─ util-deprecate@1.0.2
│ ├─ isarray@1.0.0
│ ├─ string_decoder@1.0.3
│ │ └─ safe-buffer@5.1.1
│ └─ process-nextick-args@1.0.7
├─ @angular/platform-browser-dynamic@5.0.0
│ └─ tslib@1.8.0
├─ hammerjs@2.0.8
├─ core-js@2.4.1
├─ chart.js@2.5.0
│ ├─ chartjs-color@2.2.0
│ │ ├─ chartjs-color-string@0.5.0
│ │ │ └─ color-name@1.1.3
│ │ └─ color-convert@0.5.3
│ └─ moment@2.19.3
├─ @angular/material@5.0.0-rc0
│ └─ tslib@1.8.0
├─ minimatch@3.0.4
│ └─ brace-expansion@1.1.8
│ ├─ balanced-match@1.0.0
│ └─ concat-map@0.0.1
├─ @ngx-translate/core@8.0.0
├─ moment@2.15.2
├─ raphael@2.2.7
│ └─ eve-raphael@0.5.0
├─ zone.js@0.8.14
├─ reflect-metadata@0.1.10
├─ rxjs@5.5.2
│ └─ symbol-observable@1.1.0
├─ systemjs@0.19.27
│ └─ when@3.7.8
├─ ng2-charts@1.6.0
│ └─ chart.js@2.7.1
│ ├─ chartjs-color@2.2.0
│ └─ moment@2.18.1
├─ @angular/platform-browser@5.0.0
│ └─ tslib@1.8.0
├─ pdfjs-dist@1.5.404
│ └─ node-ensure@0.0.0
└─ @angular/router@5.0.0
└─ tslib@1.8.0
Here we can immediately find out the expected versions for several important libraries:
We now have all the necessary version information to be able to configure the application with the necessary dependencies and resources so ADF version 2.0.0 will work smoothly.
To check dependencies for a beta version do for example:
Martins-MacBook-Pro:adf-workbench20 martin$ npm-remote-ls ng2-alfresco-core@2.0.0-beta3
In this section we will update a number of library/package versions in the generated package.json file.
Start by updating the generated application to use an absolute Angular version 5.0.0. Note here that we changed ^5.0.0 to 5.0.0, removing the caret, which otherwise will update you to the latest version.
Open package.json and update it to look as follows:
{
"name": "adf-workbench20",
"version": "0.0.0",
"license": "MIT",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "5.0.0",
"@angular/common": "5.0.0",
"@angular/compiler": "5.0.0",
"@angular/core": "5.0.0",
"@angular/forms": "5.0.0",
"@angular/http": "5.0.0",
"@angular/platform-browser": "5.0.0",
"@angular/platform-browser-dynamic": "5.0.0",
"@angular/router": "5.0.0",
"core-js": "^2.4.1",
"rxjs": "^5.5.2",
"zone.js": "^0.8.14"
},
...
The Core.js version is the same. Just need to remove the caret. Open package.json and update the dependencies section to look as follows:
{
"name": "adf-workbench20",
...
"private": true,
"dependencies": {
"@angular/animations": "5.0.0",
"@angular/common": "5.0.0",
"@angular/compiler": "5.0.0",
"@angular/core": "5.0.0",
"@angular/forms": "5.0.0",
"@angular/http": "5.0.0",
"@angular/platform-browser": "5.0.0",
"@angular/platform-browser-dynamic": "5.0.0",
"@angular/router": "5.0.0",
"core-js": "2.4.1",
"rxjs": "^5.5.2",
"zone.js": "^0.8.14"
},
...
The Reactive Extensions for JavaScript (RxJS) is a set of libraries to compose asynchronous and event-based programs using observable collections and Array#extras style composition in JavaScript.
The version of RxJS in the generated project is correct, we just need to remove the caret in front of it so we don't get any surprise updates that makes the app behave weird, update it as follows in package.json:
{
"name": "adf-workbench20",
...
"dependencies": {
"@angular/animations": "5.0.0",
"@angular/common": "5.0.0",
"@angular/compiler": "5.0.0",
"@angular/core": "5.0.0",
"@angular/forms": "5.0.0",
"@angular/http": "5.0.0",
"@angular/platform-browser": "5.0.0",
"@angular/platform-browser-dynamic": "5.0.0",
"@angular/router": "5.0.0",
"core-js": "2.4.1",
"rxjs": "5.5.2",
"zone.js": "^0.8.14"
},
...
To switch off numerous and unnecessary type warnings related to the RxJs library, update the root Typescript configuration (for the IDE) file adf-workbench20/tsconfig.json and the skipLibCheck and paths properties as follows:
{
"compileOnSave": false,
"compilerOptions": {
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es5",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2017",
"dom"
],
"skipLibCheck": true,
"paths": {
"rxjs/*": ["../node_modules/rxjs/*" ],
"@angular/*": ["../node_modules/@angular/*"]
}
}
}
The Zone library provides a way to represent the dynamic extent of asynchronous calls in Node. Just like the scope of a function defines where it may be used, the extent of a call represents the lifetime that is it active.
The version of Zone in the generated project is not correct, update it as follows in package.json:
{
"name": "adf-workbench20",
...
"dependencies": {
"@angular/animations": "5.0.0",
"@angular/common": "5.0.0",
"@angular/compiler": "5.0.0",
"@angular/core": "5.0.0",
"@angular/forms": "5.0.0",
"@angular/http": "5.0.0",
"@angular/platform-browser": "5.0.0",
"@angular/platform-browser-dynamic": "5.0.0",
"@angular/router": "5.0.0",
"core-js": "2.4.1",
"rxjs": "5.5.2",
"zone.js": "0.8.14"
},
...
In this section we will add a number of library/package versions to the generated package.json file.
The styling of the ADF Components is based on Google Material Design. To implement the ADF components so they follow Material Design styling the Angular Material Design components library is used. We need to install it and we can then also use these Angular Material components when building our UI.
Open up package.json and add the @angular/cdk and @angular/material library dependencies:
{
"name": "adf-workbench20",
...
"dependencies": {
"@angular/animations": "5.0.0",
"@angular/cdk": "5.0.0-rc0",
"@angular/common": "5.0.0",
"@angular/compiler": "5.0.0",
"@angular/core": "5.0.0",
"@angular/forms": "5.0.0",
"@angular/http": "5.0.0",
"@angular/material": "5.0.0-rc0",
"@angular/platform-browser": "5.0.0",
"@angular/platform-browser-dynamic": "5.0.0",
"@angular/router": "5.0.0",
"core-js": "2.4.1",
"rxjs": "5.5.2",
"zone.js": "0.8.14"
},
...
The Angular CDK is comprised of a bunch of services, directives, components, classes, modules, etc to make our lives easier when developing Angular components. The CDK actually provides general-purpose tools for building components that are not coupled to Material Design.
The ADF framework uses the Angular Flex Layout, which provides a sophisticated layout API using Flexbox CSS + mediaQuery. This module provides Angular (v4.1 and higher) developers with component layout features using a custom Layout API, mediaQuery observables, and injected DOM flexbox-2016 CSS stylings.
Bring in the library as follows in package.json:
{
"name": "adf-workbench20",
...
"dependencies": {
"@angular/animations": "5.0.0",
"@angular/cdk": "5.0.0-rc0",
"@angular/common": "5.0.0",
"@angular/compiler": "5.0.0",
"@angular/core": "5.0.0",
"@angular/flex-layout": "2.0.0-beta.10",
"@angular/forms": "5.0.0",
"@angular/http": "5.0.0",
"@angular/material": "5.0.0-rc0",
"@angular/platform-browser": "5.0.0",
"@angular/platform-browser-dynamic": "5.0.0",
"@angular/router": "5.0.0",
"core-js": "2.4.1",
"rxjs": "5.5.2",
"zone.js": "0.8.14"
},
...
The moment library provides Angular pipes for manipulating display of date and time. Add it as a dependency in package.json:
{
"name": "adf-workbench20",
...
"dependencies": {
"@angular/animations": "5.0.0",
"@angular/cdk": "5.0.0-rc0",
"@angular/common": "5.0.0",
"@angular/compiler": "5.0.0",
"@angular/core": "5.0.0",
"@angular/flex-layout": "2.0.0-beta.10",
"@angular/forms": "5.0.0",
"@angular/http": "5.0.0",
"@angular/material": "5.0.0-rc0",
"@angular/platform-browser": "5.0.0",
"@angular/platform-browser-dynamic": "5.0.0",
"@angular/router": "5.0.0",
"core-js": "2.4.1",
"moment-es6": "1.0.0",
"moment": "2.15.2",
"rxjs": "5.5.2",
"zone.js": "0.8.14"
},
...
We can see here that we also add another moment related library called moment-es6. This module exports momentjs library object as ES6 style default field.
For moment to work we need to load the JavaScript file for it in .angular-cli.json as follows:
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"name": "adf-workbench20"
},
"apps": [
{
...
"scripts": [
"../node_modules/moment/min/moment.min.js"
],
...
This script will be loaded exactly as if you had added it via a <script> tag inside index.html. Note also that any stylesheet file specified in the styles section will be loaded exactly as if you added it in a <link> tag inside index.html. You might be tempted to just specify these in the index.html file directly, doing this will not get them packaged up into a bundle by webpack.
The apps[0].styles will end up in the dist/styles.bundle.js bundle file and the apps[0].scripts will end up in the dist/scripts.bundle.js bundle file.
Ngx-Translate is an internationalization library for Angular 2+. It lets you define translations for your content in different languages and switch between them easily. Add it as a dependency in package.json:
{
"name": "adf-workbench20",
...
"dependencies": {
"@angular/animations": "5.0.0",
"@angular/cdk": "5.0.0-rc0",
"@angular/common": "5.0.0",
"@angular/compiler": "5.0.0",
"@angular/core": "5.0.0",
"@angular/flex-layout": "2.0.0-beta.10",
"@angular/forms": "5.0.0",
"@angular/http": "5.0.0",
"@angular/material": "5.0.0-rc0",
"@angular/platform-browser": "5.0.0",
"@angular/platform-browser-dynamic": "5.0.0",
"@angular/router": "5.0.0",
"@ngx-translate/core": "8.0.0",
"core-js": "2.4.1",
"moment-es6": "1.0.0",
"moment": "2.15.2",
"rxjs": "5.5.2",
"zone.js": "0.8.14"
},
There are several components in ADF that displays diagrams and charts. The backing libraries for these components are Raphael, Chart.js, Ng2-Charts. Add the necessary dependencies in package.json as follows:
{
"name": "adf-workbench20",
...
"dependencies": {
"@angular/animations": "5.0.0",
"@angular/cdk": "5.0.0-rc0",
"@angular/common": "5.0.0",
"@angular/compiler": "5.0.0",
"@angular/core": "5.0.0",
"@angular/flex-layout": "2.0.0-beta.10",
"@angular/forms": "5.0.0",
"@angular/http": "5.0.0",
"@angular/material": "5.0.0-rc0",
"@angular/platform-browser": "5.0.0",
"@angular/platform-browser-dynamic": "5.0.0",
"@angular/router": "5.0.0",
"@ngx-translate/core": "8.0.0",
"chart.js": "2.5.0",
"core-js": "2.4.1",
"moment-es6": "1.0.0",
"moment": "2.15.2",
"ng2-charts": "1.6.0",
"raphael": "2.2.7",
"rxjs": "5.5.2",
"zone.js": "0.8.14"
},
The Raphael library requires that a JS file is loaded. Set this up in .angular-cli.json as follows:
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"name": "adf-workbench20"
},
"apps": [
{
...
"scripts": [
"../node_modules/raphael/raphael.min.js",
"../node_modules/moment/min/moment.min.js"
],
The document viewer component in ADF uses a library called PDFJS. Add the dependency in package.json:
{
"name": "adf-workbench20",
...
"dependencies": {
"@angular/animations": "5.0.0",
"@angular/cdk": "5.0.0-rc0",
"@angular/common": "5.0.0",
"@angular/compiler": "5.0.0",
"@angular/core": "5.0.0",
"@angular/flex-layout": "2.0.0-beta.10",
"@angular/forms": "5.0.0",
"@angular/http": "5.0.0",
"@angular/material": "5.0.0-rc0",
"@angular/platform-browser": "5.0.0",
"@angular/platform-browser-dynamic": "5.0.0",
"@angular/router": "5.0.0",
"@ngx-translate/core": "8.0.0",
"chart.js": "2.5.0",
"core-js": "2.4.1",
"moment-es6": "1.0.0",
"moment": "2.15.2",
"ng2-charts": "1.6.0",
"pdfjs-dist": "1.5.404",
"raphael": "2.2.7",
"rxjs": "5.5.2",
"zone.js": "0.8.14"
},
The PDFJS library requires that some Assets and JS files are loaded. Set this up in .angular-cli.json as follows:
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"name": "adf-workbench20"
},
"apps": [
{
"root": "src",
"outDir": "dist",
"assets": [
"assets",
"favicon.ico",
{
"glob": "pdf.worker.js",
"input": "../node_modules/pdfjs-dist/build",
"output": "./"
}
],
...
"scripts": [
"../node_modules/pdfjs-dist/build/pdf.js",
"../node_modules/pdfjs-dist/web/pdf_viewer.js",
"../node_modules/raphael/raphael.min.js",
"../node_modules/moment/min/moment.min.js"
],
...
The PDFJS library also need to be initialized when the application is loaded via the main entry point. Update the src/main.ts file as follows:
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import 'hammerjs';
import 'chart.js';
import 'ng2-charts';
import pdfjsLib from 'pdfjs-dist';
pdfjsLib.PDFJS.workerSrc = 'pdf.worker.js';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.log(err));
We take the opportunity to add some other libraries too at this point (i.e. hammer.js, chart.js, and ng2-charts).
The following libraries are also used by ADF:
Add them to package.json as follows:
{
"name": "adf-workbench20",
...
"dependencies": {
"@angular/animations": "5.0.0",
"@angular/cdk": "5.0.0-rc0",
"@angular/common": "5.0.0",
"@angular/compiler": "5.0.0",
"@angular/core": "5.0.0",
"@angular/flex-layout": "2.0.0-beta.10",
"@angular/forms": "5.0.0",
"@angular/http": "5.0.0",
"@angular/material": "5.0.0-rc0",
"@angular/platform-browser": "5.0.0",
"@angular/platform-browser-dynamic": "5.0.0",
"@angular/router": "5.0.0",
"@ngx-translate/core": "8.0.0",
"chart.js": "2.5.0",
"classlist.js": "1.1.20150312",
"core-js": "2.4.1",
"custom-event-polyfill": "0.3.0",
"hammerjs": "2.0.8",
"intl": "1.2.5",
"minimatch": "3.0.4",
"moment-es6": "1.0.0",
"moment": "2.15.2",
"ng2-charts": "1.6.0",
"pdfjs-dist": "1.5.404",
"raphael": "2.2.7",
"reflect-metadata": "0.1.10",
"rxjs": "5.5.2",
"web-animations-js": "2.3.1",
"zone.js": "0.8.14"
},
By default the generated project will have the typescript version configured in a way such as this:
"typescript": "~2.4.2"
This is not ideal as the tilde character in front of it means that whenever a patch version is released by Microsoft, such as version 2.4.3, our project could be updated with the new version without us realising it. This can be bad news as this could effect some ADF component, and it could take a while before we realise what is going on. So remove the tilde and specify it as follows instead in the devDependencies section:
...
"devDependencies": {
"@angular/cli": "1.5.4",
"@angular/compiler-cli": "5.0.0",
"@angular/language-service": "5.0.0",
"@types/jasmine": "~2.5.53",
"@types/jasminewd2": "~2.0.2",
"@types/node": "~6.0.60",
"codelyzer": "^4.0.1",
"jasmine-core": "~2.6.2",
"jasmine-spec-reporter": "~4.1.0",
"karma": "~1.7.0",
"karma-chrome-launcher": "~2.1.1",
"karma-cli": "~1.0.1",
"karma-coverage-istanbul-reporter": "^1.2.1",
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.1.2",
"ts-node": "~3.2.0",
"tslint": "~5.7.0",
"typescript": "2.4.2"
}
...
There are only a few ADF libraries that we need to add to the application to prepare it for development of a Content Management application or a Process Management application. At this point we can also configure Google Material Styles.
Alfresco ADF components uses the Alfresco JavaScript library to talk to ACS and APS. This JavaScript library basically abstracts the Content Services REST API and the Process Services REST API.
Add it as follows in package.json:
{
"name": "adf-workbench20",
...
"dependencies": {
"@angular/animations": "5.0.0",
"@angular/cdk": "5.0.0-rc0",
"@angular/common": "5.0.0",
"@angular/compiler": "5.0.0",
"@angular/core": "5.0.0",
"@angular/flex-layout": "2.0.0-beta.10",
"@angular/forms": "5.0.0",
"@angular/http": "5.0.0",
"@angular/material": "5.0.0-rc0",
"@angular/platform-browser": "5.0.0",
"@angular/platform-browser-dynamic": "5.0.0",
"@angular/router": "5.0.0",
"@ngx-translate/core": "8.0.0",
"alfresco-js-api": "2.0.0",
"chart.js": "2.5.0",
"classlist.js": "1.1.20150312",
"core-js": "2.4.1",
"custom-event-polyfill": "0.3.0",
"hammerjs": "2.0.8",
"intl": "1.2.5",
"minimatch": "3.0.4",
"moment-es6": "1.0.0",
"moment": "2.15.2",
"ng2-charts": "1.6.0",
"pdfjs-dist": "1.5.404",
"raphael": "2.2.7",
"reflect-metadata": "0.1.10",
"rxjs": "5.5.2",
"web-animations-js": "2.3.1",
"zone.js": "0.8.14"
},
...
This JS library can also be used directly in cases where there are no ADF components that fit the requirements. Note that the Alfresco JavaScript library follows the same versioning as the ADF, so we specify 2.0.0.
There are only a few ADF libraries that we need to add to the application to bring in all the ADF components and services:
Add these libs as follows in package.json:
{
"name": "adf-workbench20",
...
"dependencies": {
"@angular/animations": "5.0.0",
"@angular/cdk": "5.0.0-rc0",
"@angular/common": "5.0.0",
"@angular/compiler": "5.0.0",
"@angular/core": "5.0.0",
"@angular/flex-layout": "2.0.0-beta.10",
"@angular/forms": "5.0.0",
"@angular/http": "5.0.0",
"@angular/material": "5.0.0-rc0",
"@angular/platform-browser": "5.0.0",
"@angular/platform-browser-dynamic": "5.0.0",
"@angular/router": "5.0.0",
"@ngx-translate/core": "8.0.0",
"alfresco-js-api": "2.0.0",
"@alfresco/adf-core": "2.0.0",
"@alfresco/adf-content-services": "2.0.0",
"@alfresco/adf-insights": "2.0.1",
"@alfresco/adf-process-services": "2.0.0",
"chart.js": "2.5.0",
"classlist.js": "1.1.20150312",
"core-js": "2.4.1",
"custom-event-polyfill": "0.3.0",
"hammerjs": "2.0.8",
"intl": "1.2.5",
"minimatch": "3.0.4",
"moment-es6": "1.0.0",
"moment": "2.15.2",
"ng2-charts": "1.6.0",
"pdfjs-dist": "1.5.404",
"raphael": "2.2.7",
"reflect-metadata": "0.1.10",
"rxjs": "5.5.2",
"web-animations-js": "2.3.1",
"zone.js": "0.8.14"
},
...
Note that for the @alfresco/insight library we use version 2.0.1
At this point you might be thinking, why would we bring in all the components and services if we are not going to use all of them? For example, if we are going to build a Content Management application we will not need the Process Services components. We don't need to worry about this as Webpack does something called Tree Shaking that will remove all "dead code", so we will not have excess code in the final app artifact.
The styling of the ADF Components is based on Google Material Design. In this section we add the necessary styles, icons, JavaScript files etc.
We need to tell the app about the i18n resources, icon resources, etc that are embedded in the ADF libraries. This is done in the .angular-cli.json file in the assets section. We want these assets to be packaged and bundled up by the Webpack tool, making them available during runtime.
Add the new assets to the assets sections in the .angular-cli.json file:
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"name": "adf-workbench20"
},
"apps": [
{
"root": "src",
"outDir": "dist",
"assets": [
"assets",
"favicon.ico",
{
"glob": "**/*",
"input": "../resources",
"output": "./resources"
},
{
"glob": "**/*",
"input": "../node_modules/@alfresco/adf-core/prebuilt-themes",
"output": "./assets/prebuilt-themes"
},
{
"glob": "**/*",
"input": "../node_modules/@alfresco/adf-core/bundles/assets",
"output": "./assets/"
},
{
"glob": "**/*",
"input": "../node_modules/@alfresco/adf-insights/bundles/assets",
"output": "./assets/"
},
{
"glob": "**/*",
"input": "../node_modules/@alfresco/adf-process-services/bundles/assets",
"output": "./assets/"
},
{
"glob": "**/*",
"input": "../node_modules/@alfresco/adf-content-services/bundles/assets",
"output": "./assets/"
},
{
"glob": "pdf.worker.js",
"input": "../node_modules/pdfjs-dist/build",
"output": "./"
}
],
...
We can see here that the prebuilt ADF Google Material themes will be copied into, and available, in the /assets/prebuilt-themes directory. What we also do here in the app[0].assets section is to tell Angular CLI that all the assets, such as images and i18n resource files, that are located in the adf-core, adf-process-services, adf-insights, and adf-content-services packages should be copied into the ./assets directory.
Note that we are referencing the node_modules directory relatively from the src directory, not from the directory where the .angular-cli.json file is located.
And now add a new property called stylePreprocessorOptions to the .angular-cli.json file as follows:
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"name": "adf-workbench20"
},
"apps": [
{
...
"stylePreprocessorOptions": {
"includePaths": [
"../node_modules/"
]
}
}
],
...
In order to simplify style imports we also add style include paths via the stylePreprocessorOptions entry in .angular-cli.json. Files in that folder, for example, ./node_modules/@alfresco/adf-content-services/_theming.scss, can be imported from anywhere in your project without the need for a relative path:
/* src/app/app.component.scss
A relative path works */
@import '../node_modules/@alfresco/adf-content-services/_theming.scss';
/* But now this works as well */
@import '~@alfresco/adf-content-services/theming';
The styling of the ADF Components is based on Google Material Design. In this section we add the necessary styles and icons to the application.
The Google Material Design Icons should be loaded via the src/index.html file, update it as follows:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>ADF Workbench 2.0</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link href="https://fonts.googleapis.com/css?family=Muli" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body>
<app-root></app-root>
</body>
</html>
The Google Material Design Theme is set up in the src/styles.scss file as follows:
@import '~@alfresco/adf-core/prebuilt-themes/adf-blue-orange.css';
@import '~@alfresco/adf-content-services/theming';
@import '~@alfresco/adf-process-services/theming';
@import '~@alfresco/adf-core/theming';
@import '~@angular/material/theming';
@include mat-core($alfresco-typography);
$primary: mat-palette($alfresco-accent-orange);
$accent: mat-palette($alfresco-accent-purple);
$warn: mat-palette($alfresco-warn);
$theme: mat-light-theme($primary, $accent, $warn);
@include angular-material-theme($theme);
@include adf-content-services-theme($theme);
@include adf-process-services-theme($theme);
@include adf-core-theme($theme);
body, html {
margin: 0;
height: 100%;
overflow: hidden;
font-size: mat-font-size($alfresco-typography, body-1);
font-family: mat-font-family($alfresco-typography);
line-height: mat-line-height($alfresco-typography, body-1);
}
body {
overflow: auto;
}
As you can see from the above app[0].styles configuration the styles.scss file was already included in our .angular-cli.json file. This is where you add style information that is relevant to the entire application, such as the Google Material Design theme that the app will use. And so far, this is what you would expect. But, as you know, Angular 2 takes styling to the next level.
Assuming you have done a bit of Angular web development, you know that you can also add style information in your components. If you open the existing src/app/app.component.ts file, you will notice that there is both a templateUrl and a styleUrls attribute in the @Component() metadata section:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'app';
}
In the styleUrls property you can specify a list of relative URLs of CSS files to include.
If we include CSS information in the CSS files, you will see that when we render the page, the CSS gets rewritten so that the CSS is scoped to the component. Looking at an ADF component, such as the LoginComponent, we can see that this is used:
@Component({
selector: 'adf-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss'],
host: {'(blur)': 'onBlur($event)'},
encapsulation: ViewEncapsulation.None
})
export class LoginComponent implements OnInit {
It’s time to install all the libraries that we defined in package.json. We need to do this now otherwise it will be difficult to work with the components from our IDE as it would not know anything about the modules and classes we are trying to use. Basically we need to get the local /node_modules directory populated with all the new libraries that we configured.
Remove the node_modules directory and then run npm install to download all the extra libraries that we have defined in package.json to node_modules:
Martins-MacBook-Pro:adf-workbench20 mbergljung$ rm -rf node_modules/
Martins-MacBook-Pro:adf-workbench20 mbergljung$ npm install
...
Testing binary
Binary is fine
added 1110 packages in 15.43s
Martins-MacBook-Pro:adf-workbench20 mbergljung$ npm dedup
moved 1 package in 3.132s
After we have run npm install we also run npm dedup. This will remove any duplicate transitive dependencies. If a transitive dependency already exists it will not be downloaded again, but this depends on the order we have specified the libraries in package.json. So always good to do a dedup after an install.
You might be tempted to run npm update instead of npm install. This will also work but it changes package.json dependencies version specifications. It will add carets in front of version numbers, so not what we want. So be careful with this command.
List the node_modules directory if you want to find out all the downloaded libs:
Martins-MacBook-Pro:node_modules mbergljung$ ls -l|more
total 0
drwxr-xr-x 6 mbergljung staff 204 29 Nov 13:26 @alfresco
drwxr-xr-x 18 mbergljung staff 612 29 Nov 13:26 @angular
drwxr-xr-x 5 mbergljung staff 170 29 Nov 13:26 @angular-devkit
drwxr-xr-x 4 mbergljung staff 136 29 Nov 13:26 @ngtools
drwxr-xr-x 3 mbergljung staff 102 29 Nov 13:26 @ngx-translate
drwxr-xr-x 3 mbergljung staff 102 29 Nov 13:26 @schematics
drwxr-xr-x 7 mbergljung staff 238 29 Nov 13:26 @types
drwxr-xr-x 6 mbergljung staff 204 29 Nov 13:26 abbrev
drwxr-xr-x 7 mbergljung staff 238 29 Nov 13:26 accepts
drwxr-xr-x 9 mbergljung staff 306 29 Nov 13:26 acorn
...
Note here that the @alfresco/adf-core library has all its dependencies specified in its own package.json, as would be expected, and we call these transitive dependencies. As we saw earlier on, we can easily find out the dependencies for a library we use with the npm-remote-ls tool.
Now it's time to make sure that the package.json for our adf-workbench20 application has been configured with matching ADF 2.0 libraries. In your IDE, navigate into the node_modules directory and look up the @alfresco/adf-core library. You should see something like this:
We should not see any node_modules directory for the @alfresco/adf-core library if we have configured the app’s package.json correctly. On the other hand, if we have not configured the same library versions as ADF 2.0 expects, then we could see something like this:
In this case the core-js library has been configured with a different version in package.json than adf-core depends on, so it loads its own version. And this means that we could experience problems during runtime.
Make sure that the app you are building uses the same versions for all libraries that ADF depends on.
The ADF Core module provides all the services that the ADF components needs, such as AppConfigService, AuthenticationService, NodeService, AppsProcessService, SearchService etc. It also provides some common components, such as ToolbarComponent, UserInfoComponent, ViewerComponent, LoginComponent etc, which might be used frequently.
Open up the src/app/app.module.ts file and import the ADF CoreModule from the @alfresco/adf-core package:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CoreModule } from '@alfresco/adf-core';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
CoreModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
The ADF app needs to be configured with the location of the ACS and the APS servers. We don’t want to hardcode this in component code. Fortunately there is a file called app.config.json that can be used for this. The ADF framework will automatically look for it.
Create it with the following content and put it in the src directory:
{
"ecmHost": "http://{hostname}{:port}",
"bpmHost": "http://{hostname}{:port}",
"application": {
"name": "Alfresco ADF Appplication"
},
"languages": [
{
"key": "en",
"label": "English"
},
{
"key": "fr",
"label": "French"
},
{
"key": "de",
"label": "German"
},
{
"key": "it",
"label": "Italian"
},
{
"key": "es",
"label": "Spanish"
},
{
"key": "ja",
"label": "Japanese"
},
{
"key": "nl",
"label": "Dutch"
},
{
"key": "pt-BR",
"label": "Brazilian Portuguese"
},
{
"key": "nb",
"label": "Norwegian"
},
{
"key": "ru",
"label": "Russian"
},
{
"key": "zh-CN",
"label": "Simplified Chinese"
}
],
"logLevel": "trace"
}
Version 2.0 of the application development framework supports a number of languages, so we add them at the same time.
The app.config.json file is not loaded unless we configure it to be bundled as an asset. Open up .angular-cli.json and add it to the assets section as follows:
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"name": "adf-workbench20"
},
"apps": [
{
"root": "src",
"outDir": "dist",
"assets": [
"assets",
"favicon.ico",
"app.config.json",
{
"glob": "**/*",
"input": "../resources",
"output": "./resources"
},
...
],
...
You are probably wondering about the URLs in the app.config.json file. What values would be substituted for the hostname and port variables. At runtime the {hostname} and {port} variables will be replaced with the data from the running application, such as localhost and 4200. The ACS and the APS servers are obviously not running both on localhost:4200, only the app runs on this address, so how does that work. A Webpack web proxy is used as an intermediate, solving also the CORS problem. We need to add a file called adf-workbench20/proxy.conf.json with the following content:
{
"/alfresco": {
"target": "http://localhost:8080",
"secure": false,
"changeOrigin": true
},
"/activiti-app": {
"target": "http://localhost:9080",
"secure": false,
"changeOrigin": true
}
}
The target URLs need to match the installations we did earlier on for ACS (http://localhost:8080/alfresco) and APS (http://localhost:9080/activiti-app).
If you are using just one of the backend services, for example ACS. Then you don't need to worry about configuring the other one (e.g. /activiti-app). And vice versa.
Now, the proxy.conf.json file is not magically read by Webpack, we need to configure Webpack with the location of it. Open up .angular-cli.json and update the defaults section as follows:
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"name": "adf-workbench20"
},
...
"defaults": {
"styleExt": "scss",
"component": {
},
"serve": {
"proxyConfig": "proxy.conf.json",
"port": 4200
}
}
}
By using the Webpack proxy we don’t need to configure CORS in ACS or APS.
So what all this means is that a call to for example ACS will be using the http://localhost:4200/alfresco URL, which does not cause any CORS problems as the host and port are the same as where the app is running. This URL will then be rewritten via the Webpack proxy to http://127.0.0.1:8080/, where the ACS backend is running.
The first thing that we are going to need to do in our app is to login to Content Services and/or Process Services. We can do this with the ADF Login component. When we have a session with these services we can start using other components, such as the Document List. The login component is available in the @alfresco/adf-core library, which is already installed.
Every ADF component has extensive documentation available together with the source code in a .md markdown file. For example, if you navigate to the Login component docs you would see something like this:
The page covers everything from documentation, installation, basic usage, to advanced concepts. The basic markup that will put the login component onto the page looks like this:
<adf-login
[providers]="'ALL'"
(success)="mySuccessMethod($event)"
(error)="myErrorMethod($event)">
</adf-login>
The providers property controls what backing services to connect to. By default it will connect only to the Content Services (i.e. providers value set to 'ECM'). By specifying ALL we make sure to login to both Content Services and Process Services.
If you are using only one of the backend services, such as APS, then make sure to specify only that service in the providers field (e.g. ‘ECM’ if logging in only to ACS and 'BPM' if logging in only to APS).
The two event callback methods will be invoked when a successful login has completed (success) or if an error occurs (error).
The application that we generated comes with only one component, the main application component, represented by the TypeScript class AppComponent defined in the src/app/app.component.ts file. The view template is available in the src/app/app.component.html file.
To use the login component from the app component we need to first import it from the @alfresco/adf-core package. The Alfresco Login Component is defined in the TypeScript class LoginComponent. The import is done implicitly via a module called LoginModule, which should be added to the AppModule in the src/app/app.module.ts file as follows:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CoreModule, LoginModule } from '@alfresco/adf-core';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
CoreModule,
LoginModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
If you look in the LoginModule file you can see that it exports the LoginComponent and some directives at the root level. Any component that exist, or we create, can now use the login component.
To quickly test the Login component we will add it to the existing root app component. Open up the src/app/app.component.html template file, you should see some markup like this:
<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
<h1>
Welcome to {{ title }}!
</h1>
<img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
</div>
<h2>Here are some links to help you start: </h2>
<ul>
<li>
<h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2>
</li>
<li>
<h2><a target="_blank" rel="noopener" href="https://github.com/angular/angular-cli/wiki">CLI Documentation</a></h2>
</li>
<li>
<h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2>
</li>
</ul>
<router-outlet></router-outlet>
Change the template to look like this instead, with the Login component added:
<div style="text-align:center">
<h1>
Welcome to {{title}}!
</h1>
</div>
<adf-login
[providers]="'ALL'"
(onSuccess)="onLoginSuccess($event)"
(onError)="onLoginError($event)">
</adf-login>
<router-outlet></router-outlet>
Keep the router-outlet as that is where the router will output other components template code. It does not do anything at the moment as we don’t have any other components with routes, but we will keep it anyway.
Note. change [providers]="'ALL'" if you are just using one of the backing services. Use 'ECM' for ACS and 'BPM' for APS.
The Alfresco Login component emits two events called success and error, indicating the outcome of the login attempt. We will define new handler methods in the App component class as follows (src/app/app.component.ts😞
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'ADF Workbench';
onLoginSuccess($event) {
console.log('Successful login: ' + $event.value);
}
onLoginError($event) {
console.log('Failed login: ' + $event.value);
}
}
Note. also updated the title property to “ADF Workbench”.
It’s time to run the app and see how it looks. We would expect to see a login page.
Start the server by running npm start:
Martins-MacBook-Pro:adf-workbench20 martin$ npm start
> adf-workbench20@0.0.0 start /Users/mbergljung/WebstormProjects/Angular/ADF/adf-workbench20
> ng serve --proxy-config proxy.conf.json
** NG Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **
Date: 2017-11-29T14:46:06.936Z
Hash: 52750eea6550b1135660
Time: 11257ms
chunk {inline} inline.bundle.js (inline) 5.79 kB [entry] [rendered]
chunk {main} main.bundle.js (main) 56 kB [initial] [rendered]
chunk {polyfills} polyfills.bundle.js (polyfills) 519 kB [initial] [rendered]
chunk {styles} styles.bundle.js (styles) 686 kB [initial] [rendered]
chunk {vendor} vendor.bundle.js (vendor) 26.3 MB [initial] [rendered]
webpack: Compiled successfully.
You should see all Webpack bundles created and the app launched successfully. Note that we use npm start instead of ng serve as we want the Webpack proxy to be active.
Accessing the app on http://localhost:4200 should show the login page as follows:
Pretty cool!
Let’s try and login with the common username and password:
Clicking SIGN IN should log you in successfully to ACS and/or APS (depending on how you configured the login component). The login screen will change so the SIGN IN button now says WELCOME:
The JavaScript console in the browser should show you log from the callback method onLoginSuccess:
Everything is working fine now and we are logged in to ACS and/or APS.
To use Angular CLI to set up an Angular application is really easy. And to configure it to work with ADF 2.0.0 is not that difficult either. It was all done to get you up to speed on how everything hangs together. Of course it is also possible to generate an ADF application with the Yeoman generator when you are on top of how things work together. Or just clone an ADF ready Angular CLI project.
We can now start bringing in some more ADF components. However, before we do that it might be a good idea to refactor/restructure the app a bit so it resembles a more realistic application with toolbar, menu, logout etc. Read this Adding Navigation, Menu, Toolbar, and Logout to your ADF 2.0 App article for more info on how to do that.