blob: 272a423129a5fc80a6644d6b0f7d5cdfdd68b14f [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 {default as ListEditable, ItemScope} from '../../controller';
type TranscludedScope<T> = {$form: ng.IFormController, $item?: T} & ng.IScope
/**
* Transcludes list-editable slots and proxies item and form scope values to the slot scope,
* also provides a way to retrieve internal list-editable ng-repeat $index by controller getter.
* User can provide an alias for $item by setting item-name attribute on transclusion slot element.
*/
export class ListEditableTransclude<T> {
/**
* Transcluded slot name.
*/
slot: string;
list: ListEditable<T>;
static $inject = ['$scope', '$element'];
constructor(private $scope: ItemScope<T>, private $element: JQLite) {}
$postLink() {
this.list.$transclude((clone, transcludedScope: TranscludedScope<T>) => {
// Ilya Borisov: at first I tried to use a slave directive to get value from
// attribute and set it to ListEditableTransclude controller, but it turns out
// this directive would run after list-editable-transclude, so that approach
// doesn't work. That's why I decided to access a raw DOM attribute instead.
const itemName = clone.attr('item-name') || '$item';
// We don't want to keep references to any parent scope objects
transcludedScope.$on('$destroy', () => {
delete transcludedScope[itemName];
delete transcludedScope.$form;
});
Object.defineProperties(transcludedScope, {
[itemName]: {
get: () => {
// Scope might get destroyed
if (!this.$scope)
return;
return this.$scope.item;
},
set: (value) => {
// There are two items: the original one from collection and an item from
// cache that will be saved, so the latter should be the one we set.
if (!this.$scope)
return;
this.$scope.item = value;
},
// Allows to delete property later
configurable: true
},
$form: {
get: () => {
// Scope might get destroyed
if (!this.$scope)
return;
return this.$scope.form;
},
// Allows to delete property later
configurable: true
}
});
this.$element.append(clone);
}, null, this.slot);
}
/**
* Returns list-editable ng-repeat $index.
*/
get $index() {
if (!this.$scope)
return;
return this.$scope.$index;
}
$onDestroy() {
this.$scope = this.$element = null;
}
}
export function listEditableTransclude() {
return {
restrict: 'A',
require: {
list: '^listEditable'
},
scope: false,
controller: ListEditableTransclude,
bindToController: {
slot: '@listEditableTransclude'
}
};
}