Using the EntityService from JavaScript

Abstract

How to access items in JavaScript via the EntityService API.

The EntityService is a standalone XHR (XMLHttpRequest) library for creating, fetching, saving, and deleting Sitecore entities. The library is /sitecore/shell/client/Services/Assets/lib/entityservice.js.

This topic describes:

The EntityService has many utilities and helpers that assist in the data transaction between the front end and back end. For example:

var peopleService = new EntityService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.createEntity.should.be.a.type( "function" );
peopleService.fetchEntity.should.be.a.type( "function" );
peopleService.fetchEntities.should.be.a.type( "function" );
peopleService.loadMetadata.should.be.a.type( "function" );

The structure of an entity object is based on metadata (a schema). The EntityService requests this metadata only once from the server, using the OPTIONS request.

This example shows how you instantiate the EntityService and create an entity:

  1. An EntityService is instantiated.

  2. When the EntityService is asked to execute any server related request (createEntity, fetchEntity, and so on), the supplied URL is accessed with an OPTIONS request. All subsequent requests are queued until the server responds with a valid metadata object.

  3. After the metadata object is returned, it is attached to the context EntityService and all entities can be validated and sanitized based on this metadata.

    The following code examples are based on this metadata:

    var metadata = {
      "entity": {
        "key": "ItemID",
        "properties": [ {
          "key": "id",
          "datatype": "number"
        }, {
          "key": "ItemID",
          "datatype": "guid"
        }, {
          "key": "isActive",
          "datatype": "boolean"
        }, {
          "key": "balance",
          "datatype": "string"
        }, {
          "key": "picture",
          "datatype": "string"
        }, {
          "key": "age",
          "datatype": "number"
        }, {
          "key": "name",
          "datatype": "string"
        }, {
          "key": "gender",
          "datatype": "string"
        }, {
          "key": "company",
          "datatype": "string"
        }, {
          "key": "email",
          "datatype": "email"
        }, {
          "key": "phone",
          "datatype": "string"
        }, {
          "key": "address",
          "datatype": "string"
        }, {
          "key": "about",
          "datatype": "string"
        }, {
          "key": "registered",
          "datatype": "date"
        }, {
          "key": "latitude",
          "datatype": "number"
        }, {
          "key": "longitude",
          "datatype": "number"
        }, {
          "key": "subscribed",
          "datatype": "string"
        }, {
          "key": "children",
          "datatype": [ {
            "properties": [ {
              "key": "name",
              "datatype": "string"
            } ]
          } ]
        }, {
          "key": "tags",
          "datatype": [ "string" ]
        } ]
      }
    };
    

Entity represents an Entity client side

Constructor

EntityService.Entity ( sanitizedData, entityServiceSchema, options )

Parameters

  • sanitizedData: object. An object that meets has been validated by the schema

  • entityServiceSchema: object. The entity schema

  • options: object. The entity options

Returns

Entity: an entity

You create an entity by passing an object, setting the path in the Sitecore content tree where you want the item to be created, and calling the execute method:

var peopleService = new EntityService( {
  url: "/sitecore/api/ssc/people"
} );
var aNewGuy = {
  name: "David",
  isActive: "true",
  gender: "male"
};
peopleService.createEntity( aNewGuy ).then( function ( david ) {
  david.should.be.an.instanceOf( EntityService.Entity )};
  david.isNew.should.be.false;
  david.ItemID.should.not.be.empty;
  david.name.should.eql( "David" );
  david.isActive.should.eql( true );
  david.gender.should.eql( "male" );
  /**
   * ... and because not all of the key/values for this `Entity` were given based on the
   * `peopleService` end point metadata, they will still be added to the `Entity`. Their default
   * values are also based on the metadata.
   */
  david.should.have.a.property( "id", null );
  david.should.have.a.property( "balance", null );
  david.should.have.a.property( "picture", null );
  david.should.have.a.property( "age", null );
  david.should.have.a.property( "company", null );
  david.should.have.a.property( "email", null );
  david.should.have.a.property( "phone", null );
  david.should.have.a.property( "address", null );
  david.should.have.a.property( "about", null );
  david.should.have.a.property( "registered" );
  david.should.have.a.property( "latitude", null );
  david.should.have.a.property( "longitude", null );
  david.should.have.a.property( "subscribed", null );
  david.should.have.a.property( "children", null );
  david.should.have.a.property( "tags", null );
  done();
} ).fail( done );

You create a dirty entity without a call to the server by not giving an object to the createEntity method. All dirty entities are considered new. You can check this with the isNew property.

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

