logo





GradeBook released!
Oct, 2008
Solstice adds a grade book to its toolkit!

New Versions!
April, 2008
A new version of all our software is available!

CommonView Released
April, 2008
Check out our latest collaborative tool.

RESTful Web Services
Jan, 2008
Solstice provides support for RESTful development.

How to work with Ajax

To work with Ajax inside a Solstice application you need to do three things:

  1. Specify the remote call in config.
  2. Create the server-side component, a remote controller.
  3. Create the client-side component.

Let's look at the steps in that order:

Specify the Remote Controller

Lets say we want to add some dynamic content to our Account Screen application. Edit the config.xml file for that app.

You will need to add/edit the remotes section to define your remote call:

XML
<remotes> <remote name="time_of_day">AccountScreen::Controller::Remote::TimeOfDay</remote> </remotes>

The name attribute allows you to specify which remote you want to run when you write your client-side code. The content of the remote tag is the package of the Controller that will run when triggered.

The Remote Controller

Creating a Remote Controller

We need to create the Controller that we specified in our config file.

Basic Remote Controller Structure

Perl
package AccountScreen::Controller::Remote::TimeOfDay; use strict; use warnings; use 5.006_000; use base qw(Solstice::Controller::Remote); use Solstice::DateTime; sub validate { my $self = shift; } sub abortRemote { my $self = shift; } sub runRemote { my $self = shift; } 1;

This is just a basic subclass of Solstice::Controller::Remote.

The Model for Remote Controllers

When you call:

Perl
$self->getModel();

in a remote controller you get an anonymous data structure.

The structure is a reproduction of the data passed to Solstice.Remote.run() in the client-side code. We'll read more about this later.

If the model is more complex than a scalar, it will be returned by reference.

For example, if my model is an array of ids:

Perl
my $ids_ref = $self->getModel(); for my $id (@{$ids_ref}){ # ... }

The Lifecycle for Remote Controllers

You'll notice that the validPreConditions, freshen, getView, revert, update, validate, commit lifecycle doesn't show up in remote controllers. Because these controllers aren't helping to prepare a page there is a simplified lifecycle. You can think of it like this:

Perl
if( validate() ){ runRemote(); }else{ abortRemote(); }

validate() gives you a chance to look over your model and bail out if necessary. runRemote() will usually contain the majority of the work, while abortRemote() allows you to do some cleanup.

If you do not define validate(), runRemote() will always run.

Accessing Stateful Objects

In our use of Solstice we found that it was frequently nice to have extremely low-latency/low-overhead Ajax calls. To make these faster and lighter-weight we made the use of Solstice's stateful API optional.

So, if your remote controller needs to make use of objects like the current page's Application, Solstice::Session, or Solstice::ButtonService you will need to tell Solstice to load all the state functionality:

Perl
sub runRemote { my $self = shift; $self->initStatefulAPI(); my $button_service = Solstice::ButtonService->new(); # ... }

Working with the Remote Controller API

getApplication() - Getting the Application Object

Most of an application's stateful data is kept in it's Application object. You can access this object via the getApplication() method:

Perl
sub runRemote { my $self = shift; $self->initStatefulAPI(); my $application = $self->getApplication(); $item = $application->getItem(); }

Note the initStatefulAPI() call - Application objects require this.

addContentUpdate() - Replacing Element Content

Remote controllers allow you to specify a list of content updates. For example:

Perl
sub runRemote { my $self = shift; $self->addContentUpdate('html_element_id', "<span>Hello</span>"); $self->addContentUpdate('html_element_id2', Solstice::DateTime->new(time)->toCommon()); }

Would change this HTML:

HTML
<body> <div id="html_element_id"></div> <div id="html_element_id2">12:00:00 01-01-1900</div> </body>

into:

HTML
<body> <div id="html_element_id"><span>Hello</span></div> <div id="html_element_id2">1/07/2006 3:01 PM</div> </body>

addBlockReplacement() - Replacing Elements Entirely

Remote controllers allow you to specify a list of HTML elements to replace. For example:

Perl
sub runRemote { my $self = shift; $self->addBlockReplacement('html_element_id', "<span>Hello</span>"); $self->addBlockReplacement( 'html_element_id2', '<div id="html_element_id2">' . Solstice::DateTime->new(time)->toCommon() . '</div>' ); }

Would change this HTML:

HTML
<body> <div id="html_element_id"></div> <div id="html_element_id2">12:00:00 01-01-1900</div> </body>

into:

HTML
<body> <span>Hello</span> <div id="html_element_id2">1/07/2006 3:01 PM</div> </body>

addAction() - Triggering Javascript Updates

You can cause a list of arbitrary javascript commands to be executed client-side.

Do this with the addAction() method:

Perl
sub runRemote { my $self = shift; $self->addAction('show_element("new_element");'); $self->addContentUpdate('new_element', "Content for new element"); }

This first calls a Javascript method to show the HTML element with id new_element, then puts new content into the element.

A Whole Controller

A whole remote controller might look like this:

Perl
package AccountScreen::Controller::Remote::TimeOfDay; use strict; use warnings; use 5.006_000; use base qw(Solstice::Controller::Remote); use AccountScreen::View::EventTime; sub validate { my $self = shift; my $event_id = $self->getModel(); return (defined $event_id && $event_id); } sub abortRemote { my $self = shift; $self->addAction('cleanup_timeofday();'); } sub runRemote { my $self = shift; my $event_id = $self->getModel(); $self->initStatefulAPI(); my $application = $self->getApplication(); my $event = $application->getEvent(); my $time_view = AccountScreen::View::EventTime->new($event); my $content = ""; $time_view->paint(\$content); $self->addAction("create_time_element($event_id);"); $self->addAction("position_time_element($event_id);"); $self->addContentUpdate($event_id, $content); $self->addAction('cleanup_timeofday();'); } 1;

Calling the Remote Code from the Client-side

Finally, you need to create some code that triggers the Ajax controller.

Remote controllers are used via the Javascript function Solstice.Remote.run().

Any way you can call this method in the client-side Javascript will work. You could use Solstice::OnLoadService or put a <script> tag into your template - these would both work. Let's look at another common example:

On a ButtonService Button/Link

You can attach remote functionality to Solstice::ButtonService buttons using the client_action property. The remote call will then run when the button is clicked.

The code in generateParams would look like this:

Perl
sub generateParams { my $self = shift; my $event_id = $self->getModel()->getID(); my $button_service = Solstice::ButtonService->new(); my $time_of_day_popin = $button_service->makeButton({ label => 'Time of Day', client_action => "Solstice.Remote.run('AccountScreen', 'time_of_day', $event_id);", }); $self->setParam('time_of_day_popin', $time_of_day_popin->getPopInLink()); }

Note the Solstice.Remote.run client action's first two arguments - the first is the application in which you define the remote handler, and the second is the name of the handler to run. These match up with the config file we edited above.

The third argument is an arbitrarily complex Javascript value that gets passed to the remote controller as a model. In this case, our remote controller takes the ID of the event for which we are making a link.

Of course, don't forget to add a <-- sol_var time_of_day_popin --> to your template.