瀏覽代碼

Implemented ControllerCollection.bind#as API, also with custom factory callback.

Aleksander Nowodzinski 9 年之前
父節點
當前提交
9b8fe7e859
共有 1 個文件被更改,包括 129 次插入43 次删除
  1. 129 43
      packages/ckeditor5-ui/src/controllercollection.js

+ 129 - 43
packages/ckeditor5-ui/src/controllercollection.js

@@ -20,14 +20,12 @@ export default class ControllerCollection extends Collection {
 	 *		collection.add( someController );
 	 *
 	 * **Note**: If needed, controller collection can stay in sync with a collection of models to
-	 * manage list–like components. See {@link ui.ControllerCollection._sync} to learn more.
+	 * manage list–like components. See {@link ui.ControllerCollection#bind} to learn more.
 	 *
 	 * @param {String} name Name of the collection.
-	 * @param {utils.Collection.<ui.Model>} [models] Models to be synchronized with this controller collection.
-	 * @param {Function} [ControllerClass] Specifies the constructor of the controller to be used for each model.
-	 * @param {Function} [ViewClass] Specifies constructor of the view.
+	 * @param {utils.Locale} [locale] The {@link ckeditor5.Editor#locale editor's locale} instance.
 	 */
-	constructor( name, models, ControllerClass, ViewClass ) {
+	constructor( name, locale ) {
 		super();
 
 		if ( !name ) {
@@ -47,15 +45,19 @@ export default class ControllerCollection extends Collection {
 		this.name = name;
 
 		/**
+		 * See {@link ui.View#locale}
+		 *
+		 * @readonly
+		 * @member {utils.Locale} ui.ControllerCollection#locale
+		 */
+		this.locale = locale;
+
+		/**
 		 * Parent controller of this collection.
 		 *
 		 * @member {ui.Controller} ui.ControllerCollection#parent
 		 */
 		this.parent = null;
-
-		if ( models ) {
-			this._sync( models, ControllerClass, ViewClass );
-		}
 	}
 
 	/**
@@ -83,60 +85,144 @@ export default class ControllerCollection extends Collection {
 
 	/**
 	 * Synchronizes controller collection with a {@link utils.Collection} of {@link ui.Model} instances.
-	 * The entire collection of controllers reflects changes to the collection of the models.
+	 * The entire collection of controllers reflects changes to the collection of the models, working as a factory.
+	 *
+	 * This method helps bringing complex list–like UI components to life out of the data (like models). The process
+	 * can be automatic:
+	 *
+	 *		// This collection stores models.
+	 *		const models = new Collection( { idProperty: 'label' } );
+	 *
+	 *		// This controller collection will become a factory for the collection of models.
+	 *		const controllers = new ControllerCollection( 'foo', locale );
 	 *
-	 * This method helps bringing complex list–like UI components to life out of the data (model).
+	 *		// Activate the binding – since now, this controller collection works like a **factory**.
+	 *		controllers.bind( models ).as( FooController, FooView );
 	 *
-	 *		const models = new Collection();
-	 *		const controllers = new ControllerCollection( 'foo', models, FooController, FooView );
+	 *		// As new models arrive to the collection, each becomes an instance of FooController (FooView)
+	 *		// in the controller collection.
+	 *		models.add( new Model( { label: 'foo' } ) );
+	 *		models.add( new Model( { label: 'bar' } ) );
 	 *
-	 *		// Each model becomes an instance of FooController and FooView in the controller collection.
-	 *		models.add( new Model( { ... } ) );
-	 *		models.add( new Model( { ... } ) );
 	 *		console.log( controllers.length == 2 );
 	 *
 	 *		// Controller collection is updated as the model is removed.
-	 *		models.remove( ... );
+	 *		models.remove( 0 );
 	 *		console.log( controllers.length == 1 );
 	 *
+	 * or the factory can be driven by a custom callback:
+	 *
+	 *		// This collection stores any kind of data.
+	 *		const data = new Collection();
+	 *
+	 *		// This controller collection will become a custom factory for the data.
+	 *		const controllers = new ControllerCollection( 'foo', locale );
+	 *
+	 *		// Activate the binding – the **factory** is driven by a custom callback.
+	 *		controllers.bind( data ).as( ( item, locale ) => {
+	 *			if ( item.foo == 'bar' ) {
+	 *				return new BarController( ..., BarView( locale ) );
+	 *			} else {
+	 *				return new DifferentController( ..., DifferentView( locale ) );
+	 *			}
+	 *		} );
+	 *
+	 *		// As new data arrive to the collection, each is handled individually by the callback.
+	 *		// This will produce BarController.
+	 *		data.add( { foo: 'bar' } );
+	 *
+	 *		// And this one will become DifferentController.
+	 *		data.add( { foo: 'baz' } );
+	 *
+	 *		console.log( controllers.length == 2 );
+	 *
+	 *		// Controller collection is updated as the data is removed.
+	 *		data.remove( 0 );
+	 *		console.log( controllers.length == 1 );
+	 *
+	 *
 	 * @param {utils.Collection.<ui.Model>} models Models to be synchronized with this controller collection.
-	 * @param {Function} ControllerClass Specifies the constructor of the controller to be used.
-	 * @param {Function} ViewClass Specifies constructor of the view.
 	 */
-	_sync( models, ControllerClass, ViewClass ) {
+	bind( models ) {
 		const idProperty = models._idProperty;
 
-		for ( let model of models ) {
-			// TODO: View locale.
-			this.add( getController( model, ControllerClass, ViewClass, idProperty ) );
-		}
-
-		// Updated controller collection when a new model is added.
-		models.on( 'add', ( evt, model, index ) => {
-			this.add( getController( model, ControllerClass, ViewClass, idProperty ), index );
-		} );
-
-		// Update controller collection when a model is removed.
-		models.on( 'remove', ( evt, model ) => {
-			this.remove( model[ idProperty ] );
-		} );
+		return {
+			/**
+			 * Activates factory using controller and view classes or uses a custom callback to produce
+			 * controller (view) instances.
+			 *
+			 * @method ui.ControllerCollection.bind#as
+			 * @param {Function} ControllerClassOrFunction Specifies the constructor of the controller to be used or
+			 * a custom callback function which produces controllers.
+	 		 * @param {Function} [ViewClass] Specifies constructor of the view to be used. If not specified,
+	 		 * `ControllerClassOrFunction` works as as custom callback function.
+			 */
+			as: ( ControllerClassOrFunction, ViewClass ) => {
+				const createController = ViewClass ?
+						defaultControllerFactory( ControllerClassOrFunction, ViewClass, idProperty )
+					:
+						customControllerFactory( ControllerClassOrFunction, idProperty );
+
+				for ( let model of models ) {
+					this.add( createController( model, this.locale ) );
+				}
+
+				// Updated controller collection when a new model is added.
+				models.on( 'add', ( evt, model, index ) => {
+					this.add( createController( model, this.locale ), index );
+				} );
+
+				// Update controller collection when a model is removed.
+				models.on( 'remove', ( evt, model ) => {
+					this.remove( model[ idProperty ] );
+				} );
+			}
+		};
 	}
 }
 
-// Returns a controller instance (with a view) for given model and class names.
+// Initializes controller factory with controller and view classes.
 //
-// @param {ui.Model} model A model of the controller.
 // @param {Function} ControllerClass Specifies the constructor of the controller to be used.
 // @param {Function} ViewClass Specifies constructor of the view.
 // @param {String} idProperty A property used to associate the controller with its model {@link utils.Collection._idProperty}.
-// @returns {ui.Controller}
-function getController( model, ControllerClass, ViewClass, idProperty ) {
-	let controller = new ControllerClass( model, new ViewClass() );
+// @returns {Function}
+function defaultControllerFactory( ControllerClass, ViewClass, idProperty ) {
+	// Returns a controller instance (and its view) for given model and class names.
+	//
+	// @param {ui.Model} model A model of the controller.
+	// @param {utils.Locale} [locale] The {@link ckeditor5.Editor#locale editor's locale} instance.
+	// @returns {ui.Controller}
+	return ( model, locale ) => {
+		return flagController( new ControllerClass( model, new ViewClass( locale ) ), idProperty );
+	};
+}
+
+// Initializes controller factory which is fed by a custom callback.
+//
+// @param {Function} callback A callback which is to return an instance of {@link ui.Controller}.
+// @param {String} idProperty A property used to associate the controller with its model {@link utils.Collection._idProperty}.
+// @returns {Function}
+function customControllerFactory( callback, idProperty ) {
+	// Returns a controller instance (and its view) produced by the custom callback.
+	//
+	// @param {ui.Model} model A model of the controller.
+	// @param {utils.Locale} [locale] The {@link ckeditor5.Editor#locale editor's locale} instance.
+	// @returns {ui.Controller}
+	return ( ...args ) => {
+		return flagController( callback( ...args ), idProperty );
+	};
+}
 
-	// Give the new controller an id corresponding with _idProperty of the model.
-	// It allows retrieving this controller instance by the model in the future
-	// and avoids a brute–force search in the entire controller collection.
-	controller.id = model[ idProperty ];
+// Gives the controller an id corresponding with {@link utils.Collection#_idProperty} of the model.
+// It allows retrieving this controller instance by the model in the future
+// and avoids a brute–force search in the entire controller collection.
+//
+// @param {ui.Controller} controller An instance of controller.
+// @param {String} idProperty A property used to associate the controller with its model {@link utils.Collection._idProperty}.
+// @returns {ui.Controller}
+function flagController( controller, idProperty ) {
+	controller.id = controller.model[ idProperty ];
 
 	return controller;
 }