Tabs docs for CMS

This commit is contained in:
Ingo Schommer 2012-11-06 23:59:35 +01:00
parent 3214a0a756
commit ada42103ed

View File

@ -110,7 +110,46 @@ before the form itself is layouted with its sibling panels to avoid incorrect di
SilverStripe constructs forms and its fields within PHP,
mainly through the `[getCMSFields()](api:DataObject->getCMSFields())` method.
This in turn means that the CMS loads these forms as HTML via Ajax calls,
e.g. after saving a record (which requires a form refresh), or switching the section in the CMS>
e.g. after saving a record (which requires a form refresh), or switching the section in the CMS.
Depending on where in the DOM hierarchy you want to use a form,
custom templates and additional CSS classes might be required for correct operation.
For example, the "EditForm" has specific view and logic JavaScript behaviour
which can be enabled via adding the "cms-edit-form" class.
In order to set the correct layout classes, we also need a custom template.
To obey the inheritance chain, we use `$this->getTemplatesWithSuffix('_EditForm')` for
selecting the most specific template (so `MyAdmin_EditForm.ss`, if it exists).
Basic example form in a CMS controller subclass:
:::php
class MyAdmin extends LeftAndMain {
function getEditForm() {
$form = new Form(
$this,
'EditForm',
new FieldSet(
TabSet::create(
'Root',
Tab::create('Main',
TextField::create('MyText')
)
)->setTemplate('CMSTabset')
),
new FieldSet(
FormAction::create('doSubmit')
)
);
// Required for correct CMS layout
$form->addExtraClass('cms-edit-form');
$form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
return $form;
}
}
Note: Usually you don't need to worry about these settings,
and will simply call `parent::getEditForm()` to modify an existing,
correctly configured form.
## JavaScript through jQuery.entwine
@ -351,6 +390,103 @@ Note that a similar tree logic is also used for the
form fields to select one or more entries from those hierarchies
(`[api:TreeDropdownField]` and `[api:TreeMultiselectField]`).
## Tabs
We're using [jQuery UI tabs](http://jqueryui.com/), but in a customized fashion.
HTML with tabs can be created either directly through HTML templates in the CMS,
or indirectly through a `[api:TabSet]` form field. Since tabsets are useable
outside of the CMS as well, the baseline application of tabs happens via
a small wrapper around `jQuery.tabs()` stored in `TabSet.js`.
In the CMS however, tabs need to do more: They memorize their active tab
in the user's browser, and lazy load content via ajax once they're activated.
They also need to work across different "layout containers" (see above),
meaning a tab navigation might be in a layout header, while the tab
content is occupied by the main content area. jQuery assumes a common
parent in the DOM for both the tab navigation and its target DOM elements.
In order to achieve this level of flexibility, most tabsets in the CMS
use a custom template which leaves rendering the tab navigation to
a separate template: `CMSMain.ss`. See the "Forms" section above
for an example form.
Here's how you would apply this template to your own tabsets used in the CMS.
Note that you usually only need to apply it to the outermost tabset,
since all others should render with their tab navigation inline.
Form template with custom tab navigation (trimmed down):
:::ss
<form $FormAttributes data-layout-type="border">
<div class="cms-content-header north">
<% if Fields.hasTabset %>
<% with Fields.fieldByName('Root') %>
<div class="cms-content-header-tabs">
<ul>
<% loop Tabs %>
<li><a href="#$id">$Title</a></li>
<% end_loop %>
</ul>
</div>
<% end_with %>
<% end_if %>
</div>
<div class="cms-content-fields center">
<fieldset>
<% loop Fields %>$FieldHolder<% end_loop %>
</fieldset>
</div>
</form>
Tabset template without tab navigation (e.g. `CMSTabset.ss`)
:::ss
<div $AttributesHTML>
<% loop Tabs %>
<% if Tabs %>
$FieldHolder
<% else %>
<div $AttributesHTML>
<% loop Fields %>
$FieldHolder
<% end_loop %>
</div>
<% end_if %>
<% end_loop %>
</div>
Lazy loading works based on the `href` attribute of the tab navigation.
The base behaviour is applied through adding a class `.cms-tabset` to a container.
Assuming that each tab has its own URL which is tracked in the HTML5 history,
the current tab display also has to work when loaded directly without Ajax.
This is achieved by template conditionals (see "MyActiveCondition").
The `.cms-panel-link` class will automatically trigger the ajax loading,
and load the HTML content into the main view. Example:
:::ss
<div id="my-tab-id" class="cms-tabset" data-ignore-tab-state="true">
<ul>
<li class="<% if MyActiveCondition %> ui-tabs-active<% end_if %>">
<a href="admin/mytabs/tab1" class="cms-panel-link">
Tab1
</a>
</li>
<li class="<% if MyActiveCondition %> ui-tabs-active<% end_if %>">
<a href="admin/mytabs/tab2" class="cms-panel-link">
Tab2
</a>
</li>
</ul>
</div>
The URL endpoints `admin/mytabs/tab1` and `admin/mytabs/tab2`
should return HTML fragments suitable for inserting into the content area,
through the `PjaxResponseNegotiator` class (see above).
## Related
* [Howto: Extend the CMS Interface](../howto/extend-cms-interface)