Ajax design patterns
Moderator: General Moderators
Ajax design patterns
Haven't seen any discussion here about this, but think it would be interesting to hear and discuss about people's approach to "ajaxifying" their web apps. Ajax is of course quite a broad subject, but what I mean is:
- you build a web app on top of some MVC framework (your own or something like ZF)
- the web app has different kinds of content which can be posted using ajax. Say comments in a comment thread, posts in a forum, etc
- you might even combine several forms with different actions
I guess the Facebook page is a good example. Everything you want to do can be done from one place, starting with a single text field. Once it has focus you get a few icons in view, which all lead to more functionality like uploading images or videos, etc
Questions I would have:
How do you handle the ajax forms?
Do you have a non-js fallback with normal forms?
Do you develop two separate actions or controllers handling the normal and the ajax part?
How do you handle validation in the forms, do you reuse the validation which already exists in the models?
As an example of one approach, I just found out that ZF has something like contextswitch
http://framework.zend.com/manual/en/zen ... jaxcontext
- you build a web app on top of some MVC framework (your own or something like ZF)
- the web app has different kinds of content which can be posted using ajax. Say comments in a comment thread, posts in a forum, etc
- you might even combine several forms with different actions
I guess the Facebook page is a good example. Everything you want to do can be done from one place, starting with a single text field. Once it has focus you get a few icons in view, which all lead to more functionality like uploading images or videos, etc
Questions I would have:
How do you handle the ajax forms?
Do you have a non-js fallback with normal forms?
Do you develop two separate actions or controllers handling the normal and the ajax part?
How do you handle validation in the forms, do you reuse the validation which already exists in the models?
As an example of one approach, I just found out that ZF has something like contextswitch
http://framework.zend.com/manual/en/zen ... jaxcontext
-
alex.barylski
- DevNet Evangelist
- Posts: 6267
- Joined: Tue Dec 21, 2004 5:00 pm
- Location: Winnipeg
Re: Ajax design patterns
Good question.
My own framework offers complete rendered sections of a web page automagically when you use a view manager class. For things like updating <select> lists I would have a separate JSON controller, personally, but thats me.
For things like adding the a comment to an existing list (ie: wordpress blog) is the most interesting question. Returning a raw data result like in JSON, would require rendering on the client into the required HTML, which is likley duplicated on the server already via a template.
I suppose I would make the comments an individual template and return that in AJAX requests but I'd rather return the comment just added. So I would probably just embed the single comment template into another template which render a list of results.
My own framework offers complete rendered sections of a web page automagically when you use a view manager class. For things like updating <select> lists I would have a separate JSON controller, personally, but thats me.
For things like adding the a comment to an existing list (ie: wordpress blog) is the most interesting question. Returning a raw data result like in JSON, would require rendering on the client into the required HTML, which is likley duplicated on the server already via a template.
I suppose I would make the comments an individual template and return that in AJAX requests but I'd rather return the comment just added. So I would probably just embed the single comment template into another template which render a list of results.
Re: Ajax design patterns
Having a non-JS fallback is dependent on the specific site and form. When relevant, I often start with a non-JS solution and add the AJAX component on top of it - reusing the fields, form action, etc.
For form response that add content to the site, I usually have the smallest item (such as a comment) as a renderable view partial server-side, and use in according to context - render a comment list with it or return it from the server on successful creation of a new comment. As Alex said, having the rendering logic both on the server and in client is duplicate code that is hard to maintain.
It's worth noting that returning individual items via an AJAX operation would sometimes require binding new events to it (any JS functionality that already exists in the view on similar items). For that I often abstract the event binding operation and make restrict it to specific DOM contexts (jQuery makes this really easy).
For form response that add content to the site, I usually have the smallest item (such as a comment) as a renderable view partial server-side, and use in according to context - render a comment list with it or return it from the server on successful creation of a new comment. As Alex said, having the rendering logic both on the server and in client is duplicate code that is hard to maintain.
It's worth noting that returning individual items via an AJAX operation would sometimes require binding new events to it (any JS functionality that already exists in the view on similar items). For that I often abstract the event binding operation and make restrict it to specific DOM contexts (jQuery makes this really easy).
Re: Ajax design patterns
@PCSpectra & pytrin: indeed it seems like there are 2 approaches in the example of posting something to an existing list.
1) add the new content through ajax and at the same time dynamically add it to the page. This would mean duplicating the "rendering logic". With that you probably mean the HTML that's needed to wrap around the new comment/post/item
2) add the new content by posting with Ajax and have an existing controller/action return the partial HTML complete with the new content
So that second option seems the best. How do those controllers and actions look like? Do you have separate controllers for handling the ajax part, separate actions or a switch inside existing actions to detect ajax calls?
1) add the new content through ajax and at the same time dynamically add it to the page. This would mean duplicating the "rendering logic". With that you probably mean the HTML that's needed to wrap around the new comment/post/item
2) add the new content by posting with Ajax and have an existing controller/action return the partial HTML complete with the new content
So that second option seems the best. How do those controllers and actions look like? Do you have separate controllers for handling the ajax part, separate actions or a switch inside existing actions to detect ajax calls?
Re: Ajax design patterns
Yeah when the markup is duplicated I either
1) use a helper from within my javascript class and rename the file to .js.php ( so the markup looks 'hard coded' to the client who hits view source, but on the server it is dynamically generated )
2) ill output a template to a div and use javascript to fill in dynamic data from a json object and copy the resultant string into the body of the page
3) ill have a URL or route I can hit that will return fully rendered html that just fits in place on the page
Rather then using the context switch sometimes I just go
if( $this->isXhttpRequest() )
{ // do whatever }
1) use a helper from within my javascript class and rename the file to .js.php ( so the markup looks 'hard coded' to the client who hits view source, but on the server it is dynamically generated )
2) ill output a template to a div and use javascript to fill in dynamic data from a json object and copy the resultant string into the body of the page
3) ill have a URL or route I can hit that will return fully rendered html that just fits in place on the page
Rather then using the context switch sometimes I just go
if( $this->isXhttpRequest() )
{ // do whatever }
Re: Ajax design patterns
I sometimes use separate actions for the AJAX requests, and sometimes reuse the same action action with switches on the type (like Josh suggested). All depending on how different the regular action is from the AJAX one.
- inghamn
- Forum Contributor
- Posts: 174
- Joined: Mon Apr 16, 2007 10:33 am
- Location: Bloomington, IN, USA
Re: Ajax design patterns
I use context switching in my stuff, and it's been working quite well. (Not with ZF, but my own templating class). When every controller has the ability to give you HTML, XML, JSON, TXT, or whatever, you can save time and just write stuff once.
If some javascript needs data, you have a much easier time of figuring out where to get it. And usually don't have to write up a custom service for it.
If some javascript needs data, you have a much easier time of figuring out where to get it. And usually don't have to write up a custom service for it.
Re: Ajax design patterns
Interesting to hear some approaches. Could some of you post small snippets of code to show what you mean? Even if it's just one action or something which shows the basics. It might make it easier for me to visualize what you mean
- inghamn
- Forum Contributor
- Posts: 174
- Joined: Mon Apr 16, 2007 10:33 am
- Location: Bloomington, IN, USA
Re: Ajax design patterns
In the addressing application I've been working on recently..here's the controller for searching for streets.
It looks for streets based on the name you send ($_GET['streetName']), and returns results in HTML, or whatever other format you ask it.
For the record, I use PageControllers. This controller lives at /BASE_URL/streets
It looks for streets based on the name you send ($_GET['streetName']), and returns results in HTML, or whatever other format you ask it.
For the record, I use PageControllers. This controller lives at /BASE_URL/streets
Code: Select all
$template = isset($_GET['format']) ? new Template('default',$_GET['format']) : new Template();
if ($template->outputFormat == 'html') {
$template->blocks[] = new Block('streets/breadcrumbs.inc');
$template->blocks[] = new Block('streets/findStreetForm.inc');
}
if (isset($_GET['streetName'])) {
$fields = AddressList::parseAddress($_GET['streetName'],'streetNameOnly');
if (count($fields)) {
$streets = new StreetList($fields);
}
else {
$streets = new StreetList();
$streets->find();
}
}
elseif ($template->outputFormat != 'html') {
$streets = new StreetList();
$streets->find();
}
if (isset($streets)) {
$template->blocks[] = new Block('streets/streetList.inc',array('streetList'=>$streets));
}
echo $template->render();
- inghamn
- Forum Contributor
- Posts: 174
- Joined: Mon Apr 16, 2007 10:33 am
- Location: Bloomington, IN, USA
Re: Ajax design patterns
Now, for the AJAXy stuff....
The findStreetForm is a block (what most everyone else calls a partial) that I can include into any template that needs it. You see it called for up in that controller, if the user's getting HTML output.
I've become a huge fan of YUI. So, the findStreetForm does autocomplete by doing it's own searches against the very same URL, only it's asking for JSON.
The findStreetForm is a block (what most everyone else calls a partial) that I can include into any template that needs it. You see it called for up in that controller, if the user's getting HTML output.
I've become a huge fan of YUI. So, the findStreetForm does autocomplete by doing it's own searches against the very same URL, only it's asking for JSON.
Code: Select all
<div id="findStreetForm" class="yui-skin-sam">
<form method="get" action="<?php echo $_SERVER['SCRIPT_NAME']; ?>">
<fieldset><legend>Search</legend>
<label for="streetName">Street Name</label>
<div id="findStreetForm-autocomplete" style="width:15em;">
<input name="streetName" id="streetName"
value="<?php echo isset($_GET['streetName']) ? View::escape($_GET['streetName']) : ''; ?>" />
<div id="container"></div>
</div>
<button type="submit" class="search">Search</button>
</fieldset>
</form>
</div>
<script type="text/javascript">
findStreetForm_autosuggest = function() {
var streetService = new YAHOO.util.XHRDataSource("<?php echo BASE_URL; ?>/streets");
streetService.responseType = YAHOO.util.XHRDataSource.TYPE_JSON;
streetService.responseSchema = {
resultsList: "streets",
fields: ["name"]
};
streetService.maxCacheEntries = 5;
var findStreetFormAC = new YAHOO.widget.AutoComplete("streetName",
"container",
streetService);
findStreetFormAC.generateRequest = function(query) {
return '?format=json;streetName=' + query;
}
return{
streetService: streetService,
findStreetFormAC: findStreetFormAC
};
}();
</script>
- inghamn
- Forum Contributor
- Posts: 174
- Joined: Mon Apr 16, 2007 10:33 am
- Location: Bloomington, IN, USA
Re: Ajax design patterns
I organize my application's files like so:
So now, all that's left is to write up the streetList.inc block. And need to write a different version of that block for each output format I want to support.
So the JSON streetList.inc is:
And the HTML streetList.inc is
Code: Select all
/application
/classes
/blocks
/html
/streets
findStreetForm.inc
streetList.inc
/json
/streets
streetList.inc
/xml
/streets
streetList.inc
/txt
/html
/streets
home.php
/library
/templates
/html
default.inc
two-column.inc
three-column.inc
full-width.inc
/json
default.inc
/xml
default.inc
/txt
default.inc
So the JSON streetList.inc is:
Code: Select all
$streets = array();
foreach ($this->streetList as $street) {
$name = addslashes($street->getStreetName());
$town = addslashes($street->getTown()->getDescription());
$notes = addslashes($street->getNotes());
$string = <<<EOD
{
"id":"{$street->getId()}",
"name":"$name",
"town":"$town",
"status":"{$street->getStatus_code()}"
}
EOD;
$streets[] = $string;
}
$streets = implode(",\n",$streets);
echo '{"streets":['.$streets.']}';
Code: Select all
if (count($this->streetList)) {
echo "
<table>
<thead>
<tr>
<th>Street Name</th>
<th>Status</th>
<th>Town</th>
</tr>
</thead>
<tbody>
";
foreach ($this->streetList as $street) {
$name = View::escape($street->getStreetName());
$status = View::escape($street->getStatus()->getDescription());
$town = View::escape($street->getTown()->getDescription());
echo "
<tr>
<td><a href=\"{$street->getURL()}\">$name</a></td>
<td>$status</td>
<td>$town</td>
</tr>
";
}
echo "
</tbody>
</table>
";
}
Re: Ajax design patterns
This is a helper method that saves models, with data populate from a form. I use it in my controllers. If it is called from a form it sets a flash messenger (Zend parlance) and redirects with an http status code, if it is posted to from an ajax enabled form it sends some relevant stuff back via jsonmatthijs wrote: Could some of you post small snippets of code to show what you mean? Even if it's just one action or something which shows the basics. It might make it easier for me to visualize what you mean
Code: Select all
/**
* Method called after model saving takes place.
*
* Method expects a form and a model, it takes data from form, populates data to model.
* It finds model's mapper, and saves model
*
* if the editing is taking place as part of an ajax call, basic information about the edited model is returned via json w/ a status code
* otherwise for non-ajax calls, the doEdit() template method will be called
*
* @param K12_Form
* @param K12_Model
*/
protected function save( $form, $model )
{
$isNewModel = $model->getId() == 0; // if model has no id, isNewModel = true
$this->populateTo( $form, $model );
$this->doSave( $form, $model );
if( $this->_request->isXmlHttpRequest() )
{
$data = new stdClass();
$data->status = 0;
$data->mode = $isNewModel ? 'new' : 'edit';
$data->model = new stdClass();
$data->model->id = $model->getId();
$data->model->title = (string)$model;
return $this->_helper->json( $data );
}
return $this->saveSuccess( $model->getId() );
}Re: Ajax design patterns
Well, I can't agree with thisPCSpectra wrote:Returning a raw data result like in JSON, would require rendering on the client into the required HTML, which is likley duplicated on the server already via a template.
My web-apps are fully AJAX featured. That is, except for the main page HTML View I have no more HTML code. Indeed, I don't have any server side Views - everything is sent to the user as JSON data (if you consider an JSON formatter helper a View ... I have one View
Just today, I've read a short article about MVC and AJAX - http://wiki.apidesign.org/wiki/MVC
So, we have MVP hereTo MVP Again
Obviously, people tend to repeat the same thing again and again, just with new technologies. Thus with the rise of Ajax, where the GUI is represented inside a browser we are again closer to MVP as the user deals with the view elements directly.
There are 10 types of people in this world, those who understand binary and those who don't
-
alex.barylski
- DevNet Evangelist
- Posts: 6267
- Joined: Tue Dec 21, 2004 5:00 pm
- Location: Winnipeg
Re: Ajax design patterns
So what happens when someone visits your site on a mobile device like a blackberry, which might not have JS enabled by default? If templates rendered via client side (using XSLT, or whatever) chances are a mobile will not render anything but simple HTML.My web-apps are fully AJAX featured. That is, except for the main page HTML View I have no more HTML code. Indeed, I don't have any server side Views - everything is sent to the user as JSON data (if you consider an JSON formatter helper a View ... I have one View ). That is, the View part is always client side - no templates, no code duplication (except for the validation, which I think can't be done in other way).
No to mention SEF/SEO. Unless your pages are purely XML data structures which are rendered into HTML (but I recall JSON being the term) how does that affect SE's???
Cheers,
Alex
- Christopher
- Site Administrator
- Posts: 13596
- Joined: Wed Aug 25, 2004 7:54 pm
- Location: New York, NY, US
Re: Ajax design patterns
So you have one piece of Javascript code that you load with the main page and that builds the page from JSON data for every page in your site?VladSun wrote:My web-apps are fully AJAX featured. That is, except for the main page HTML View I have no more HTML code. Indeed, I don't have any server side Views - everything is sent to the user as JSON data (if you consider an JSON formatter helper a View ... I have one View). That is, the View part is always client side - no templates, no code duplication (except for the validation, which I think can't be done in other way).
(#10850)