Merge pull request #2352 from mateusz/loadfragment

API Provide a thin alternative to loadPanel/submitForm.
This commit is contained in:
Ingo Schommer 2013-08-22 01:15:42 -07:00
commit aa9403b5b0
2 changed files with 121 additions and 4 deletions

View File

@ -115,7 +115,15 @@ jQuery.noConflict();
*/ */
$('.cms-container').entwine({ $('.cms-container').entwine({
CurrentXHR: null, /**
* Tracks current panel request.
*/
StateChangeXHR: null,
/**
* Tracks current fragment-only parallel PJAX requests.
*/
FragmentXHR: {},
StateChangeCount: 0, StateChangeCount: 0,
@ -419,7 +427,7 @@ jQuery.noConflict();
*/ */
handleStateChange: function() { handleStateChange: function() {
// Don't allow parallel loading to avoid edge cases // Don't allow parallel loading to avoid edge cases
if(this.getCurrentXHR()) this.getCurrentXHR().abort(); if(this.getStateChangeXHR()) this.getStateChangeXHR().abort();
var self = this, h = window.History, state = h.getState(), var self = this, h = window.History, state = h.getState(),
fragments = state.data.pjax || 'Content', headers = {}, fragments = state.data.pjax || 'Content', headers = {},
@ -455,7 +463,7 @@ jQuery.noConflict();
headers: headers, headers: headers,
url: state.url, url: state.url,
complete: function() { complete: function() {
self.setCurrentXHR(null); self.setStateChangeXHR(null);
// Remove loading indication from old content els (regardless of which are replaced) // Remove loading indication from old content els (regardless of which are replaced)
contentEls.removeClass('loading'); contentEls.removeClass('loading');
}, },
@ -465,7 +473,75 @@ jQuery.noConflict();
} }
}); });
this.setCurrentXHR(xhr); this.setStateChangeXHR(xhr);
},
/**
* ALternative to loadPanel/submitForm.
*
* Triggers a parallel-fetch of a PJAX fragment, which is a separate request to the
* state change requests. There could be any amount of these fetches going on in the background,
* and they don't register as a HTML5 history states.
*
* This is meant for updating a PJAX areas that are not complete panel/form reloads. These you'd
* normally do via submitForm or loadPanel which have a lot of automation built in.
*
* On receiving successful response, the framework will update the element tagged with appropriate
* data-pjax-fragment attribute (e.g. data-pjax-fragment="<pjax-fragment-name>"). Make sure this element
* is available.
*
* Example usage:
* $('.cms-container').loadFragment('admin/foobar/', 'FragmentName');
*
* @param url string Relative or absolute url of the controller.
* @param pjaxFragments string PJAX fragment(s), comma separated.
*/
loadFragment: function(url, pjaxFragments) {
var self = this,
xhr,
headers = {},
baseUrl = $('base').attr('href'),
fragmentXHR = this.getFragmentXHR();
// Make sure only one XHR for a specific fragment is currently in progress.
if(
typeof fragmentXHR[pjaxFragments]!=='undefined' &&
fragmentXHR[pjaxFragments]!==null
) {
fragmentXHR[pjaxFragments].abort();
fragmentXHR[pjaxFragments] = null;
}
url = $.path.isAbsoluteUrl(url) ? url : $.path.makeUrlAbsolute(url, baseUrl);
headers['X-Pjax'] = pjaxFragments;
xhr = $.ajax({
headers: headers,
url: url,
success: function(data, status, xhr) {
var elements = self.handleAjaxResponse(data, status, xhr, null);
// We are fully done now, make it possible for others to hook in here.
self.trigger('afterloadfragment', { data: data, status: status, xhr: xhr, elements: elements });
},
error: function(xhr, status, error) {
self.trigger('loadfragmenterror', { xhr: xhr, status: status, error: error });
},
complete: function() {
// Reset the current XHR in tracking object.
var fragmentXHR = self.getFragmentXHR();
if(
typeof fragmentXHR[pjaxFragments]!=='undefined' &&
fragmentXHR[pjaxFragments]!==null
) {
fragmentXHR[pjaxFragments] = null;
}
}
});
// Store the fragment request so we can abort later, should we get a duplicate request.
fragmentXHR[pjaxFragments] = xhr;
}, },
/** /**

View File

@ -287,6 +287,47 @@ tracked in the browser history, use the `pjax` attribute on the state data.
$('.cms-container').loadPanel('admin/pages', null, {pjax: 'Content'}); $('.cms-container').loadPanel('admin/pages', null, {pjax: 'Content'});
## Loading lightweight PJAX fragments
Normal navigation between URLs in the admin section of the Framework occurs through `loadPanel` and `submitForm`.
These calls make sure the HTML5 history is updated correctly and back and forward buttons work. They also take
care of some automation, for example restoring currently selected tabs.
However there are situations when you would like to only update a small area in the CMS, and when this operation should
not trigger a browser's history pushState. A good example here is reloading a dropdown that relies on backend session
information that could have been updated as part of action elsewhere, updates to sidebar status, or other areas
unrelated to the main flow.
In this case you can use the `loadFragment` call supplied by `LeftAndMain.js`. You can trigger as many of these in
parallel as you want. This will not disturb the main navigation.
$('.cms-container').loadFragment('admin/foobar/', 'Fragment1');
$('.cms-container').loadFragment('admin/foobar/', 'Fragment2');
$('.cms-container').loadFragment('admin/foobar/', 'Fragment3');
The ongoing requests are tracked by the PJAX fragment name (Fragment1, 2, and 3 above) - resubmission will
result in the prior request for this fragment to be aborted. Other parallel requests will continue undisturbed.
You can also load multiple fragments in one request, as long as they are to the same controller (i.e. URL):
$('.cms-container').loadFragment('admin/foobar/', 'Fragment2,Fragment3');
This counts as a separate request type from the perspective of the request tracking, so will not abort the singular
`Fragment2` nor `Fragment3`.
Upon the receipt of the response, the fragment will be injected into DOM where a matching `data-pjax-fragment` attribute
has been found on an element (this element will get completely replaced). Afterwards a `afterloadfragment` event
will be triggered. In case of a request error a `loadfragmenterror` will be raised and DOM will not be touched.
You can hook up a response handler that obtains all the details of the XHR request like this:
'from .cms-container': {
onafterloadfragment: function(e, data) {
// Say 'success'!
alert(data.status);
}
}
## Ajax Redirects ## Ajax Redirects
Sometimes, a server response represents a new URL state, e.g. when submitting an "add record" form, Sometimes, a server response represents a new URL state, e.g. when submitting an "add record" form,