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:
- Make an Ajax call to the server.
- Initialize the
object
property using the response data from the server. - 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.