diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index f526e71fb7..457c2f3b76 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,39 +1,38 @@ +**Please check if the PR fulfills these requirements** +- [ ] The commit message follows our [guidelines](https://github.com/Alfresco/alfresco-ng2-components/wiki/Commit-format) +- [ ] Tests for the changes have been added (for bug fixes / features) +- [ ] Docs have been added / updated (for bug fixes / features) + -**Type of contribution:** (check one with "x") +**What kind of change does this PR introduce?** (check one with "x") ``` [ ] Bugfix [ ] Feature -[ ] Code formatting -[ ] Code Refactoring +[ ] Code style update (formatting, local variables) +[ ] Refactoring (no functional changes, no api changes) [ ] Build related changes [ ] Documentation [ ] Other... Please describe: ``` -**This PR adds the following feature:** - -**Current behavior:** +**What is the current behavior?** (You can also link to an open issue here) -**New behavior:** -**This PR fixes the following issue:** - -**This PR introduces a breaking change:** (check one with "x") +**What is the new behavior?** + + + +**Does this PR introduce a breaking change?** (check one with "x") ``` -- [ ] Yes -- [ ] No +[ ] Yes +[ ] No ``` - - - -**More information:** +If this PR contains a breaking change, please describe the impact and migration path for existing applications: ... +**Other information**: \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index a3786026b6..e92a15a91e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,22 +1,17 @@ language: node_js dist: trusty -sudo: required +sudo: false node_js: - "5" addons: - apt: - sources: - - google-chrome - packages: - - google-chrome-stable before_install: - - export CHROME_BIN=/usr/bin/google-chrome - - export DISPLAY=:99.0 - - sh -e /etc/init.d/xvfb start + - "export DISPLAY=:99.0" + - "sh -e /etc/init.d/xvfb start" + env: matrix: - MODULE=ng2-alfresco-core @@ -37,31 +32,34 @@ env: before_script: - if ([ "$MODULE" != "ng2-alfresco-core" ]); then - (cd ng2-components/ng2-alfresco-core; npm install; npm link); + (cd ng2-components/ng2-alfresco-core; if [ ! -d "dist" ]; then npm install; fi; npm link); fi - if ([ "$MODULE" == "ng2-alfresco-documentlist" ] || [ "$MODULE" == "ng2-alfresco-webscript" ] || [ "$MODULE" == "ng2-activiti-processlist" ] || [ "$MODULE" == "ng2-activiti-tasklist" ]); then - (cd ng2-components/ng2-alfresco-datatable; npm link ng2-alfresco-core; npm install; npm link); + (cd ng2-components/ng2-alfresco-datatable; npm link ng2-alfresco-core; if [ ! -d "dist" ]; then npm install; fi; npm link); fi - if ([ "$MODULE" == "ng2-activiti-tasklist" ] || [ "$MODULE" == "ng2-activiti-processlist" ]); then - (cd ng2-components/ng2-activiti-form; npm link ng2-alfresco-core; npm install; npm link); + (cd ng2-components/ng2-activiti-form; npm link ng2-alfresco-core; if [ ! -d "dist" ]; then npm install; fi; npm link); fi - if ([ "$MODULE" == "ng2-activiti-processlist" ]); then - (cd ng2-components/ng2-activiti-tasklist; npm link ng2-alfresco-core; npm link ng2-alfresco-datatable; npm link ng2-activiti-form; npm install; npm link); + (cd ng2-components/ng2-activiti-tasklist; npm link ng2-alfresco-core; npm link ng2-alfresco-datatable; npm link ng2-activiti-form; if [ ! -d "dist" ]; then npm install; fi; npm link); fi - if ([ "$MODULE" == "ng2-activiti-analytics" ]); then - (cd ng2-components/ng2-activiti-diagrams; npm link ng2-alfresco-core; npm install; npm link); + (cd ng2-components/ng2-activiti-diagrams; npm link ng2-alfresco-core; if [ ! -d "dist" ]; then npm install; fi; npm link); fi - cd ng2-components/$MODULE; - - npm run travis + - npm run travis; - npm install; - - ls -ltrh ./node_modules/ script: npm run test # Send coverage data to Coveralls after_success: - - bash <(curl -s https://codecov.io/bash) + - if ([ "$TRAVIS_BRANCH" == "development" ] || [ "$TRAVIS_BRANCH" == "master" ]); then + bash <(curl -s https://codecov.io/bash) + fi cache: directories: + - ./node_modules/material-design-lite + - ./node_modules/material-design-icons - demo-shell-ng2/node_modules - ng2-components/ng2-activiti-form/node_modules - ng2-components/ng2-activiti-processlist/node_modules diff --git a/PREREQUISITES.md b/PREREQUISITES.md index 0bb6211056..f4fb366f79 100644 --- a/PREREQUISITES.md +++ b/PREREQUISITES.md @@ -6,6 +6,7 @@ The [Angular 2](https://angular.io/) based application development framework req - [Download and install Activiti](https://www.alfresco.com/products/bpm/alfresco-activiti/trial) - [Node.js](https://nodejs.org/en/) JavaScript runtime. - [npm](https://www.npmjs.com/) package manager for JavaScript. +- local nginx proxy to avoid cross-origin browser restrictions (see below) - (If you use ECM and BPM together) Make sure your user has the same username and password in both system *Note: Default username for activiti is "admin@app.activiti.com" and "admin" for Alfresco, and also the default password are different. Change them to be equal.* @@ -25,26 +26,39 @@ $ node -v v5.12.0 ``` -## Configure Nginx +## Installing nginx -To correctly configure Nginx use the following file [nginx.conf](/nginx.conf). -This will put Activiti, Alfresco and the app dev framework under the same domain. +Most Linux distributions will come with nginx available to install via your +package manager and on Mac OS you can use [Homebrew](http://brew.sh/). + +If you want to install manually however you can follow the instructions on the +[download page](http://nginx.org/en/download.html). See also the specific information for +[windows users](http://nginx.org/en/docs/windows.html). + +### Start nginx + +Start nginx using the supplied configuration in [nginx.conf](nginx.conf) + + nginx -c nginx.conf + +### Review nginx configuration + +To correctly configure nginx use the following file [nginx.conf](/nginx.conf). +This will host Activiti, Alfresco and the app dev framework under the same origin. * ECM : http://localhost:8888/alfresco/ * BPM : http://localhost:8888/activiti/ -To make everything work, you have to change the address of the ECM and BPM. In the demo app you can do that clicking on the top left menu and changing the bottom left options: ECM host and BPM host. +To make everything work, you have to change the address of the ECM and BPM. In the demo app you can do that clicking on the top right settings menu and changing the bottom left options: *ECM host* and *BPM host*. This configuration assumes few things: * Port mapping: - * Nginx entry point: 8888 - * Demo Shell: 3000 - * Alfresco: 8080 - * Activiti: 9999 + * nginx entry point: 0.0.0.0:8888 + * Demo Shell: locathost:3000 + * Alfresco: locathost:8080 + * Activiti: locathost:9999 All those values can be modified at their respective `location` directive on the [nginx.conf](/nginx.conf) file. -It also need to be compiled with the [Headers More](https://www.nginx.com/resources/wiki/modules/headers_more/) module , which add more control over sending headers to the backend. - -If you want to know more on how to install and configure Nginx to work with the Application Development Framework can be found [here](https://community.alfresco.com/community/application-development-framework/blog/2016/09/28/adf-development-set-up-with-nginx-proxy) +If you want to know more on how to install and configure nginx to work with the Application Development Framework can be found [here](https://community.alfresco.com/community/application-development-framework/blog/2016/09/28/adf-development-set-up-with-nginx-proxy) diff --git a/appveyor.yml b/appveyor.yml index eb683012f0..d697992ac3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -36,6 +36,7 @@ install: - IF %COMPONENT_NAME% EQU ng2-activiti-tasklist (cd ng2-components/ng2-alfresco-datatable && npm link ng2-alfresco-core && npm install && npm link && cd ../../) - IF %COMPONENT_NAME% EQU ng2-activiti-tasklist (cd ng2-components/ng2-activiti-form && npm link ng2-alfresco-core && npm install && npm link && cd ../../) - IF %COMPONENT_NAME% EQU ng2-alfresco-webscript (cd ng2-components/ng2-alfresco-datatable && npm link ng2-alfresco-core && npm install && npm link && cd ../../) + - IF %COMPONENT_NAME% EQU ng2-activiti-analytics (cd ng2-components/ng2-activiti-diagrams && npm link link ng2-alfresco-core && npm install && npm link && cd ../../) - cd ng2-components/%COMPONENT_NAME% - IF %COMPONENT_NAME% NEQ ng2-alfresco-core (npm link ng2-alfresco-core) - IF %COMPONENT_NAME% EQU ng2-alfresco-documentlist (npm link ng2-alfresco-datatable) @@ -55,3 +56,21 @@ test_script: # Don't actually build. build: off + +cache: + - node_modules + - C:\projects\alfresco-ng2-components\ng2-components\ng2-activiti-form\node_modules + - C:\projects\alfresco-ng2-components\ng2-components\ng2-activiti-processlist\node_modules + - C:\projects\alfresco-ng2-components\ng2-components\ng2-activiti-tasklist\node_modules + - C:\projects\alfresco-ng2-components\ng2-components\ng2-alfresco-core\node_modules + - C:\projects\alfresco-ng2-components\ng2-components\ng2-alfresco-datatable\node_modules + - C:\projects\alfresco-ng2-components\ng2-components\ng2-alfresco-documentlist\node_modules + - C:\projects\alfresco-ng2-components\ng2-components\ng2-alfresco-login\node_modules + - C:\projects\alfresco-ng2-components\ng2-components\ng2-alfresco-search\node_modules + - C:\projects\alfresco-ng2-components\ng2-components\ng2-alfresco-upload\node_modules + - C:\projects\alfresco-ng2-components\ng2-components\ng2-alfresco-viewer\node_modules + - C:\projects\alfresco-ng2-components\ng2-components\ng2-alfresco-webscript\node_modules + - C:\projects\alfresco-ng2-components\ng2-components\ng2-alfresco-tag\node_modules + - C:\projects\alfresco-ng2-components\ng2-components\ng2-activiti-analytics\node_modules + - C:\projects\alfresco-ng2-components\ng2-components\ng2-alfresco-userinfo\node_modules + - C:\projects\alfresco-ng2-components\ng2-components\ng2-activiti-diagrams\node_modules diff --git a/demo-shell-ng2/app/app.component.css b/demo-shell-ng2/app/app.component.css index 6eff75fc29..e819fafef6 100644 --- a/demo-shell-ng2/app/app.component.css +++ b/demo-shell-ng2/app/app.component.css @@ -9,7 +9,7 @@ } .user-profile { - margin-right: 50px; + margin-right: 10px; } .mdl-button-padding { diff --git a/demo-shell-ng2/app/app.component.html b/demo-shell-ng2/app/app.component.html index bbea07d6f6..2986ba16ac 100644 --- a/demo-shell-ng2/app/app.component.html +++ b/demo-shell-ng2/app/app.component.html @@ -4,10 +4,9 @@
-
- -
+ + @@ -20,7 +19,8 @@
Task List -
@@ -58,21 +64,30 @@
Process Filters - +
Process List -
-
+
Process Details
+
+ Start Process + +
diff --git a/demo-shell-ng2/app/components/activiti/activiti-demo.component.ts b/demo-shell-ng2/app/components/activiti/activiti-demo.component.ts index 37bc4c51f2..82feddfefd 100644 --- a/demo-shell-ng2/app/components/activiti/activiti-demo.component.ts +++ b/demo-shell-ng2/app/components/activiti/activiti-demo.component.ts @@ -22,7 +22,11 @@ import { ActivitiApps, ActivitiTaskList } from 'ng2-activiti-tasklist'; -import { ActivitiProcessInstanceListComponent } from 'ng2-activiti-processlist'; +import { + ActivitiProcessInstanceListComponent, + ActivitiStartProcessInstance, + ProcessInstance +} from 'ng2-activiti-processlist'; import { ActivatedRoute } from '@angular/router'; import { Subscription } from 'rxjs/Rx'; import { @@ -30,9 +34,14 @@ import { DataSorting } from 'ng2-alfresco-datatable'; +import { FormRenderingService } from 'ng2-activiti-form'; +import { /*CustomEditorComponent*/ CustomStencil01 } from './custom-editor/custom-editor.component'; + declare let __moduleName: string; declare var componentHandler; +const currentProcessIdNew = '__NEW__'; + @Component({ moduleId: __moduleName, selector: 'activiti-demo', @@ -62,6 +71,9 @@ export class ActivitiDemoComponent implements AfterViewChecked { @ViewChild('activitiprocessdetails') activitiprocessdetails: any; + @ViewChild(ActivitiStartProcessInstance) + activitiStartProcess: ActivitiStartProcessInstance; + @ViewChild('tabmain') tabMain: any; @@ -78,16 +90,16 @@ export class ActivitiDemoComponent implements AfterViewChecked { taskSchemaColumns: any [] = []; processSchemaColumns: any [] = []; - taskFilter: any; + taskFilter: FilterRepresentationModel; report: any; - processFilter: any; + processFilter: FilterRepresentationModel; sub: Subscription; dataTasks: ObjectDataTableAdapter; dataProcesses: ObjectDataTableAdapter; - constructor(private route: ActivatedRoute) { + constructor(private route: ActivatedRoute, private formRenderingService: FormRenderingService) { this.dataTasks = new ObjectDataTableAdapter( [], [ @@ -100,11 +112,16 @@ export class ActivitiDemoComponent implements AfterViewChecked { this.dataProcesses = new ObjectDataTableAdapter( [], [ - {type: 'text', key: 'name', title: 'Name', cssClass: 'full-width name-column', sortable: true}, - {type: 'text', key: 'started', title: 'Started', cssClass: 'hidden', sortable: true} + {type: 'text', key: 'name', title: 'Name', cssClass: 'full-width name-column'}, + {type: 'text', key: 'started', title: 'Started', cssClass: 'hidden'} ] ); - this.dataProcesses.setSorting(new DataSorting('started', 'desc')); + + // Uncomment this line to replace all 'text' field editors with custom component + // formRenderingService.setComponentTypeResolver('text', () => CustomEditorComponent, true); + + // Uncomment this line to map 'custom_stencil_01' to local editor component + formRenderingService.setComponentTypeResolver('custom_stencil_01', () => CustomStencil01, true); } ngOnInit() { @@ -154,10 +171,10 @@ export class ActivitiDemoComponent implements AfterViewChecked { } onSuccessTaskList(event: FilterRepresentationModel) { - this.currentTaskId = this.activititasklist.getCurrentTaskId(); + this.currentTaskId = this.activititasklist.getCurrentId(); } - onProcessFilterClick(event: any) { + onProcessFilterClick(event: FilterRepresentationModel) { this.processFilter = event; } @@ -166,7 +183,7 @@ export class ActivitiDemoComponent implements AfterViewChecked { } onSuccessProcessList(event: any) { - this.currentProcessInstanceId = this.activitiprocesslist.getCurrentProcessId(); + this.currentProcessInstanceId = this.activitiprocesslist.getCurrentId(); } onTaskRowClick(taskId) { @@ -177,8 +194,17 @@ export class ActivitiDemoComponent implements AfterViewChecked { this.currentProcessInstanceId = processInstanceId; } - onStartProcessInstance() { - this.activitiprocesslist.reload(); + navigateStartProcess() { + this.currentProcessInstanceId = currentProcessIdNew; + } + + onStartProcessInstance(instance: ProcessInstance) { + this.currentProcessInstanceId = instance.id; + this.activitiStartProcess.reset(); + } + + isStartProcessMode() { + return this.currentProcessInstanceId === currentProcessIdNew; } processCancelled(data: any) { @@ -195,7 +221,7 @@ export class ActivitiDemoComponent implements AfterViewChecked { } onFormCompleted(form) { - this.activititasklist.load(this.taskFilter); + this.activititasklist.reload(); this.currentTaskId = null; } diff --git a/demo-shell-ng2/app/components/activiti/custom-editor/custom-editor.component.ts b/demo-shell-ng2/app/components/activiti/custom-editor/custom-editor.component.ts new file mode 100644 index 0000000000..b71a11ae3c --- /dev/null +++ b/demo-shell-ng2/app/components/activiti/custom-editor/custom-editor.component.ts @@ -0,0 +1,23 @@ +import { NgModule, Component } from '@angular/core'; +import { WidgetComponent } from 'ng2-activiti-form'; + +@Component({ + selector: 'custom-editor', + template: ` +
Look, I'm a custom editor!
+ ` +}) +export class CustomEditorComponent extends WidgetComponent {} + +@Component({ + selector: 'custom-stencil-01', + template: `
ADF version of custom Activiti stencil
` +}) +export class CustomStencil01 extends WidgetComponent {} + +@NgModule({ + declarations: [ CustomEditorComponent, CustomStencil01 ], + exports: [ CustomEditorComponent, CustomStencil01 ], + entryComponents: [ CustomEditorComponent, CustomStencil01 ] +}) +export class CustomEditorsModule {} diff --git a/demo-shell-ng2/app/components/login/login-demo.component.ts b/demo-shell-ng2/app/components/login/login-demo.component.ts index 868b866608..f11e33dcae 100644 --- a/demo-shell-ng2/app/components/login/login-demo.component.ts +++ b/demo-shell-ng2/app/components/login/login-demo.component.ts @@ -73,7 +73,6 @@ export class LoginDemoComponent implements OnInit { } onLogin($event) { - console.log($event); this.router.navigate(['/home']); } diff --git a/demo-shell-ng2/custom-translation/alfresco-login/i18n/en.json b/demo-shell-ng2/custom-translation/alfresco-login/i18n/en.json new file mode 100644 index 0000000000..7c1ef10cd9 --- /dev/null +++ b/demo-shell-ng2/custom-translation/alfresco-login/i18n/en.json @@ -0,0 +1,26 @@ +{ + "LOGIN": { + "LOGO": "Alfresco", + "LABEL": { + "LOGIN": "Login", + "USERNAME": "Username", + "PASSWORD": "Password", + "REMEMBER": "Remember" + }, + "MESSAGES": { + "USERNAME-REQUIRED": "Required", + "USERNAME-MIN": "Your username needs to be at least 4 characters.", + "PASSWORD-REQUIRED": "Enter your password to sign in", + "LOGIN-ERROR-CREDENTIALS": "You have entered an invalid username or password", + "LOGIN-ERROR-PROVIDERS": "Providers cannot be undefined", + "LOGIN-SUCCESS": "Login successful" + }, + "BUTTON": { + "LOGIN": "SIGN IN" + }, + "ACTION": { + "HELP": "NEED HELP?", + "REGISTER": "REGISTER" + } + } +} diff --git a/demo-shell-ng2/custom-translation/alfresco-login/i18n/it.json b/demo-shell-ng2/custom-translation/alfresco-login/i18n/it.json new file mode 100644 index 0000000000..c274b00912 --- /dev/null +++ b/demo-shell-ng2/custom-translation/alfresco-login/i18n/it.json @@ -0,0 +1,25 @@ +{ + "LOGIN": { + "LABEL": { + "LOGIN": "Autenticazione", + "USERNAME": "Nome utente", + "PASSWORD": "Password", + "REMEMBER": "Ricordami" + }, + "MESSAGES": { + "USERNAME-REQUIRED": "Campo obbligatorio", + "USERNAME-MIN": "Inserire un nome utente di minimo 4 caratteri.", + "PASSWORD-REQUIRED": "Campo obbligatorio", + "LOGIN-ERROR-CREDENTIALS": "Nome utente o password non validi", + "LOGIN-ERROR-PROVIDERS": "Providers non può essere nullo", + "LOGIN-SUCCESS": "Login effettuata con successo" + }, + "BUTTON": { + "LOGIN": "Accedi" + }, + "ACTION": { + "HELP": "BISOGNO DI AIUTO?", + "REGISTER": "REGISTRATI" + } + } +} \ No newline at end of file diff --git a/demo-shell-ng2/custom-translation/i18n/en.json b/demo-shell-ng2/custom-translation/i18n/en.json new file mode 100644 index 0000000000..b35c62c63a --- /dev/null +++ b/demo-shell-ng2/custom-translation/i18n/en.json @@ -0,0 +1,24 @@ +{ + "title": "Welcome", + + "DOCUMENT_LIST": { + "COLUMNS": { + "DISPLAY_NAME": "Display name", + "CREATED_BY": "Created by", + "CREATED_ON": "Created on" + }, + "ACTIONS": { + "FOLDER": { + "SYSTEM_1": "System folder action 1", + "CUSTOM": "Custom folder action", + "DELETE": "Delete" + }, + "DOCUMENT": { + "DOWNLOAD": "Download", + "DELETE": "Delete", + "SYSTEM_2": "System document action 2", + "CUSTOM": "Custom action" + } + } + } +} diff --git a/demo-shell-ng2/i18n/it.json b/demo-shell-ng2/custom-translation/i18n/it.json similarity index 53% rename from demo-shell-ng2/i18n/it.json rename to demo-shell-ng2/custom-translation/i18n/it.json index 5e6b750063..bd5e5efc77 100644 --- a/demo-shell-ng2/i18n/it.json +++ b/demo-shell-ng2/custom-translation/i18n/it.json @@ -18,24 +18,8 @@ "CUSTOM": "Custom action" } } - }, - - "DATATABLE": { - "COLUMNS": { - "DISPLAY_NAME": "Display name", - "CREATED_BY": "Created by", - "CREATED_ON": "Created on" - }, - "ACTIONS": { - "FOLDER": { - "SYSTEM_1": "System folder action 1", - "CUSTOM": "Custom folder action" - }, - "DOCUMENT": { - "DOWNLOAD": "Download", - "SYSTEM_2": "System document action 2", - "CUSTOM": "Custom action" - } - } } + + + } diff --git a/demo-shell-ng2/i18n/en.json b/demo-shell-ng2/i18n/en.json deleted file mode 100644 index 1993786440..0000000000 --- a/demo-shell-ng2/i18n/en.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "title": "Welcome", - - "DOCUMENT_LIST": { - "COLUMNS": { - "DISPLAY_NAME": "Display name", - "CREATED_BY": "Created by", - "CREATED_ON": "Created on" - }, - "ACTIONS": { - "FOLDER": { - "SYSTEM_1": "System folder action 1", - "CUSTOM": "Custom folder action", - "DELETE": "Delete" - }, - "DOCUMENT": { - "DOWNLOAD": "Download", - "DELETE": "Delete", - "SYSTEM_2": "System document action 2", - "CUSTOM": "Custom action" - } - } - }, - - "DATATABLE": { - "COLUMNS": { - "DISPLAY_NAME": "Display name", - "CREATED_BY": "Created by", - "CREATED_ON": "Created on" - }, - "ACTIONS": { - "FOLDER": { - "SYSTEM_1": "System folder action 1", - "CUSTOM": "Custom folder action" - }, - "DOCUMENT": { - "DOWNLOAD": "Download", - "SYSTEM_2": "System document action 2", - "CUSTOM": "Custom action" - } - } - }, - - "LOGIN": { - "LOGO": "Alfresco", - "LABEL": { - "LOGIN": "Login", - "USERNAME": "Username", - "PASSWORD": "Password", - "REMEMBER": "Remember" - }, - "MESSAGES": { - "USERNAME-REQUIRED": "Required", - "USERNAME-MIN": "Your username needs to be at least 4 characters.", - "PASSWORD-REQUIRED": "Enter your password to sign in", - "LOGIN-ERROR": "You have entered an invalid username or password", - "LOGIN-SUCCESS": "Login successful" - }, - "BUTTON": { - "LOGIN": "Login" - }, - "ACTION": { - "HELP": "NEED HELP?", - "REGISTER": "REGISTER" - } - } -} diff --git a/demo-shell-ng2/index.html b/demo-shell-ng2/index.html index aec2e4da25..2fe2040a7f 100644 --- a/demo-shell-ng2/index.html +++ b/demo-shell-ng2/index.html @@ -48,6 +48,11 @@ + + + + + + + +``` + +Where `` should be replaced with a valid url pointing to your Activiti installation, for example: + +```html + +``` +- `/app/rest/script-files/controllers` + provides all stencil controllers stored within Activiti + +- `runtime.ng1.js` + provides a compatibility layer for controllers created with Angular 1 + (this is to avoid runtime errors when loading Angular 1 code into `` component) + +- `runtime.adf.js` + provides API for stencil management and registration, + i.e. mapping html templates with corresponding controller classes + +## Creating new stencil + +Create a new stencil and add a new item called `ng2 component 01`. + +The internal identifier in this case should be `ng2_component_01`. +This value will be used as field type when form gets rendered. + +## Form runtime template + +This should be a valid Angular 2 component template that you want to render in `` component: + +```html +
+
Angular2 Component
+
Created by: {{name}}
+
+``` + +## Form editor template + +This can be any html layout to be rendered as a component placeholder in Activiti Form Designer. + +```html +
+
+ Angular2 Component 01 +
+
+``` + +## Custom component controller + +This field should contain JavaScript code for Angular 2 component class. + +_Note: If you are using TypeScript then you should be putting transpiled JavaScript code here, +you can try official [TypeScript playground](http://www.typescriptlang.org/play/) +to see how TS code gets transpiled into JS._ + +### JavaScript code + +```js +var SampleClass1 = (function () { + function SampleClass1() { + this.name = 'Denys'; + console.log('ng2_component_01 ctor'); + } + SampleClass1.prototype.ngOnInit = function () { + console.log('OnInit called'); + }; + return SampleClass1; +}()); +``` + +### TypeScript code + +The TypeScript version of the code above is: + +```ts +import { OnInit } from '@angular/core'; + +class SampleClass1 implements OnInit { + + constructor() { + console.log('ctor called'); + } + + ngOnInit() { + console.log('OnInit called'); + } + +} +``` + +### Mapping template with component class + +In order to map **form runtime template** with the corresponding component class +you will need registering both parts with `adf.registerComponent(identifier, class)` api: + +```js +if (adf) { + adf.registerComponent('ng2_component_01', SampleClass1); +} +``` + +### Final result + +```js +var SampleClass1 = (function () { + function SampleClass1() { + this.name = 'Denys'; + console.log('ng2_component_01 ctor'); + } + SampleClass1.prototype.ngOnInit = function () { + console.log('OnInit called'); + }; + return SampleClass1; +}()); + +if (adf) { + adf.registerComponent('ng2_component_01', SampleClass1); +} +``` + +## Runtime result + +When rendered on the form this stencil item should look like the following: + +```html +Angular2 Component +Created by: Denys +``` + +ADF Form component will automatically assemble and compile a valid Angular 2 component on the fly. \ No newline at end of file diff --git a/ng2-components/ng2-activiti-form/index.ts b/ng2-components/ng2-activiti-form/index.ts index 519cc0e2c3..9c8ecde6b8 100644 --- a/ng2-components/ng2-activiti-form/index.ts +++ b/ng2-components/ng2-activiti-form/index.ts @@ -19,12 +19,14 @@ import { NgModule, ModuleWithProviders } from '@angular/core'; import { CoreModule } from 'ng2-alfresco-core'; import { ActivitiForm } from './src/components/activiti-form.component'; +import { FormFieldComponent } from './src/components/form-field/form-field.component'; import { ActivitiStartForm } from './src/components/activiti-start-form.component'; import { FormService } from './src/services/form.service'; import { EcmModelService } from './src/services/ecm-model.service'; import { NodeService } from './src/services/node.service'; import { WidgetVisibilityService } from './src/services/widget-visibility.service'; import { ActivitiAlfrescoContentService } from './src/services/activiti-alfresco.service'; +import { FormRenderingService } from './src/services/form-rendering.service'; import { HttpModule } from '@angular/http'; import { WIDGET_DIRECTIVES } from './src/components/widgets/index'; @@ -34,10 +36,12 @@ export * from './src/services/form.service'; export * from './src/components/widgets/index'; export * from './src/services/ecm-model.service'; export * from './src/services/node.service'; +export * from './src/services/form-rendering.service'; export const ACTIVITI_FORM_DIRECTIVES: any[] = [ ActivitiForm, ActivitiStartForm, + FormFieldComponent, ...WIDGET_DIRECTIVES ]; @@ -46,7 +50,8 @@ export const ACTIVITI_FORM_PROVIDERS: any[] = [ EcmModelService, NodeService, WidgetVisibilityService, - ActivitiAlfrescoContentService + ActivitiAlfrescoContentService, + FormRenderingService ]; @NgModule({ @@ -57,6 +62,9 @@ export const ACTIVITI_FORM_PROVIDERS: any[] = [ declarations: [ ...ACTIVITI_FORM_DIRECTIVES ], + entryComponents: [ + ...WIDGET_DIRECTIVES + ], providers: [ ...ACTIVITI_FORM_PROVIDERS ], diff --git a/ng2-components/ng2-activiti-form/package.json b/ng2-components/ng2-activiti-form/package.json index f764072bce..4194e67ce9 100644 --- a/ng2-components/ng2-activiti-form/package.json +++ b/ng2-components/ng2-activiti-form/package.json @@ -1,7 +1,7 @@ { "name": "ng2-activiti-form", "description": "Alfresco Activiti Form Component for Angular 2", - "version": "0.4.0", + "version": "0.5.0", "author": "Alfresco Software, Ltd.", "scripts": { "clean": "npm install rimraf && rimraf dist node_modules typings", @@ -58,18 +58,12 @@ "systemjs": "0.19.27", "zone.js": "^0.6.23", - "intl": "1.2.4", - "dialog-polyfill": "^0.4.3", - "element.scrollintoviewifneeded-polyfill": "^1.0.1", - "material-design-icons": "2.2.3", - "material-design-lite": "1.2.1", - "moment": "2.15.1", "md-date-time-picker": "^2.2.0", "ng2-translate": "2.5.0", - "alfresco-js-api": "^0.4.0", - "ng2-alfresco-core": "0.4.0" + "alfresco-js-api": "^0.5.0", + "ng2-alfresco-core": "0.5.0" }, "devDependencies": { "@types/node": "^6.0.42", diff --git a/ng2-components/ng2-activiti-form/src/components/activiti-form.component.html b/ng2-components/ng2-activiti-form/src/components/activiti-form.component.html index ae6aaaf3c9..48411ae393 100644 --- a/ng2-components/ng2-activiti-form/src/components/activiti-form.component.html +++ b/ng2-components/ng2-activiti-form/src/components/activiti-form.component.html @@ -15,23 +15,7 @@
-
-
- -
-
- -
-
- -
-
- -
-
- UNKNOWN WIDGET TYPE: {{field.type}} -
-
+
diff --git a/ng2-components/ng2-activiti-form/src/components/activiti-start-form.component.html b/ng2-components/ng2-activiti-form/src/components/activiti-start-form.component.html index 2dd449b9b6..5914fefde0 100644 --- a/ng2-components/ng2-activiti-form/src/components/activiti-start-form.component.html +++ b/ng2-components/ng2-activiti-form/src/components/activiti-start-form.component.html @@ -12,23 +12,7 @@
-
-
- -
-
- -
-
- -
-
- -
-
- UNKNOWN WIDGET TYPE: {{field.type}} -
-
+
diff --git a/ng2-components/ng2-activiti-form/src/components/activiti-start-form.component.spec.ts b/ng2-components/ng2-activiti-form/src/components/activiti-start-form.component.spec.ts index 46d20208d6..76701f658b 100644 --- a/ng2-components/ng2-activiti-form/src/components/activiti-start-form.component.spec.ts +++ b/ng2-components/ng2-activiti-form/src/components/activiti-start-form.component.spec.ts @@ -20,6 +20,7 @@ import { ComponentFixture, TestBed, async } from '@angular/core/testing'; import { Observable } from 'rxjs/Rx'; import { ActivitiStartForm } from './activiti-start-form.component'; +import { FormFieldComponent } from './form-field/form-field.component'; import { WIDGET_DIRECTIVES } from './widgets/index'; import { FormService } from './../services/form.service'; import { EcmModelService } from './../services/ecm-model.service'; @@ -43,6 +44,7 @@ describe('ActivitiStartForm', () => { imports: [ CoreModule ], declarations: [ ActivitiStartForm, + FormFieldComponent, ...WIDGET_DIRECTIVES ], providers: [ diff --git a/ng2-components/ng2-activiti-form/src/components/activiti-start-form.component.ts b/ng2-components/ng2-activiti-form/src/components/activiti-start-form.component.ts index ae5cedbf9e..d4793c8601 100644 --- a/ng2-components/ng2-activiti-form/src/components/activiti-start-form.component.ts +++ b/ng2-components/ng2-activiti-form/src/components/activiti-start-form.component.ts @@ -83,7 +83,7 @@ export class ActivitiStartForm extends ActivitiForm implements OnInit, AfterView } if (this.translate) { - this.translate.addTranslationFolder('node_modules/ng2-activiti-form/src'); + this.translate.addTranslationFolder('ng2-activiti-form', 'node_modules/ng2-activiti-form/dist/src'); } } diff --git a/ng2-components/ng2-activiti-form/src/components/form-field/form-field.component.spec.ts b/ng2-components/ng2-activiti-form/src/components/form-field/form-field.component.spec.ts new file mode 100644 index 0000000000..d0e85f82c3 --- /dev/null +++ b/ng2-components/ng2-activiti-form/src/components/form-field/form-field.component.spec.ts @@ -0,0 +1,100 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CoreModule } from 'ng2-alfresco-core'; +import { ActivitiFormModule } from './../../../index'; +import { ComponentFixture, TestBed, async } from '@angular/core/testing'; +import { FormFieldComponent } from './form-field.component'; +import { FormRenderingService } from './../../services/form-rendering.service'; +import { FormModel, FormFieldModel, FormFieldTypes } from './../widgets/core/index'; +import { TextWidget, CheckboxWidget } from './../widgets/index'; + +describe('FormFieldComponent', () => { + + let fixture: ComponentFixture; + let component: FormFieldComponent; + let componentHandler: any; + let form: FormModel; + + let formRenderingService: FormRenderingService; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ CoreModule, ActivitiFormModule ] + }) + .compileComponents(); + })); + + beforeEach(() => { + componentHandler = jasmine.createSpyObj('componentHandler', [ + 'upgradeAllRegistered', + 'upgradeElement' + ]); + window['componentHandler'] = componentHandler; + + fixture = TestBed.createComponent(FormFieldComponent); + component = fixture.componentInstance; + formRenderingService = fixture.debugElement.injector.get(FormRenderingService); + form = new FormModel(); + }); + + it('should create default component instance', () => { + let field = new FormFieldModel(form, { + type: FormFieldTypes.TEXT + }); + + component.field = field; + fixture.detectChanges(); + + expect(component.componentRef).toBeDefined(); + expect(component.componentRef.componentType).toBe(TextWidget); + }); + + it('should create custom component instance', () => { + let field = new FormFieldModel(form, { + type: FormFieldTypes.TEXT + }); + + formRenderingService.setComponentTypeResolver(FormFieldTypes.TEXT, () => CheckboxWidget, true); + component.field = field; + fixture.detectChanges(); + + expect(component.componentRef).toBeDefined(); + expect(component.componentRef.componentType).toBe(CheckboxWidget); + }); + + it('should require field to create component', () => { + component.field = null; + fixture.detectChanges(); + + expect(component.componentRef).toBeUndefined(); + }); + + it('should require component type to be resolved', () => { + let field = new FormFieldModel(form, { + type: FormFieldTypes.TEXT + }); + + spyOn(formRenderingService, 'resolveComponentType').and.returnValue(null); + component.field = field; + fixture.detectChanges(); + + expect(formRenderingService.resolveComponentType).toHaveBeenCalled(); + expect(component.componentRef).toBeUndefined(); + }); + +}); diff --git a/ng2-components/ng2-activiti-form/src/components/form-field/form-field.component.ts b/ng2-components/ng2-activiti-form/src/components/form-field/form-field.component.ts new file mode 100644 index 0000000000..5657511464 --- /dev/null +++ b/ng2-components/ng2-activiti-form/src/components/form-field/form-field.component.ts @@ -0,0 +1,133 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + Component, + OnInit, OnDestroy, + ViewChild, + ViewContainerRef, + Input, + ComponentRef, + ComponentFactoryResolver, + ComponentFactory, + Compiler, + NgModule, + ModuleWithComponentFactories +} from '@angular/core'; + +import { CoreModule } from 'ng2-alfresco-core'; + +import { WidgetVisibilityService } from './../../services/widget-visibility.service'; +import { FormRenderingService } from './../../services/form-rendering.service'; +import { WidgetComponent } from './../widgets/widget.component'; +import { FormFieldModel } from './../widgets/core/index'; + +declare var adf: any; + +@Component({ + selector: 'form-field', + template: ` +
+
+
+ ` +}) +export class FormFieldComponent implements OnInit, OnDestroy { + + @ViewChild('container', { read: ViewContainerRef }) + container: ViewContainerRef; + + @Input() + field: FormFieldModel = null; + + componentRef: ComponentRef<{}>; + + constructor( + private formRenderingService: FormRenderingService, + private componentFactoryResolver: ComponentFactoryResolver, + private visibilityService: WidgetVisibilityService, + private compiler: Compiler) { + } + + ngOnInit() { + if (this.field) { + let customTemplate = this.field.form.customFieldTemplates[this.field.type]; + if (customTemplate && this.hasController(this.field.type)) { + let factory = this.getComponentFactorySync(this.field.type, customTemplate); + this.componentRef = this.container.createComponent(factory); + let instance: any = this.componentRef.instance; + if (instance) { + instance.field = this.field; + } + } else { + let componentType = this.formRenderingService.resolveComponentType(this.field); + if (componentType) { + let factory = this.componentFactoryResolver.resolveComponentFactory(componentType); + this.componentRef = this.container.createComponent(factory); + let instance = this.componentRef.instance; + instance.field = this.field; + instance.fieldChanged.subscribe(field => { + console.log('WidgetComponent.fieldChanged was used only to trigger visibility engine, components should do that internally if needed'); + if (field && field.form) { + this.visibilityService.refreshVisibility(field.form); + } + }); + } + } + } + } + + ngOnDestroy() { + if (this.componentRef) { + this.componentRef.destroy(); + this.componentRef = null; + } + } + + private hasController(type: string): boolean { + return (adf && adf.components && adf.components[type]); + } + + private getComponentFactorySync(type: string, template: string): ComponentFactory { + let componentInfo = adf.components[type]; + + if (componentInfo.factory) { + return componentInfo.factory; + } + + let metadata = { + selector: `runtime-component-${type}`, + template: template + }; + + let factory = this.createComponentFactorySync(this.compiler, metadata, componentInfo.class); + componentInfo.factory = factory; + return factory; + } + + private createComponentFactorySync(compiler: Compiler, metadata: Component, componentClass: any): ComponentFactory { + const cmpClass = componentClass || class RuntimeComponent { }; + const decoratedCmp = Component(metadata)(cmpClass); + + @NgModule({ imports: [CoreModule], declarations: [decoratedCmp] }) + class RuntimeComponentModule { } + + let module: ModuleWithComponentFactories = compiler.compileModuleAndAllComponentsSync(RuntimeComponentModule); + return module.componentFactories.find(x => x.componentType === decoratedCmp); + } + +} diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/amount/amount.widget.html b/ng2-components/ng2-activiti-form/src/components/widgets/amount/amount.widget.html index 773504db8f..985153bc81 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/amount/amount.widget.html +++ b/ng2-components/ng2-activiti-form/src/components/widgets/amount/amount.widget.html @@ -1,13 +1,13 @@ -
+ - + [disabled]="field.readOnly" + placeholder="{{field.placeholder}}"> {{field.validationSummary}}
diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/amount/amount.widget.spec.ts b/ng2-components/ng2-activiti-form/src/components/widgets/amount/amount.widget.spec.ts index 75eda10f8e..4b14eea026 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/amount/amount.widget.spec.ts +++ b/ng2-components/ng2-activiti-form/src/components/widgets/amount/amount.widget.spec.ts @@ -23,7 +23,7 @@ describe('AmountWidget', () => { let widget: AmountWidget; beforeEach(() => { - widget = new AmountWidget(null); + widget = new AmountWidget(); }); it('should setup currentcy from field', () => { diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/amount/amount.widget.ts b/ng2-components/ng2-activiti-form/src/components/widgets/amount/amount.widget.ts index 93086e9006..5efd7c2bb3 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/amount/amount.widget.ts +++ b/ng2-components/ng2-activiti-form/src/components/widgets/amount/amount.widget.ts @@ -15,8 +15,8 @@ * limitations under the License. */ -import { Component, ElementRef, OnInit } from '@angular/core'; -import { TextFieldWidgetComponent } from './../textfield-widget.component'; +import { Component, OnInit } from '@angular/core'; +import { WidgetComponent } from './../widget.component'; @Component({ moduleId: module.id, @@ -24,16 +24,12 @@ import { TextFieldWidgetComponent } from './../textfield-widget.component'; templateUrl: './amount.widget.html', styleUrls: ['./amount.widget.css'] }) -export class AmountWidget extends TextFieldWidgetComponent implements OnInit { +export class AmountWidget extends WidgetComponent implements OnInit { static DEFAULT_CURRENCY: string = '$'; currency: string = AmountWidget.DEFAULT_CURRENCY; - constructor(elementRef: ElementRef) { - super(elementRef); - } - ngOnInit() { if (this.field && this.field.currency) { this.currency = this.field.currency; diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/attach/attach.widget.html b/ng2-components/ng2-activiti-form/src/components/widgets/attach/attach.widget.html index e79e98dbc7..286748a802 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/attach/attach.widget.html +++ b/ng2-components/ng2-activiti-form/src/components/widgets/attach/attach.widget.html @@ -2,7 +2,7 @@
- {{getLinkedFileName()}} + {{getLinkedFileName()}}