Bei meiner Diplomarbeit arbeite ich teilweise mit einem bereits bestehenden SOAP Interface. Backbone hatte ich mir schon für das Frontend ausgesucht und plötzlich war ich in der Situation, dass eine Backbone Collection zwei separate SOAP Requests durchführen sollte. Einmal brauchte ich den Pfad zu einem source Bild und einmal zu einem target Bild. Jetzt ist das Standardverhalten der Collections, einen Request an eine REST API zu schicken, aber glücklicherweise kann man Teile von Backbone überschreiben, wie es einem passt.

Zuerst lege ich bei der Initialisierung Parameter für einen derartigen AJAX Request fest, die später einfach kopiert und übernommen werden können.

1this.standard_params = {
2        "type": "POST", // soap = post
3        "url": options.url,
4        "dataType": "xml",
5        "contentType": "application/soap+xml"
6}

Die parse() Methode wandelt das erhaltene SOAP XML in JSON zur Weiterverarbeitung um.

1parse: function( xml ) {
2        var obj = {
3                /* xml daten hier rein */
4        };
5
6        return obj;
7}

Der wichtigste Teil ist aber die sync() Methode der Collection zu überschreiben. Dabei wird für jeden Request mit Backbone.ajax() ein Promise erstellt, diese mit $.when() zusammengefasst und zurückgegeben. Wichtig ist außerdem das request Event des Models auszulösen, da ansonsten keine Weiterverarbeitung stattfindet (also kein Aufruf von .parse()).

 1sync: function( method, model, opts ) {
 2        if ( method === "read" ) {
 3                // "read" wird bei collection.fetch() übergeben
 4
 5                // === source request === 
 6
 7                // zuerst parameter klonen
 8                var source_params = _.clone( this.standard_params );
 9
10                // xml reqest bauen, also envelope und body
11                source_params.data = this._buildXML( /*data*/ );
12
13                // xhr für den request erstellen
14                var source_xhr = Backbone.ajax( _.extend( source_params, opts ) );
15
16                // ===  selbes spiel mit target request === 
17
18                // jQuery Deferred für die beiden requests erstellen
19                var both_xhr = $.when( source_xhr, target_xhr );
20                
21                // request event triggern, da ansonsten zb kein parse() aufgerufen wird
22                model.trigger( "request", model, both_xhr, opts );
23                
24                // Deferred zurückgeben
25                return both_xhr;
26        }
27}

Die Collection würde man jetzt ganz normal erstellen.

1var collection = new MyCollection([], { /* notwendige daten für requests */ });

Allerdings wird die parse() Methode für beide Requests separat aufgerufen, was ein Problem darstellt, wenn alle Daten am Ende in der Collection landen sollen. Deswegen muss man Backbone beim fetch() explizit dazusagen, dass nichts gemerged oder gelöscht werden soll. Der Callback kommt dann bequem mit Deferred.then().

1panels.fetch({
2        "merge": false,
3        "remove": false
4}).then( function() {
5        // auf neue daten reagieren
6});

Ich denke, das Konzept lässt sich auch für n > 2 Requests generalisieren.