#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 {
width: 100%;
min-height: 100px;
overflow: visible;
}
.activiti-form-container > .mdl-card__media {

View File

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

View File

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

View File

@@ -24,6 +24,7 @@ export class FormFieldTypes {
static DISPLAY_VALUE: string = 'readonly';
static READONLY_TEXT: string = 'readonly-text';
static UPLOAD: string = 'upload';
static TYPEAHEAD: string = 'typeahead';
static READONLY_TYPES: string[] = [
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
but saving back as object: { id: <id>, name: <name> }
*/
let entry: FormFieldOption[] = this.options.filter(opt => opt.id === this.value);
if (entry.length > 0) {
this.form.values[this.id] = entry[0];
let rbEntry: FormFieldOption[] = this.options.filter(opt => opt.id === this.value);
if (rbEntry.length > 0) {
this.form.values[this.id] = rbEntry[0];
} else if (this.options.length > 0) {
this.form.values[this.id] = this.options[0];
}
@@ -164,6 +164,14 @@ export class FormFieldModel extends FormWidgetModel {
this.form.values[this.id] = null;
}
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:
if (!FormFieldTypes.isReadOnlyType(this.type)) {
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 { DisplayTextWidget } from './display-text/display-text.widget';
import { UploadWidget } from './upload/upload.widget';
import { TypeaheadWidget } from './typeahead/typeahead.widget';
// core
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-text/display-text.widget';
export * from './upload/upload.widget';
export * from './typeahead/typeahead.widget';
export const CONTAINER_WIDGET_DIRECTIVES: [any] = [
TabsWidget,
@@ -64,7 +66,8 @@ export const PRIMITIVE_WIDGET_DIRECTIVES: [any] = [
RadioButtonsWidget,
DisplayValueWidget,
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);
}
getRestFieldValues(taskId: string, field: string): Promise<any> {
let alfrescoApi = this.authService.getAlfrescoApi();
return alfrescoApi.activiti.taskFormsApi.getRestFieldValues(taskId, field);
}
getFormId(res: any) {
let result = null;