Using the ItemService from JavaScript

Last updated Monday, September 12, 2016 in Sitecore Experience Platform for Developer
Keywords: Development

The ItemService is a standalone XHR (XMLHttpRequest) library for creating, fetching, saving, and deleting Sitecore items. It has many built-in utilities and helpers to assist you with data transactions between the front end and the back end.

Note

Many of the examples use functionality from Unit.js. If you want to run the examples literally, you must add a reference to Unit.js in your code.

This topic describes the following:

Creating items

You create an item by passing an object and the path in the Sitecore tree where you want the server to create the item, and then calling the execute method:

var peopleService = new ItemService( {
  url: "/sitecore/api/ssc/people"
} );
var aNewGuy = {
  name: "David",
  isActive: true,
  gender: "male"
};
peopleService.create( aNewGuy ).path( "/sitecore/content/home" ).execute().then( function ( david ) {
  david.should.be.an.instanceOf( ItemService.Item );
  david.name.should.eql( "David" );
  david.isActive.should.eql( true );
  david.gender.should.eql( "male" );
  done();
} ).fail( done );

You create a dirty item by not calling the execute method:

var peopleService = new ItemService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.create().then( function () {
  done();
} ).fail( done );

Fetching items

You fetch an item by ItemID with the fetchItem() method:

var peopleService = new ItemService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.fetchItem( "d4119c4f-31e9-4fd0-9fc4-6af1d6e36c8e" ).execute().then( function ( melton ) {
  melton.should.be.an.instanceOf( ItemService.Item );
  done();
} ).fail( done );

Fetching the children of an item

You fetch the children of an item with the fetchChildren() method:

var peopleService = new ItemService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.fetchItem( "d4119c4f-31e9-4fd0-9fc4-6af1d6e36c8e" ).execute().then( function ( melton ) {
  melton.should.be.an.instanceOf( ItemService.Item );
  melton.fetchChildren().execute().then( function ( meltonsChildren ) {
    meltonsChildren.should.be.an.Array.and.have.a.lengthOf( 3 );
    meltonsChildren[ 0 ].should.be.an.instanceOf( ItemService.Item );
    meltonsChildren[ 1 ].should.be.an.instanceOf( ItemService.Item );
    meltonsChildren[ 2 ].should.be.an.instanceOf( ItemService.Item );
    done();
  } ).fail( done );
} ).fail( done );

Saving items

You must either fetch or create an item before you can save it:

var peopleService = new ItemService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.fetchItem( "d4119c4f-31e9-4fd0-9fc4-6af1d6e36c8e" ).execute().then( function ( melton ) {
  melton.should.be.an.instanceOf( ItemService.Item );
  melton.name = "Melton the Magnificent";
  melton.save().execute().then( function ( savedMelton ) {
    savedMelton.name.should.eql( "Melton the Magnificent" );
    done();
  } ).fail( done );
} ).fail( done );

Destroying items

You must either fetch or create an item before you can destroy it:

var peopleService = new ItemService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.fetchItem( "d4119c4f-31e9-4fd0-9fc4-6af1d6e36c8e" ).execute().then( function ( melton ) {
  melton.should.be.an.instanceOf( ItemService.Item );
  melton.destroy().execute().then( function () {
    done();
  } ).fail( done );
} ).fail( done );

Searching for items

You can search for an item in two ways: you can use search or you can use query.

Using search()

You search for an item by passing a search term and then calling the execute method:

var peopleService = new ItemService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.search( "melton" ).execute().then( function ( queryResults ) {
  queryResults.should.have.a.property( "Links" ).and.be.an.Array;
  queryResults.should.have.a.property( "Results" ).and.be.an.Array;
  queryResults.should.have.a.property( "TotalCount" ).and.be.a.Number;
  queryResults.should.have.a.property( "TotalPage" ).and.be.a.Number;
  queryResults.Results[ 0 ].should.be.an.instanceOf( ItemService.Item );
  queryResults.Results[ 0 ].name.should.eql( "Banks Melton" );
  done();
} ).fail( done );

Using query()

You query for an item by passing a query item and then calling the execute method:

