blob: d4d9da62ade674297cb7e21e432ca9751dd46108 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 _ from 'lodash';
export interface ListEditableNgModel<T> extends ng.INgModelController {
$viewValue: T[],
editListItem(item: T): void,
editListIndex(index: number): void
}
export type ID = (string | number) & {tag: 'ItemID'}
export type ItemScope<T> = {$index: number, item: T, form: ng.IFormController} & ng.IScope
export default class ListEditable<T extends {_id?: any}> {
static $inject = ['$animate', '$element', '$transclude', '$timeout'];
constructor(
$animate: ng.animate.IAnimateService,
public $element: JQLite,
public $transclude: ng.ITranscludeFunction,
private $timeout: ng.ITimeoutService
) {
$animate.enabled($element, false);
this.hasItemView = $transclude.isSlotFilled('itemView');
this._cache = new Map();
}
ngModel: ListEditableNgModel<T>;
hasItemView: boolean;
private _cache: Map<ID, T>;
id(item: T | undefined, index: number): ID {
if (item && item._id)
return item._id as ID;
return index as ID;
}
$onDestroy() {
this.$element = null;
}
$onInit() {
this.ngModel.$isEmpty = (value) => {
return !Array.isArray(value) || !value.length;
};
this.ngModel.editListItem = (item) => {
this.$timeout(() => {
this.startEditView(this.id(item, this.ngModel.$viewValue.indexOf(item)));
// For some reason required validator does not re-run after adding an item,
// the $validate call fixes the issue.
this.ngModel.$validate();
});
};
this.ngModel.editListIndex = (index) => {
this.$timeout(() => {
this.startEditView(this.id(this.ngModel.$viewValue[index], index));
// For some reason required validator does not re-run after adding an item,
// the $validate call fixes the issue.
this.ngModel.$validate();
});
};
}
save(item: T, id: ID) {
this.ngModel.$setViewValue(
this.ngModel.$viewValue.map((v, i) => this.id(v, i) === id ? _.cloneDeep(item) : v)
);
}
remove(id: ID): void {
this.ngModel.$setViewValue(this.ngModel.$viewValue.filter((v, i) => this.id(v, i) !== id));
}
isEditView(id: ID): boolean {
return this._cache.has(id);
}
getEditView(id: ID): T {
return this._cache.get(id);
}
getItem(id: ID): T {
return this.ngModel.$viewValue.find((v, i) => this.id(v, i) === id);
}
startEditView(id: ID) {
this._cache.set(
id,
_.cloneDeep(this.getItem(id))
);
}
stopEditView(data: T, id: ID, form: ng.IFormController) {
// By default list-editable saves only valid values, but if you specify {allowInvalid: true}
// ng-model-option, then it will always save. Be careful and pay extra attention to validation
// when doing so, it's an easy way to miss invalid values this way.
// Don't close if form is invalid and allowInvalid is turned off (which is default value)
if (!form.$valid && !this.ngModel.$options.getOption('allowInvalid'))
return;
this._cache.delete(id);
this.save(data, id);
}
}