@@ -113,6 +113,21 @@ export default class DataController {
113113 * @returns {String } Output data.
114114 */
115115 get ( rootName = 'main' ) {
116+ if ( ! this . _checkIfRootsExists ( [ rootName ] ) ) {
117+ /**
118+ * Cannot get data from a non-existing root. This error is thrown when {@link #get DataController#get() method}
119+ * is called with non-existent root name. For example, if there is an editor instance with only `main` root,
120+ * calling {@link #get} like:
121+ *
122+ * data.get( 'root2' );
123+ *
124+ * will throw this error.
125+ *
126+ * @error datacontroller-get-non-existent-root
127+ */
128+ throw new CKEditorError ( 'datacontroller-get-non-existent-root: Attempting to get data from a non-existing root.' ) ;
129+ }
130+
116131 // Get model range.
117132 return this . stringify ( this . model . document . getRoot ( rootName ) ) ;
118133 }
@@ -181,12 +196,20 @@ export default class DataController {
181196 * **Note** This method is {@link module:utils/observablemixin~ObservableMixin#decorate decorated} which is
182197 * used by e.g. collaborative editing plugin that syncs remote data on init.
183198 *
199+ * When data is passed as a string it is initialized on a default `main` root:
200+ *
201+ * dataController.init( '<p>Foo</p>' ); // Initializes data on the `main` root.
202+ *
203+ * To initialize data on a different root or multiple roots at once, object containing `rootName` - `data` pairs should be passed:
204+ *
205+ * dataController.init( { main: '<p>Foo</p>', title: '<h1>Bar</h1>' } ); // Initializes data on the `main` and `title` roots.
206+ *
184207 * @fires init
185- * @param {String } data Input data.
186- * @param { String } [rootName='main'] Root name .
208+ * @param {String|Object.<String,String> } data Input data as a string or an object containing `rootName` - `data`
209+ * pairs to initialize data on multiple roots at once .
187210 * @returns {Promise } Promise that is resolved after the data is set on the editor.
188211 */
189- init ( data , rootName = 'main' ) {
212+ init ( data ) {
190213 if ( this . model . document . version ) {
191214 /**
192215 * Cannot set initial data to not empty {@link module:engine/model/document~Document}.
@@ -198,10 +221,33 @@ export default class DataController {
198221 throw new CKEditorError ( 'datacontroller-init-document-not-empty: Trying to set initial data to not empty document.' ) ;
199222 }
200223
201- const modelRoot = this . model . document . getRoot ( rootName ) ;
224+ let initialData = { } ;
225+ if ( typeof data === 'string' ) {
226+ initialData . main = data ; // Default root is 'main'. To initiate data on a different root, object should be passed.
227+ } else {
228+ initialData = data ;
229+ }
230+
231+ if ( ! this . _checkIfRootsExists ( Object . keys ( initialData ) ) ) {
232+ /**
233+ * Cannot init data on a non-existing root. This error is thrown when {@link #init DataController#init() method}
234+ * is called with non-existent root name. For example, if there is an editor instance with only `main` root,
235+ * calling {@link #init} like:
236+ *
237+ * data.init( { main: '<p>Foo</p>', root2: '<p>Bar</p>' } );
238+ *
239+ * will throw this error.
240+ *
241+ * @error datacontroller-init-non-existent-root
242+ */
243+ throw new CKEditorError ( 'datacontroller-init-non-existent-root: Attempting to init data on a non-existing root.' ) ;
244+ }
202245
203246 this . model . enqueueChange ( 'transparent' , writer => {
204- writer . insert ( this . parse ( data , modelRoot ) , modelRoot , 0 ) ;
247+ for ( const rootName of Object . keys ( initialData ) ) {
248+ const modelRoot = this . model . document . getRoot ( rootName ) ;
249+ writer . insert ( this . parse ( initialData [ rootName ] , modelRoot ) , modelRoot , 0 ) ;
250+ }
205251 } ) ;
206252
207253 return Promise . resolve ( ) ;
@@ -216,19 +262,51 @@ export default class DataController {
216262 * This method also creates a batch with all the changes applied. If all you need is to parse data, use
217263 * the {@link #parse} method.
218264 *
219- * @param {String } data Input data.
220- * @param {String } [rootName='main'] Root name.
265+ * When data is passed as a string it is set on a default `main` root:
266+ *
267+ * dataController.set( '<p>Foo</p>' ); // Sets data on the `main` root.
268+ *
269+ * To set data on a different root or multiple roots at once, object containing `rootName` - `data` pairs should be passed:
270+ *
271+ * dataController.set( { main: '<p>Foo</p>', title: '<h1>Bar</h1>' } ); // Sets data on the `main` and `title` roots.
272+ *
273+ * @param {String|Object.<String,String> } data Input data as a string or an object containing `rootName` - `data`
274+ * pairs to set data on multiple roots at once.
221275 */
222- set ( data , rootName = 'main' ) {
223- // Save to model.
224- const modelRoot = this . model . document . getRoot ( rootName ) ;
276+ set ( data ) {
277+ let newData = { } ;
278+ if ( typeof data === 'string' ) {
279+ newData . main = data ; // Default root is 'main'. To set data on a different root, object should be passed.
280+ } else {
281+ newData = data ;
282+ }
283+
284+ if ( ! this . _checkIfRootsExists ( Object . keys ( newData ) ) ) {
285+ /**
286+ * Cannot set data on a non-existing root. This error is thrown when {@link #set DataController#set() method}
287+ * is called with non-existent root name. For example, if there is an editor instance with only `main` root,
288+ * calling {@link #set} like:
289+ *
290+ * data.set( { main: '<p>Foo</p>', root2: '<p>Bar</p>' } );
291+ *
292+ * will throw this error.
293+ *
294+ * @error datacontroller-set-non-existent-root
295+ */
296+ throw new CKEditorError ( 'datacontroller-set-non-existent-root: Attempting to set data on a non-existing root.' ) ;
297+ }
225298
226299 this . model . enqueueChange ( 'transparent' , writer => {
227300 writer . setSelection ( null ) ;
228301 writer . removeSelectionAttribute ( this . model . document . selection . getAttributeKeys ( ) ) ;
229302
230- writer . remove ( writer . createRangeIn ( modelRoot ) ) ;
231- writer . insert ( this . parse ( data , modelRoot ) , modelRoot , 0 ) ;
303+ for ( const rootName of Object . keys ( newData ) ) {
304+ // Save to model.
305+ const modelRoot = this . model . document . getRoot ( rootName ) ;
306+
307+ writer . remove ( writer . createRangeIn ( modelRoot ) ) ;
308+ writer . insert ( this . parse ( newData [ rootName ] , modelRoot ) , modelRoot , 0 ) ;
309+ }
232310 } ) ;
233311 }
234312
@@ -275,6 +353,23 @@ export default class DataController {
275353 */
276354 destroy ( ) { }
277355
356+ /**
357+ * Checks if all provided root names are existing editor roots.
358+ *
359+ * @private
360+ * @param {Array.<String> } rootNames Root names to check.
361+ * @returns {Boolean } Whether all provided root names are existing editor roots.
362+ */
363+ _checkIfRootsExists ( rootNames ) {
364+ for ( const rootName of rootNames ) {
365+ if ( ! this . model . document . getRootNames ( ) . includes ( rootName ) ) {
366+ return false ;
367+ }
368+ }
369+
370+ return true ;
371+ }
372+
278373 /**
279374 * Event fired by decorated {@link #init} method.
280375 * See {@link module:utils/observablemixin~ObservableMixin.decorate} for more information and samples.
0 commit comments