var peopleService = new ItemService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.query(( "xxxx-xxxx-xxxx-xxxx" ).execute().then( function ( queryResults ) {
  queryResults.should.have.a.property( "Links" ).and.be.an.Array;
  queryResults.should.have.a.property( "Results" ).and.be.an.Array;
  queryResults.should.have.a.property( "TotalCount" ).and.be.a.Number;
  queryResults.should.have.a.property( "TotalPage" ).and.be.a.Number;
  queryResults.Results[ 0 ].should.be.an.instanceOf( ItemService.Item );
  done();
} ).fail( done );

“xxxx-xxxx-xxxx-xxxx” is the Sitecore ID of the query item.

Creating a dirty item

A dirty item is an item that is only in browser memory and not saved on the server. This can be useful if your application has to wait for user input. Use the following code to create a dirty item:

var peopleService = new ItemService( {

  url: "/sitecore/api/ssc/people"
} );
peopleService.create().then( function ( aNewDirtyItem ) {
  /* At this point `aNewDirtyItem` has not been saved */
  aNewDirtyItem.should.be.an.instanceOf( ItemService.Item );
  aNewDirtyItem.name = "Melton";
  /* After modifying the dirty item, now we save it */
  aNewDirtyItem.save().execute().then( function () {
    done();
  } ).fail( done );
} ).fail( done );

Checking if an item is dirty

You can check if an item has not been saved (it is a “dirty item") with the isNew property:

var peopleService = new ItemService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.create().then( function ( guy ) {
  guy.isNew.should.be.true;
  done();
} ).fail( done );

When an item comes from the server, isNew is always false:

var peopleService = new ItemService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.create( {
  name: "guy"
} ).execute().then( function ( guy ) {
  guy.isNew.should.be.false;
  done();
} ).fail( done );

Retrieving an item as raw JSON

You may need to retrieve the data of an item as raw JSON (without all of the additional methods and properties). Use the following code to retrieve an item as raw JSON:

var peopleService = new ItemService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.fetchItem( "d4119c4f-31e9-4fd0-9fc4-6af1d6e36c8e" ).execute().then( function ( melton ) {
  var meltonAsJSON = melton.json();
  melton.should.be.an.instanceOf( ItemService.Item );
  meltonAsJSON.should.be.an.instanceOf( Object );
  meltonAsJSON.should.not.be.an.instanceOf( ItemService.Item );
  done();
} ).fail( done );

Tracking items

You can add tracking to an item. This makes it possible to add additional functionality:

  • The item can be saved automatically when a property changes.
  • You can call hasChanged() to see if the item has changed.
  • You can call revertChanges() to revert property values.

You set the trackable option to true to add tracking to your item:

var peopleService = new ItemService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.fetchItem( "d4119c4f-31e9-4fd0-9fc4-6af1d6e36c8e" ).option( "trackable", true ).execute().then( function ( melton ) {
  melton.option( "trackable" ).should.be.true;
  done();
} ).fail( done );

Saving items automatically

You can specify that Sitecore.Services.Client saves changes to an item automatically. Set trackable to true to turn this feature on. You can listen to the save event by using the emitter “on” or “once”:

var peopleService = new ItemService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.fetchItem( "d4119c4f-31e9-4fd0-9fc4-6af1d6e36c8e" ).option( "trackable", true ).execute().then( function ( melton ) {
  /* Listening to the `save` event `once`. You can also use `on` here to continuously listen to the `save` event. */
  melton.once( "save", function ( error ) {
    done();
  } );
  melton.name = "Melton the Magnificent";
} ).fail( done );

The hasChanged() method

You can specify that the ItemService adds the hasChanged() method by setting trackable to true. You use the hasChanged() method to check if an item has changed:

var peopleService = new ItemService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.fetchItem( "d4119c4f-31e9-4fd0-9fc4-6af1d6e36c8e" ).option( "trackable", true ).execute().then( function ( melton ) {
  melton.hasChanged().should.be.false;
  melton.name = "Melton the Magnificent";
  melton.hasChanged().should.be.true;
  done();
} ).fail( done );

The hasChanged method does not return true if a value changes between null, undefined, and ‘’ (the empty string):

var peopleService = new ItemService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.fetchItem( "d4119c4f-31e9-4fd0-9fc4-6af1d6e36c8e" ).option( "trackable", true ).execute().then( function ( melton ) {
  melton.hasChanged().should.be.false;
  melton.subscribed = "";
  melton.hasChanged().should.be.false;
  melton.subscribed = null;
  melton.hasChanged().should.be.false;
  melton.subscribed = undefined;
  melton.hasChanged().should.be.false;
  done();
} ).fail( done );

The revertChanges() method

You can specify that the ItemService add the revertChanges() method by setting trackable to true. You use the revertChanges() method to revert changes to the item:

var peopleService = new ItemService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.fetchItem( "d4119c4f-31e9-4fd0-9fc4-6af1d6e36c8e" ).option( "trackable", true ).execute().then( function ( melton ) {
  melton.name.should.eql( "Banks Melton" );
  melton.name = "Melton the Magnificent";
  melton.name.should.eql( "Melton the Magnificent" );
  melton.hasChanged().should.be.true;
  melton.revertChanges();
  melton.hasChanged().should.be.false;
  melton.name.should.eql( "Banks Melton" );
  done();
} ).fail( done );

The Query object

Each ItemService method returns a query object:

var peopleService = new ItemService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.create( {} ).should.be.an.instanceOf( ItemService.Query );
peopleService.fetchItem( "d4119c4f-31e9-4fd0-9fc4-6af1d6e36c8e" ).should.be.an.instanceOf( ItemService.Query );
peopleService.query( "/sitecore" ).should.be.an.instanceOf( ItemService.Query );
peopleService.search( "sitecore" ).should.be.an.instanceOf( ItemService.Query );
The query object has chainable helper methods. These methods add the given value to the query string of the request:
var peopleService = new ItemService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.create( {} )
  .database( "master" )
  .facet( "a facet" )
  .fields( "ItemName" )
  .includeMetadata( true )
  .includeStandardTemplateFields( true )
  .language( "en" )
  .page( 2 )
  .path( "/sitecore/content/home" )
  .sort( "aItemName" )
  .take( 5 )
  .version( "latest" )
  .should.be.an.instanceOf( ItemService.Query );
You call the execute() method to run the query. The execute() method returns a promise:
var peopleService = new ItemService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.fetchItem( "d4119c4f-31e9-4fd0-9fc4-6af1d6e36c8e" ).execute().then( function ( melton ) {
  melton.should.be.an.instanceOf( ItemService.Item );
  done();
} ).fail( done );

Handling promises

Almost all asynchronous calls return a promise. Specifically, and prominently, the execute() method returns a promise. The ItemService uses the q module to handle this.

var peopleService = new ItemService( {
  url: "/sitecore/api/ssc/people"
} );
var fetchQuery = peopleService.fetchItem( "d4119c4f-31e9-4fd0-9fc4-6af1d6e36c8e" );
var fetchQueryPromise = fetchQuery.execute();
fetchQueryPromise.should.have.a.property( "then" );
fetchQueryPromise.should.have.a.property( "fail" );

Working with middleware

The ItemService provides middleware functionality, using the sc-useify module:

ItemService.use( function ( data, next ) {
  data.timestamp = new Date().getTime();
  next( null, data );
} );

When you have integrated middleware, then the next time you receive data from the server, this data will have a timestamp property:

var peopleService = new ItemService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.fetchItem( "d4119c4f-31e9-4fd0-9fc4-6af1d6e36c8e" ).execute().then( function ( melton ) {
  melton.should.have.a.property( "timestamp" );
  done();
} ).fail( done );

You can clear all middleware workers (or clear by key):

ItemService.useify.clear();

Because you cleared all middleware, the data will not have a timestamp property the next time you receive it from the server:

var peopleService = new ItemService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.fetchItem( "d4119c4f-31e9-4fd0-9fc4-6af1d6e36c8e" ).execute().then( function ( melton ) {
  melton.should.have.not.a.property( "timestamp" );
  done();
} ).fail( done );

Working with event emitters

You can extend items with event emitters:

var peopleService = new ItemService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.fetchItem( "d4119c4f-31e9-4fd0-9fc4-6af1d6e36c8e" ).execute().then( function ( melton ) {
  melton.on( "save", function ( error ) {
    done();
  } );
  melton.age = 40;
  melton.save().execute();
} ).fail( done );

ItemService and validation

The ItemService does not provide any client-side validation. The following example changes the ID of the item to an invalid GUID and then triggers a save. The client will allow the save to execute because there is no validation. However, when the server receives the request with invalid data, it will not resolve and the promise will fail:

var peopleService = new ItemService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.fetchItem( "d4119c4f-31e9-4fd0-9fc4-6af1d6e36c8e" ).execute().then( function ( melton ) {
  melton.ItemID = "invalid guid";
  melton.save().execute().then( function ( badMelton ) {
    should( badMelton ).not.exist;
  } ).fail( function ( error ) {
    error.should.be.an.instanceOf( Error );
    done();
  } );
} ).fail( done );