diff --git a/src/service/loader-partial.js b/src/service/loader-partial.js index eb9351bcb..5f751f4fe 100644 --- a/src/service/loader-partial.js +++ b/src/service/loader-partial.js @@ -18,10 +18,11 @@ angular.module('pascalprecht.translate') * @description * Represents Part object to add and set parts at runtime. */ - function Part(name) { + function Part(name, priority) { this.name = name; this.isActive = true; this.tables = {}; + this.priority = priority || 0; } /** @@ -102,6 +103,19 @@ angular.module('pascalprecht.translate') return dst; } + function getPrioritizedParts() { + var prioritizedParts = []; + for(var part in parts) { + if (parts[part].isActive) { + prioritizedParts.push(parts[part]); + } + } + prioritizedParts.sort(function (a, b) { + return a.priority - b.priority; + }); + return prioritizedParts; + } + /** * @ngdoc function @@ -114,18 +128,20 @@ angular.module('pascalprecht.translate') * translation data, but only registers a part to be loaded in the future. * * @param {string} name A name of the part to add + * @param {int} [priority=0] Sets the load priority of this part. + * * @returns {object} $translatePartialLoaderProvider, so this method is chainable * @throws {TypeError} The method could throw a **TypeError** if you pass the param * of the wrong type. Please, note that the `name` param has to be a * non-empty **string**. */ - this.addPart = function(name) { + this.addPart = function(name, priority) { if (!isStringValid(name)) { throw new TypeError('Couldn\'t add part, part name has to be a string!'); } if (!hasPart(name)) { - parts[name] = new Part(name); + parts[name] = new Part(name, priority); } parts[name].isActive = true; @@ -268,40 +284,28 @@ angular.module('pascalprecht.translate') } var loaders = [], - tables = [], - deferred = $q.defer(); + deferred = $q.defer(), + prioritizedParts = getPrioritizedParts(); - function addTablePart(table) { - tables.push(table); - } + angular.forEach(prioritizedParts, function(part, index) { + loaders.push( + part.getTable(options.key, $q, $http, options.$http, options.urlTemplate, errorHandler) + ); + part.urlTemplate = options.urlTemplate; + }); - for (var part in parts) { - if (hasPart(part) && parts[part].isActive) { - loaders.push( - parts[part] - .getTable(options.key, $q, $http, options.$http, options.urlTemplate, errorHandler) - .then(addTablePart) - ); - parts[part].urlTemplate = options.urlTemplate; + $q.all(loaders).then( + function() { + var table = {}; + angular.forEach(prioritizedParts, function(part) { + deepExtend(table, part.tables[options.key]); + }); + deferred.resolve(table); + }, + function() { + deferred.reject(options.key); } - } - - if (loaders.length) { - $q.all(loaders).then( - function() { - var table = {}; - for (var i = 0; i < tables.length; i++) { - deepExtend(table, tables[i]); - } - deferred.resolve(table); - }, - function() { - deferred.reject(options.key); - } - ); - } else { - deferred.resolve({}); - } + ); return deferred.promise; }; @@ -312,11 +316,12 @@ angular.module('pascalprecht.translate') * @methodOf pascalprecht.translate.$translatePartialLoader * * @description - * Registers a new part of the translation table. This method does actually not perform any xhr - * requests to get a translation data. The new parts would be loaded from the server next time - * `angular-translate` asks to loader to loaded translations. + * Registers a new part of the translation table. This method does not actually perform any xhr + * requests to get translation data. The new parts will be loaded in order of priority from the server next time + * `angular-translate` asks the loader to load translations. * * @param {string} name A name of the part to add + * @param {int} [priority=0] Sets the load priority of this part. * * @returns {object} $translatePartialLoader, so this method is chainable * @@ -328,13 +333,13 @@ angular.module('pascalprecht.translate') * @throws {TypeError} The method could throw a **TypeError** if you pass the param of the wrong * type. Please, note that the `name` param has to be a non-empty **string**. */ - service.addPart = function(name) { + service.addPart = function(name, priority) { if (!isStringValid(name)) { throw new TypeError('Couldn\'t add part, first arg has to be a string'); } if (!hasPart(name)) { - parts[name] = new Part(name); + parts[name] = new Part(name, priority); $rootScope.$emit('$translatePartialLoaderStructureChanged', name); } else if (!parts[name].isActive) { parts[name].isActive = true; diff --git a/test/unit/service/loader-partial.spec.js b/test/unit/service/loader-partial.spec.js index 326a5fe28..99c1f7a18 100644 --- a/test/unit/service/loader-partial.spec.js +++ b/test/unit/service/loader-partial.spec.js @@ -38,6 +38,7 @@ describe('pascalprecht.translate', function() { return deferred.promise; }; }); + })); describe('$translatePartialLoaderProvider', function () { @@ -762,6 +763,57 @@ describe('pascalprecht.translate', function() { expect(counter).toEqual(2); }); }); + + it('should load parts in order of priority', function(done) { + module(function($provide) { + //Randomly delay $http response to simulate real-world scenario. + $provide.decorator('$httpBackend', function($delegate) { + var proxy = function(method, url, data, callback, headers) { + var interceptor = function() { + var _this = this, + _arguments = arguments, + _timeout = Math.floor((Math.random() * 50) + 1); + console.log('setting timeout to', _timeout); + setTimeout(function() { + callback.apply(_this, _arguments); + }, _timeout); + }; + return $delegate.call(this, method, url, data, interceptor, headers); + }; + for(var key in $delegate) { + proxy[key] = $delegate[key]; + } + return proxy; + }); + }); + + inject(function($translatePartialLoader, $httpBackend) { + var table; + + $httpBackend.whenGET('/locales/part1-en.json').respond(200, '{"key1":"value1","key2":"value2","key3":"value3","key4":"value4"}'); + $httpBackend.whenGET('/locales/part2-en.json').respond(200, '{"key2" : "overridenby2","key4":"overridenby2"}'); + $httpBackend.whenGET('/locales/part3-en.json').respond(200, '{"key3" : "overridenby3","key4":"overridenby3"}'); + + $translatePartialLoader.addPart('part2', 1); + $translatePartialLoader.addPart('part3', 2); + $translatePartialLoader.addPart('part1', 0); + $translatePartialLoader({ + key : 'en', + urlTemplate : '/locales/{part}-{lang}.json' + }).then(function(data) { + table = data; + expect(table.key1).toEqual('value1'); + expect(table.key2).toEqual('overridenby2'); + expect(table.key3).toEqual('overridenby3'); + expect(table.key4).toEqual('overridenby3'); + done(); + }, function() { + table = {}; + }); + + $httpBackend.flush(); + }); + }); }); });