Resource Agents

1.2

Basics - Agent Adapters - Defining Classes - Creating a New Resource - Editing a Resource - Building a Dual-Use Form - Deleting a Resource - Specifying a Callback - Changing the Path Prefix - Options

Basics

The resource agents are JavaScript objects that have following capabilities:

  • Keeping an object that represent a resource of the server.
  • Creating, updating and deleting it by sending Ajax requests to the server.

Cape.JS provides similar objects called collection agent, which handles a resource collection.

Agent Adapters

See Agent Adapters of Collection Agents.

Defining Classes

In order to use resource agents, you should define a class inheriting the Cape.ResourceAgent class.

You can define such a class calling Cape.createResourceAgentClass() method:

var UserAgent = Cape.createResourceAgentClass({
  constructor: function(client, options) {
    this.resourceName = 'user';
  }
})

You can also define it using the ES6 syntax:

class UserAgent extends Cape.ResourceAgent {
  constructor(client, options) {
    super(client, options);
    this.resourceName = 'user';
  }
}

The resource agent uses the resourceName property to construct the URL paths as explained below.

Creating a New Resource

Suppose that you can create a resource by sending an HTTP request POST /users to the server with following JSON string:

{ "user": { "name": "John", "email": "john@example.com" } }

Then, you can build an HTML form like this:

class UserForm extends Cape.Component {
  init() {
    this.agent = new UserAgent(this);
    this.refresh();
  }

  render(m) {
    m.formFor('user', m => {
      m.div(m => {
        m.labelFor('name', 'Name').sp();
        m.textField('name');
      });
      m.div(m => {
        m.labelFor('email', 'Email').sp();
        m.textField('email', { type: 'email' });
      });
      m.div(m => {
        m.onclick(e => this.agent.create());
        m.btn('Create');
      });
    });
  }
}

If you mount this component into your web page, you will see an HTML form whose two blank input fields and a ‘Create’ button.

When you input 'John' and 'john@example.com' to these fields and click the ‘Create’ button, the resource agent sends a POST request to /users with the following JSON body:

{ "user": { "name": "John", "email": "john@example.com" } }

Editing a Resource

Suppose that when you send an HTTP request GET /users/123 to the server, it returns the following JSON string:

{ "user": { "id": 123, "name": "John", "email": "john@example.com" } }

And suppose also that you can update this resource by sending an HTTP request PATCH /users/123 to the server with following JSON string:

{ "user": { "name": "John", "email": "dummy@example.io" } }

Then, you can build an HTML form like this:

class UserForm extends Cape.Component {
  init() {
    this.agent = new UserAgent(this);
    this.agent.id = 123;
    this.agent.init();
  }

  render(m) {
    m.formFor('user', m => {
      m.div(m => {
        m.labelFor('name', 'Name').sp();
        m.textField('name');
      });
      m.div(m => {
        m.labelFor('email', 'Email').sp();
        m.textField('email', { type: 'email' });
      });
      m.div(m => {
        m.onclick(e => this.agent.update());
        m.btn('Update');
      });
    });
  }
}

If you mount this component into your web page, you will see an HTML form whose two text input fields are filled with current values.

Note that we use this.agent.init() instead of this.refresh() here (at the line 5). The init() method of resource agents does following three things:

  1. Make an Ajax call to the server.
  2. Initialize the object property using the response data from the server.
  3. Call the refresh() method of the associated component.

When you modify the value of email field of form to 'dummy@example.io' and click the ‘Update’ button, the resource agent sends a PATCH request to /users/123 with the following JSON body:

{ "user": { "name": "John", "email": "dummy@example.io" } }

Building a Dual-Use Form

By synthesizing the two Component classes defined above, we will get the following class:

class UserForm extends Cape.Component {
  init() {
    this.agent = new UserAgent(this);
    this.agent.id = this.root.data.id;
    if (this.agent.id) this.agent.init();
    else this.refresh();
  }

  render(m) {
    m.formFor('user', m => {
      m.div(m => {
        m.labelFor('name', 'Name').sp();
        m.textField('name');
      });
      m.div(m => {
        m.labelFor('email', 'Email').sp();
        m.textField('email', { type: 'email' });
      });
      m.div(m => {
        if (this.agent.id) {
          m.onclick(e => this.agent.update());
          m.btn('Update');
        }
        else {
          m.onclick(e => this.agent.create());
          m.btn('Create');
        }
      });
    });
  }
}

This UserForm class can be used both for a new user form:

<h1>New User</h1>
<div id="user-form"></div>

<script src="./user_form.js"></script>
<script>
  var component = new UserForm();
  component.mount('user-form');
</script>

and for a edit user form:

<h1>Edit User</h1>
<div id="user-form" data-id="123"></div>

<script src="./user_form.js"></script>
<script>
  var component = new UserForm();
  component.mount('user-form');
</script>

Note that the component gets the string '123' through this.root.data.id.

Deleting a Resource

You can delete an existing resource by calling #destroy method of the resource agent.

  m.onclick(e => this.agent.destroy());
  m.btn('Delete');

Specifying a Callback

If you want to perform some tasks after the Ajax request is done, you should specify a callback function as the first argument of #create(), #update() or #destroy() like this:

m.onclick(e => this.agent.create(function(data) {
  if (data.result === 'OK') {
    window.location = '/users';
  }
  else {
    this.agent.errors = data.error_messages;
    this.refresh();
  }
}));

The specified callback should take an argument, which can be an object or a string according to the content type of response.

Changing the Path Prefix

See Changing the Path Prefix of Collection Agents.