Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Module info #15225

Closed
wants to merge 3 commits into from
Closed

Conversation

petebacondarwin
Copy link
Contributor

What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)

feature

What is the current behavior? (You can also link to an open issue here)

No way to find information about the currently loaded modules

What is the new behavior (if this is a feature change)?

Two new features:

  • a method on Module (info()) that lets developers add arbitrary info about their modules.
  • a property on $injector (modules) that lets developers access the module objects that were loaded into the injector at bootstrap.

This second item could also be useful to @ocombe for his ocLazyLoad project.

Does this PR introduce a breaking change?

No

Please check if the PR fulfills these requirements

Other information:

Replaces the previous #12465 PR

@ocombe
Copy link
Contributor

ocombe commented Oct 7, 2016

It's happening !!!!

OMG

Is this real life?

src/loader.js Outdated
* {@link $injector#modules `$injector.modules`} property:
*
* ```js
* var version = $injector.modules['myModule'].info().version;
Copy link
Member

Choose a reason for hiding this comment

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

Wouldn't angular.module('myModule').info() also work?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, but in that case you do know, necessarily if the module was
a) loaded in the first place
b) added to the injector
If it was not loaded then your line of code would throw an exception

Copy link
Member

Choose a reason for hiding this comment

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

I mean use it in place of $injector.modules['myModule'].info() - i.e. at "runtime" - so it should have been added to the injector.
I was mainly curious basically. $injector.modules['myModule'] works for me 😃

Hm...since we brought it up, $injector is kind of ambiguous, since it may refer to the providerInjector (which doesn't know about modules) or the instanceInjector (which does).

I doubt anyone is using the providerInjector, but still it might be a good idea to add modules on the providerInjector as well (for symmetry).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't follow. You can't use angular.module('myModule').info() since you don't know if that module has been loaded or included in the current app.
In fact, you don't even know what the current app is since there might be more than one bootstrapped on a page.

Also, $injector is not ambiguous. It is the only injector that you can inject into components and services, etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But maybe I'm missing the point...

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 he's referring to the $injector that you can inject into config function which is not the same injector as is injected into a service, component, etc.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, I am referring to the providerInjector that can be injected into providers/config blocks. E.g.:

.provider('foo', ['$injector', function fooProvider(providerInjector) {
  this.$get = ['$injector', function foo(instanceInjector) {
    console.log(providerInjector === instanceInjector);   // false
  }
})

(Again, I don't expect many people to be actually using it, but you never know 😃)

Regarding angular.module('someModule').info():
Assume you are the author of a library or addon that relies on another library (e.g. myAwesomeMaterialToolbar). You can request that your addon is loaded after ngMaterial, so you can detect its version and do different things. E.g.:

// my-awesome-material-toolbar.js
var mdVersion = angular.module('ngMaterial').info().version;
var deps = (mdVersion >= '1.1.1') ? [] : ['myShimForAFeatureAddedInNgMaterial1.1.1'];

angular.module('myAwesomeMaterialToolbar', deps);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will fix the providerInjector/instanceInjector thing.

Regarding the load time dependencies use case. For sure you can do what you suggest with the functionality provided in this PR, although you are having to ensure that the developer loads the script files in the correct order and is not using some async loading helper that might mess things up.

It would be safer (IMO) to do configuration of your awesome library in providers, etc... Although I accept it is difficult to optionally add in a directive, you could make the directive a noop if not wanted.

Copy link
Member

Choose a reason for hiding this comment

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

Works for me 👍

src/loader.js Outdated
* or `this` if called as a setter.
*
* @description
* Additional info about this module
Copy link
Member

Choose a reason for hiding this comment

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

This makes it sound as if passing an object would extend the existing info (not overwrite it).
I think we should be pretty explicit about that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK


it('should store the object passed as a param', function() {
theModule.info({ version: '1.2' });
expect(theModule.info()).toEqual({ version: '1.2' });
Copy link
Member

Choose a reason for hiding this comment

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

I would add a test to verify it overwrites the previous info object.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK

@petebacondarwin
Copy link
Contributor Author

PTAL @gkalpak

*/
info: function(value) {
if (isDefined(value)) {
if (!isObject(value)) throw ngMinErr('aobj', 'Argument \'{0}\' must be an object', 'value');
Copy link
Member

Choose a reason for hiding this comment

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

This should probably be value instead of 'value' (i.e. without the quotes), right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No. I don't think so. The idea is that the error can be general and be used with other parameters names

Copy link
Member

@gkalpak gkalpak Oct 9, 2016

Choose a reason for hiding this comment

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

Right. Then why do you need the {0}? Why don't you just write 'Argument \'value\' must be an object' directly? I guess in order to make it easier to recognize the part that is interchangeable.
👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am not 100% sure but I think that the format string has to be consistent for all usage of the error.

var test1 = angular.module('test1', ['test2']).info({ version: '1.1' });
var test2 = angular.module('test2', []).info({ version: '1.2' });
module('test1');
inject(['$injector', function($injector) {
Copy link
Member

Choose a reason for hiding this comment

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

OOC, is the array necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Didn't we decide to make all our tests minification proof?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm sure there was an issue about it

Copy link
Member

@gkalpak gkalpak Oct 9, 2016

Choose a reason for hiding this comment

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

I don't remember anything like that (why would we)?
Maybe we have decided to make ngMocks minification-safe (because it makes it easier for some people to incorporate it into their workflow), but not our tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, yes you are right @gkalpak (as always) - I was thinking of this: #13542

Copy link
Member

Choose a reason for hiding this comment

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

They don't have to be minification-proof but shouldn't we aim at making them pass in strict DI mode?

Copy link
Member

Choose a reason for hiding this comment

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

There is not such thing as "strict DI" in tests :)
(Also none of the other tests are, so it is obviously not a problem.)

Copy link
Member

Choose a reason for hiding this comment

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

There is not such thing as "strict DI" in tests :)

What I meant by that is that we are manually annotating the injected functions, if strict DI is enabled.

@gkalpak
Copy link
Member

gkalpak commented Oct 10, 2016

LGTM (with or without the array notation 😃)

@petebacondarwin
Copy link
Contributor Author

Thanks @gkalpak - I just thought we should check that there is something similar in A2 before landing this.

@petebacondarwin petebacondarwin modified the milestones: 1.6.3, 1.6.x Feb 8, 2017
@petebacondarwin
Copy link
Contributor Author

So in Angular there is the NgModule concept, which provides a similar place to store useful metadata about your modules/app. Let's merge this.

Copy link
Member

@gkalpak gkalpak left a comment

Choose a reason for hiding this comment

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

Have you considered "version-stamping" ngLocale?

@@ -265,5 +265,6 @@ function publishExternalAPI(angular) {
$$cookieReader: $$CookieReaderProvider
});
}
]);
])
.info({ angularVersion: '"NG_VERSION_FULL"' });
Copy link
Member

Choose a reason for hiding this comment

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

Why not call it just version (since it's already namespaced by the module name).
Especially in other modules (ngAnimate, ngAria, etc).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I guess I was leaning towards the idea that you would stamp your module with the version of Angular that it was designed for, rather than the version of the module itself...

Copy link
Member

Choose a reason for hiding this comment

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

To me angularVersion seems more intuitive; otherwise I'd feel we're talking about the module version as Pete said.

Copy link
Member

Choose a reason for hiding this comment

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

I tend to think it as the module version indeed. But it doesn't make any real difference. angularVersion works for me.

@petebacondarwin petebacondarwin force-pushed the module-info branch 2 times, most recently from 972e0a2 to 13326ee Compare February 28, 2017 12:26
@petebacondarwin
Copy link
Contributor Author

I will add version stamping to the locale modules too.

@petebacondarwin
Copy link
Contributor Author

It turns out that adding the version stamp to the locale files is a little more involved with changes to grunt required :-(

@gkalpak
Copy link
Member

gkalpak commented Mar 1, 2017

Sounds like it's not worth it. It still LGTM without vervion-stamping locale files 😃

The new `info()` method lets developers store arbitrary information about
their module for consumption later.
The `modules` property is a hash of the modules loaded into the injector
at bootstrap time. This can be used to access the module's info.
You can now check what version of AngularJS a core module is designed for:

```
var angularVersion = $injector.modules['myModule'].info().angularVersion;
```
petebacondarwin added a commit that referenced this pull request Mar 2, 2017
The new `info()` method lets developers store arbitrary information about
their module for consumption later.

Closes #15225
ellimist pushed a commit to ellimist/angular.js that referenced this pull request Mar 15, 2017
The new `info()` method lets developers store arbitrary information about
their module for consumption later.

Closes angular#15225
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants