Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .jshintrc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"_": false,
"angular": false,
"require": false,
"module": false
"module": false,
"window": false
}
}
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ angular.module('myApp').config(function (widgetsProvider) {

You must set the manifest generator function in order for the directive to work. This is how we can know for a specific plugin, what files should be loaded and with which module name to create the injector. Above you can see an example for a manifest generator, but you can do whatever you like. You can put both relative and absolute URL's, of course.

*Note:* In case [requirejs](http://requirejs.org/) is available in the global scope, it will be used to load the javascript files. So if your widget needs more than one js file, you can include [requirejs](http://requirejs.org/) and use AMD to load them.

You can actually set multiple manifest generators and they will be evaluated in the order that they were defined. So a generator is allowed to return `undefined` in case it simply wants a different generator to handle it. The way the generators responses are handled is that the last generator that didn't return `undefined` will be used, unless a different generator returned a result with higher `priority`.

## Service Usage (widget)
Expand Down Expand Up @@ -125,7 +127,7 @@ BTW, one event that is shared by default is `$locationChangeStart`. This is in o

## How to use in the real world

This framework is best used by having a separate project for each widget. During development, the developer sees only his own widget. All widgets should be built in a consistent manner, usually with one concatenated minified .js and .css files.
This framework is best used by having a separate project for each widget. During development, the developer sees only his own widget. All widgets should be built in a consistent manner, usually with one concatenated minified .js and .css files.

## License

Expand Down
10 changes: 10 additions & 0 deletions app/scripts/services/tag-appender.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ angular.module('angularWidgetInternal')

return function (url, filetype) {
var deferred = $q.defer();
if (filetype === 'js' && window.requirejs && headElement === document.getElementsByTagName('head')[0]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I didn't understand why this headElement check is necessary...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First of all because I don't know if there are current scenarios where you're changing the element where to insert the scripts.. It's in a value so everyone can decorate or override it and I didn't want to introduce a back compatibility issue, as RequireJS will not respect the value of headElement and will insert the scripts always in document.head.

But also it is very convenient in order to avoid breaking the current tests (that where injecting a mock instead of headElement) as RequireJS will be present in all the tests.

I considered adding some kind of flag or configuration option to toggle requirejs on and off, but then I realized that the headElement is a nice toggle for testing purposes while also saving back-compatibility.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is better to use $window.requirejs and then you can mock $window to have or to not to have requirejs. I'll patch this up when merging later today. Thanks!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good to me. The bad thing is that it's going to affect all the other tests, but I guess it's o.k. Let me know if you want me to make it instead.

Thanks to you!

window.requirejs([url], function (module) {
deferred.resolve(module);
$rootScope.$digest();
}, function (err) {
deferred.reject(err);
$rootScope.$digest();
});
return deferred.promise;
}
if (requireCache.indexOf(url) !== -1) {
deferred.resolve();
return deferred.promise;
Expand Down
3 changes: 2 additions & 1 deletion bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"es5-shim": "~2.1.0",
"angular-route": ">= 1.2.0",
"angular-ui-router": "~0.2.11",
"angular-widget": "~0.1.30"
"angular-widget": "~0.1.30",
"requirejs": "~2.1.19"
}
}
4 changes: 3 additions & 1 deletion karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ module.exports = function (config) {
'{app,.tmp}/*.js',
'{app,.tmp}/scripts/*.js',
'{app,.tmp}/scripts/*/**/*.js',
{pattern:'{,.tmp}/test/mock/mock-lazyloaded-file.js',included:false},
'{,.tmp/}test/**/*.js',
'{app,.tmp}/views/**/*.html'
'{app,.tmp}/views/**/*.html',
'app/bower_components/requirejs/require.js'
],

// list of files / patterns to exclude
Expand Down
3 changes: 2 additions & 1 deletion test/.jshintrc
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"using": false,
"pause": false,
"resume": false,
"sleep": false
"sleep": false,
"window": false
}
}
1 change: 1 addition & 0 deletions test/mock/mock-lazyloaded-file.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
window.lazyLoadingWorking = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This effects global state, so once you set it to true one time it will remain true in future tests even if lazy loading does not work. You'll need to reset it back to false in beforeEach

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can give a try to requirejs.undef to see it that triggers the module loading again when requested.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

53 changes: 53 additions & 0 deletions test/spec/services/tag-appender.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,59 @@ describe('Unit testing tagAppender service', function () {
});
});

describe('Loading with requirejs when available and headElement is head', function () {

var moduleName = 'base/test/mock/mock-lazyloaded-file.js';

beforeEach(function () {
//Restoring the head element makes requirejs come into action
module({
headElement: window.document.getElementsByTagName('head')[0]
});
});

afterEach(inject(function ($window){
$window.requirejs.undef(moduleName);
$window.lazyLoadingWorking = undefined;
}));

it('should load the javascript files', inject (function (tagAppender, $window) {
var done = false;
expect($window.lazyLoadingWorking).toBeFalsy();
tagAppender(moduleName, 'js').then(function (mod) {
expect($window.lazyLoadingWorking).toBe(true);
done = true;
});
waitsFor(function() {
return done;
});
}));

it('should fail when file doesn\'t exist', inject (function (tagAppender) {
var done = false;
tagAppender('base/test/mock/non-existing-file.js', 'js').catch(function () {
done = true;
});
waitsFor(function() {
return done;
});
}));

it('should not fail when same file loads two times', inject (function (tagAppender, $window) {
var done = false;
expect($window.lazyLoadingWorking).toBeFalsy();
tagAppender(moduleName, 'js').then(function () {
tagAppender(moduleName, 'js').then(function () {
expect($window.lazyLoadingWorking).toBe(true);
done= true;
});
});
waitsFor(function() {
return done;
});
}));
});

it('should append script tag when js file is added', inject(function (tagAppender) {
tagAppender('dummy.js', 'js');
expect(headElement.appendChild.calls.length).toBe(1);
Expand Down