You fetch a single entity with the fetchEntity method, giving the entity id/guid. fetchEntity returns a query so you need to call execute.

var peopleService = new EntityService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.fetchEntity( "951c3e2e-02e8-4bbc-bbc8-e69ada95e670" ).execute().then( function ( cooley ) {
  cooley.name.should.eql( "Queen Cooley" );
  done();
} ).fail( done );

You fetch all entities based on the given URL with the fetchEntities method.

var peopleService = new EntityService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.fetchEntities().execute().then( function ( people ) {
  people.should.be.an.Array.with.a.lengthOf( 3 );
  people[ 0 ].should.be.an.instanceOf( EntityService.Entity );
  people[ 1 ].should.be.an.instanceOf( EntityService.Entity );
  people[ 2 ].should.be.an.instanceOf( EntityService.Entity );
  people[ 0 ].isValid().should.be.ok;
  people[ 1 ].isValid().should.be.ok;
  /*
   * The data of people[ 2 ] has been intentionally made invalid
   */
  people[ 2 ].isValid().should.not.be.ok;
  done();
} ).fail( done );

You must fetch or create an entity before you can save it.

var peopleService = new EntityService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.fetchEntity( "951c3e2e-02e8-4bbc-bbc8-e69ada95e670" ).execute().then( function ( cooley ) {
  cooley.should.be.an.instanceOf( EntityService.Entity );
  cooley.name = "Mrs Queen Cooley";
  cooley.save().then( function ( savedCooley ) {
    savedCooley.name.should.eql( "Mrs Queen Cooley" );
    done();
  } ).fail( done );
} ).fail( done );
Each time you save an Entity, it will trigger a ‘save’ event that you can listen for: 
( entity.on(’save’, callback) ).

You must fetch or create an entity before you can destroy it.

var peopleService = new EntityService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.fetchEntity( "951c3e2e-02e8-4bbc-bbc8-e69ada95e670" ).execute().then( function ( cooley ) {
  cooley.should.be.an.instanceOf( EntityService.Entity );
  cooley.destroy().then( function () {
    done();
  } ).fail( done );
} ).fail( done );

A dirty entity is an entity you create in browser memory without saving it. This is useful, for example, if you are waiting for user input.

var peopleService = new EntityService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.createEntity().then( function ( aNewDirtyEntity ) {
  /* At this point `aNewDirtyEntity` has not been saved */
  aNewDirtyEntity.should.be.an.instanceOf( EntityService.Entity );
  aNewDirtyEntity.name = "Queen";
  /* After modifying the dirty item, now we save it */
  aNewDirtyEntity.save().then( function () {
    done();
  } ).fail( done );
} );

Checking if an entity is dirty

You check if an entity has not been saved (it is “dirty”) with the isNew property:

var peopleService = new EntityService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.createEntity().then( function ( guy ) {
  guy.isNew.should.be.true;
  done();
} ).fail( done );
When the origin of an Entityis is the server, isNew is always false.
var peopleService = new EntityService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.createEntity( {
  name: "guy"
} ).then( function ( guy ) {
  /*
   * `guy` is technically from the server because we are creating a new `Entity` by giving an
   * object to the `createEntity` method.
   */
  guy.isNew.should.be.false;
  done();
} ).fail( done );

There are situations where you need to retrieve the data stored in an entity as raw JSON without any of the additional methods and properties. The following method is used internally to retrieve the data that is sent to the server:

var peopleService = new EntityService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.fetchEntity( "951c3e2e-02e8-4bbc-bbc8-e69ada95e670" ).execute().then( function ( queen ) {
  var queenAsJson = queen.json();
  queen.should.be.an.instanceOf( EntityService.Entity );
  queenAsJson.should.be.an.instanceOf( Object );
  queenAsJson.should.not.be.an.instanceOf( EntityService.Entity );
  done();
} ).fail( done );

You can extend an entity with tracking to add additional functionality, for example:

  • Automatically save when a property changes.

  • Call hasChanged() to see if the entity has changed.

  • Call revertChanges() to revert property values.

Add tracking to your entity with the trackable option:

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

When you turn trackable on, changes are automatically saved. You can listen to the save event using emitter on or once.

var peopleService = new EntityService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.fetchEntity( "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 );

When you turn trackable on, Sitecore adds the hasChanged method so you can check if an entity has changed:

var peopleService = new EntityService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.fetchEntity( "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 EntityService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.fetchEntity( "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 );

When you turn trackable on, Sitecore adds the revertChanges method, so you can revert changes.

var peopleService = new EntityService( {
  url: "/sitecore/api/ssc/people"
} );
peopleService.fetchEntity( "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 );