diff --git a/src/dropdown/list/createlistdropdown.js b/src/dropdown/list/createlistdropdown.js index e22feb07..f69c545d 100644 --- a/src/dropdown/list/createlistdropdown.js +++ b/src/dropdown/list/createlistdropdown.js @@ -26,7 +26,7 @@ export default function createListDropdown( model, locale ) { const listView = dropdownView.listView = new ListView( locale ); - listView.items.bindTo( model.items ).as( itemModel => { + listView.items.bindTo( model.items ).using( itemModel => { const item = new ListItemView( locale ); // Bind all attributes of the model to the item view. diff --git a/src/viewcollection.js b/src/viewcollection.js index 5d40ed58..a51e04fd 100644 --- a/src/viewcollection.js +++ b/src/viewcollection.js @@ -11,7 +11,6 @@ import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror'; import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin'; import Collection from '@ckeditor/ckeditor5-utils/src/collection'; import mix from '@ckeditor/ckeditor5-utils/src/mix'; -import View from './view'; /** * Collects {@link module:ui/view~View} instances. @@ -70,15 +69,6 @@ export default class ViewCollection extends Collection { * @member {HTMLElement} */ this._parentElement = null; - - /** - * A helper mapping between bound collection items passed to {@link #bindTo} - * and view instances. Speeds up the view management. - * - * @protected - * @member {HTMLElement} - */ - this._boundItemsToViewsMap = new Map(); } /** @@ -150,121 +140,6 @@ export default class ViewCollection extends Collection { this._parentElement = elementOrDocFragment; } - /** - * Binds this collection to {@link module:utils/collection~Collection another collection}. For each item in the - * second collection there will be one view instance added to this collection. - * - * The process can be automatic: - * - * // This collection stores items. - * const items = new Collection( { idProperty: 'label' } ); - * - * // This view collection will become a factory out of the collection of items. - * const views = new ViewCollection( locale ); - * - * // Activate the binding – since now, this view collection works like a **factory**. - * // Each new item is passed to the FooView constructor like new FooView( locale, item ). - * views.bindTo( items ).as( FooView ); - * - * // As new items arrive to the collection, each becomes an instance of FooView - * // in the view collection. - * items.add( new Model( { label: 'foo' } ) ); - * items.add( new Model( { label: 'bar' } ) ); - * - * console.log( views.length == 2 ); - * - * // View collection is updated as the model is removed. - * items.remove( 0 ); - * console.log( views.length == 1 ); - * - * or the factory can be driven by a custom callback: - * - * // This collection stores any kind of data. - * const data = new Collection(); - * - * // This view collection will become a custom factory for the data. - * const views = new ViewCollection( locale ); - * - * // Activate the binding – the **factory** is driven by a custom callback. - * views.bindTo( data ).as( item => { - * if ( !item.foo ) { - * return null; - * } else if ( item.foo == 'bar' ) { - * return new BarView(); - * } else { - * return new DifferentView(); - * } - * } ); - * - * // As new data arrive to the collection, each is handled individually by the callback. - * // This will produce BarView. - * data.add( { foo: 'bar' } ); - * - * // And this one will become DifferentView. - * data.add( { foo: 'baz' } ); - * - * // Also there will be no view for data lacking the `foo` property. - * data.add( {} ); - * - * console.log( controllers.length == 2 ); - * - * // View collection is also updated as the data is removed. - * data.remove( 0 ); - * console.log( controllers.length == 1 ); - * - * @param {module:utils/collection~Collection} collection A collection to be bound. - * @returns {module:ui/viewcollection~ViewCollection#bindTo#as} - */ - bindTo( collection ) { - return { - /** - * Determines the output view of the binding. - * - * @static - * @param {Function|module:ui/view~View} CallbackOrViewClass Specifies the constructor of the view to be used or - * a custom callback function which produces views. - */ - as: ( CallbackOrViewClass ) => { - let createView; - - if ( CallbackOrViewClass.prototype instanceof View ) { - createView = ( item ) => { - const viewInstance = new CallbackOrViewClass( this.locale, item ); - - this._boundItemsToViewsMap.set( item, viewInstance ); - - return viewInstance; - }; - } else { - createView = ( item ) => { - const viewInstance = CallbackOrViewClass( item ); - - this._boundItemsToViewsMap.set( item, viewInstance ); - - return viewInstance; - }; - } - - // Load the initial content of the collection. - for ( let item of collection ) { - this.add( createView( item ) ); - } - - // Synchronize views as new items are added to the collection. - this.listenTo( collection, 'add', ( evt, item, index ) => { - this.add( createView( item ), index ); - } ); - - // Synchronize views as items are removed from the collection. - this.listenTo( collection, 'remove', ( evt, item ) => { - this.remove( this._boundItemsToViewsMap.get( item ) ); - - this._boundItemsToViewsMap.delete( item ); - } ); - } - }; - } - /** * Delegates selected events coming from within the collection to desired {@link module:utils/emittermixin~EmitterMixin}. * diff --git a/tests/viewcollection.js b/tests/viewcollection.js index 9a7612e7..8f0823fc 100644 --- a/tests/viewcollection.js +++ b/tests/viewcollection.js @@ -6,7 +6,6 @@ /* global document, setTimeout */ import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror'; -import Collection from '@ckeditor/ckeditor5-utils/src/collection'; import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils'; import View from '../src/view'; import ViewCollection from '../src/viewcollection'; @@ -25,7 +24,6 @@ describe( 'ViewCollection', () => { expect( collection.locale ).to.be.undefined; expect( collection.ready ).to.be.false; expect( collection._parentElement ).to.be.null; - expect( collection._boundItemsToViewsMap ).to.be.instanceOf( Map ); expect( collection._idProperty ).to.equal( 'viewUid' ); } ); @@ -291,139 +289,6 @@ describe( 'ViewCollection', () => { } ); } ); - describe( 'bindTo()', () => { - class ViewClass extends View { - constructor( locale, data ) { - super( locale ); - - this.template = new Template( { - tag: 'b' - } ); - - this.data = data; - } - } - - it( 'provides "as()" interface', () => { - const returned = collection.bindTo( {} ); - - expect( returned ).to.have.keys( 'as' ); - expect( returned.as ).to.be.a( 'function' ); - } ); - - describe( 'as()', () => { - it( 'does not chain', () => { - const returned = collection.bindTo( new Collection() ).as( ViewClass ); - - expect( returned ).to.be.undefined; - } ); - - it( 'binds collection as a view factory – initial content', () => { - const locale = {}; - const items = new Collection(); - - items.add( { id: '1' } ); - items.add( { id: '2' } ); - - collection = new ViewCollection( locale ); - collection.bindTo( items ).as( ViewClass ); - - expect( collection ).to.have.length( 2 ); - expect( collection.get( 0 ) ).to.be.instanceOf( ViewClass ); - expect( collection.get( 1 ) ).to.be.instanceOf( ViewClass ); - expect( collection.get( 0 ).locale ).to.equal( locale ); - expect( collection.get( 1 ).data ).to.equal( items.get( 1 ) ); - } ); - - it( 'binds collection as a view factory – new content', () => { - const locale = {}; - const items = new Collection(); - - collection = new ViewCollection( locale ); - collection.bindTo( items ).as( ViewClass ); - - expect( collection ).to.have.length( 0 ); - - items.add( { id: '1' } ); - items.add( { id: '2' } ); - - expect( collection ).to.have.length( 2 ); - expect( collection.get( 0 ) ).to.be.instanceOf( ViewClass ); - expect( collection.get( 1 ) ).to.be.instanceOf( ViewClass ); - expect( collection.get( 0 ).locale ).to.equal( locale ); - expect( collection.get( 1 ).data ).to.equal( items.get( 1 ) ); - } ); - - it( 'binds collection as a view factory – item removal', () => { - const locale = {}; - const items = new Collection(); - - collection = new ViewCollection( locale ); - collection.bindTo( items ).as( ViewClass ); - - expect( collection ).to.have.length( 0 ); - - items.add( { id: '1' } ); - items.add( { id: '2' } ); - - expect( collection ).to.have.length( 2 ); - expect( collection.get( 0 ) ).to.be.instanceOf( ViewClass ); - expect( collection.get( 1 ) ).to.be.instanceOf( ViewClass ); - expect( collection.get( 0 ).locale ).to.equal( locale ); - expect( collection.get( 1 ).data ).to.equal( items.get( 1 ) ); - - items.remove( 1 ); - expect( collection.get( 0 ).data ).to.equal( items.get( 0 ) ); - - items.remove( 0 ); - expect( collection ).to.have.length( 0 ); - } ); - - it( 'binds collection as a view factory – custom factory (arrow function)', () => { - const locale = {}; - const items = new Collection(); - - collection = new ViewCollection( locale ); - collection.bindTo( items ).as( ( item ) => { - return new ViewClass( locale, item ); - } ); - - expect( collection ).to.have.length( 0 ); - - items.add( { id: '1' } ); - items.add( { id: '2' } ); - - expect( collection ).to.have.length( 2 ); - expect( collection.get( 0 ) ).to.be.instanceOf( ViewClass ); - expect( collection.get( 1 ) ).to.be.instanceOf( ViewClass ); - expect( collection.get( 0 ).locale ).to.equal( locale ); - expect( collection.get( 1 ).data ).to.equal( items.get( 1 ) ); - } ); - - // https://github.com/ckeditor/ckeditor5-ui/issues/113 - it( 'binds collection as a view factory – custom factory (normal function)', () => { - const locale = { locale: true }; - const items = new Collection(); - - collection = new ViewCollection( locale ); - collection.bindTo( items ).as( function( item ) { - return new ViewClass( locale, item ); - } ); - - items.add( { id: '1' } ); - - expect( collection ).to.have.length( 1 ); - - const view = collection.get( 0 ); - - // Wrong args will be passed to the callback if it's treated as the view constructor. - expect( view ).to.be.instanceOf( ViewClass ); - expect( view.locale ).to.equal( locale ); - expect( view.data ).to.equal( items.get( 0 ) ); - } ); - } ); - } ); - describe( 'delegate()', () => { it( 'should throw when event names are not strings', () => { expect( () => {