#633 basic typeahead implementation

This commit is contained in:
Denys Vuika
2016-09-05 21:45:07 +01:00
parent e66b0ad516
commit 679cdfe6cf
10 changed files with 186 additions and 9 deletions

View File

@@ -1,6 +1,7 @@
.activiti-form-container { .activiti-form-container {
width: 100%; width: 100%;
min-height: 100px; min-height: 100px;
overflow: visible;
} }
.activiti-form-container > .mdl-card__media { .activiti-form-container > .mdl-card__media {

View File

@@ -41,11 +41,13 @@ will be removed during future revisions
--> -->
<div class="activiti-form-debug-container"> <div class="activiti-form-debug-container">
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="switch-1" [class.is-checked]="debugMode"> <div style="float: right">
<input type="checkbox" id="switch-1" class="mdl-switch__input" [(ngModel)]="debugMode"> <label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="switch-1" [class.is-checked]="debugMode">
<span class="mdl-switch__label"></span> <input type="checkbox" id="switch-1" class="mdl-switch__input" [(ngModel)]="debugMode">
<span class="debug-toggle-text">Debug mode</span> <span class="mdl-switch__label"></span>
</label> <span class="debug-toggle-text">Debug mode</span>
</label>
</div>
<div *ngIf="debugMode && hasForm()"> <div *ngIf="debugMode && hasForm()">
<h4>Values</h4> <h4>Values</h4>

View File

@@ -46,6 +46,9 @@
<div *ngSwitchCase="'upload'"> <div *ngSwitchCase="'upload'">
<upload-widget [field]="field" (fieldChanged)="fieldChanged($event);"></upload-widget> <upload-widget [field]="field" (fieldChanged)="fieldChanged($event);"></upload-widget>
</div> </div>
<div *ngSwitchCase="'typeahead'">
<typeahead-widget [field]="field" (fieldChanged)="fieldChanged($event);"></typeahead-widget>
</div>
<div *ngSwitchDefault> <div *ngSwitchDefault>
<span>UNKNOWN WIDGET TYPE: {{field.type}}</span> <span>UNKNOWN WIDGET TYPE: {{field.type}}</span>
</div> </div>

View File

@@ -24,6 +24,7 @@ export class FormFieldTypes {
static DISPLAY_VALUE: string = 'readonly'; static DISPLAY_VALUE: string = 'readonly';
static READONLY_TEXT: string = 'readonly-text'; static READONLY_TEXT: string = 'readonly-text';
static UPLOAD: string = 'upload'; static UPLOAD: string = 'upload';
static TYPEAHEAD: string = 'typeahead';
static READONLY_TYPES: string[] = [ static READONLY_TYPES: string[] = [
FormFieldTypes.HYPERLINK, FormFieldTypes.HYPERLINK,

View File

@@ -150,9 +150,9 @@ export class FormFieldModel extends FormWidgetModel {
This is needed due to Activiti issue related to reading radio button values as value string This is needed due to Activiti issue related to reading radio button values as value string
but saving back as object: { id: <id>, name: <name> } but saving back as object: { id: <id>, name: <name> }
*/ */
let entry: FormFieldOption[] = this.options.filter(opt => opt.id === this.value); let rbEntry: FormFieldOption[] = this.options.filter(opt => opt.id === this.value);
if (entry.length > 0) { if (rbEntry.length > 0) {
this.form.values[this.id] = entry[0]; this.form.values[this.id] = rbEntry[0];
} else if (this.options.length > 0) { } else if (this.options.length > 0) {
this.form.values[this.id] = this.options[0]; this.form.values[this.id] = this.options[0];
} }
@@ -164,6 +164,14 @@ export class FormFieldModel extends FormWidgetModel {
this.form.values[this.id] = null; this.form.values[this.id] = null;
} }
break; break;
case FormFieldTypes.TYPEAHEAD:
let taEntry: FormFieldOption[] = this.options.filter(opt => opt.id === this.value);
if (taEntry.length > 0) {
this.form.values[this.id] = taEntry[0];
} else if (this.options.length > 0) {
this.form.values[this.id] = null;
}
break;
default: default:
if (!FormFieldTypes.isReadOnlyType(this.type)) { if (!FormFieldTypes.isReadOnlyType(this.type)) {
this.form.values[this.id] = this.value; this.form.values[this.id] = this.value;

View File

@@ -28,6 +28,7 @@ import { RadioButtonsWidget } from './radio-buttons/radio-buttons.widget';
import { DisplayValueWidget } from './display-value/display-value.widget'; import { DisplayValueWidget } from './display-value/display-value.widget';
import { DisplayTextWidget } from './display-text/display-text.widget'; import { DisplayTextWidget } from './display-text/display-text.widget';
import { UploadWidget } from './upload/upload.widget'; import { UploadWidget } from './upload/upload.widget';
import { TypeaheadWidget } from './typeahead/typeahead.widget';
// core // core
export * from './widget.component'; export * from './widget.component';
@@ -48,6 +49,7 @@ export * from './radio-buttons/radio-buttons.widget';
export * from './display-value/display-value.widget'; export * from './display-value/display-value.widget';
export * from './display-text/display-text.widget'; export * from './display-text/display-text.widget';
export * from './upload/upload.widget'; export * from './upload/upload.widget';
export * from './typeahead/typeahead.widget';
export const CONTAINER_WIDGET_DIRECTIVES: [any] = [ export const CONTAINER_WIDGET_DIRECTIVES: [any] = [
TabsWidget, TabsWidget,
@@ -64,7 +66,8 @@ export const PRIMITIVE_WIDGET_DIRECTIVES: [any] = [
RadioButtonsWidget, RadioButtonsWidget,
DisplayValueWidget, DisplayValueWidget,
DisplayTextWidget, DisplayTextWidget,
UploadWidget UploadWidget,
TypeaheadWidget
]; ];

View File

@@ -0,0 +1,29 @@
.typeahead-widget {
width: 100%;
}
.typeahead-autocomplete {
background-color: #fff;
position: absolute;
z-index: 5;
color: #555;
margin: -15px 0 0 0;
}
.typeahead-autocomplete > ul {
list-style-type: none;
position: static;
height: auto;
width: auto;
min-width: 124px;
padding: 8px 0;
margin: 0;
box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);
border-radius: 2px;
}
.typeahead-autocomplete > ul > li {
opacity: 1;
}

View File

@@ -0,0 +1,22 @@
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label typeahead-widget"
[class.is-dirty]="value">
<input class="mdl-textfield__input"
type="text"
[attr.id]="field.id"
[(ngModel)]="value"
(ngModelChange)="checkVisibility(field)"
(keyup)="onKey($event)"
(blur)="onBlur()"
[disabled]="field.readOnly">
<label class="mdl-textfield__label" [attr.for]="field.id">{{field.name}}</label>
</div>
<div class="typeahead-autocomplete mdl-shadow--2dp" *ngIf="popupVisible">
<ul>
<li *ngFor="let item of getOptions()"
class="mdl-menu__item"
(click)="onItemClick(item, $event)">
{{item.name}}
</li>
</ul>
</div>

View File

@@ -0,0 +1,103 @@
/*!
* @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 } from '@angular/core';
import { FormService } from './../../../services/form.service';
import { WidgetComponent } from './../widget.component';
import { FormFieldOption } from './../core/form-field-option';
declare let __moduleName: string;
declare var componentHandler;
@Component({
moduleId: __moduleName,
selector: 'typeahead-widget',
templateUrl: './typeahead.widget.html',
styleUrls: ['./typeahead.widget.css']
})
export class TypeaheadWidget extends WidgetComponent implements OnInit {
popupVisible: boolean = false;
minTermLength: number = 1;
value: string;
constructor(private formService: FormService) {
super();
}
ngOnInit() {
this.formService
.getRestFieldValues(
this.field.form.taskId,
this.field.id
)
.then(
(result: FormFieldOption[]) => {
let options = result || [];
this.field.options = options;
this.field.updateForm();
let fieldValue = this.field.value;
if (fieldValue) {
let toSelect = options.find(item => item.id === fieldValue);
if (toSelect) {
this.value = toSelect.name;
}
}
},
error => console.log(error)
);
}
getOptions(): FormFieldOption[] {
let val = this.value.toLocaleLowerCase();
return this.field.options.filter(item => {
let name = item.name.toLocaleLowerCase();
return name.indexOf(val) > -1;
});
}
onKey(event: KeyboardEvent) {
// this.values += (<HTMLInputElement>event.target).value + ' | ';
this.popupVisible = !!(this.value && this.value.length >= this.minTermLength);
}
onBlur() {
setTimeout(() => {
this.popupVisible = false;
let options = this.field.options || [];
let field = options.find(item => item.name.toLocaleLowerCase() === this.value.toLocaleLowerCase());
if (field) {
this.field.value = field.id;
this.value = field.name;
} else {
this.field.value = null;
this.value = null;
}
this.field.updateForm();
}, 200);
}
onItemClick(item: FormFieldOption, event: Event) {
event.preventDefault();
this.field.value = item.id;
this.value = item.name;
}
}

View File

@@ -212,6 +212,11 @@ export class FormService {
.catch(this.handleError); .catch(this.handleError);
} }
getRestFieldValues(taskId: string, field: string): Promise<any> {
let alfrescoApi = this.authService.getAlfrescoApi();
return alfrescoApi.activiti.taskFormsApi.getRestFieldValues(taskId, field);
}
getFormId(res: any) { getFormId(res: any) {
let result = null; let result = null;