It’s one of those gray areas where “can” and “can’t” don’t really live. It’s more of the land of “easier” vs. “harder”, but maybe a 10,000-meter overview of the problem all this was designed to solve is in order.
JavaScript was built in about a week and a half by more or less one person, in the early days of the web when “web designers” were clamoring for ways to make websites more sparkly. It wasn’t intended for managing big codebases, and therefore isn’t great at it.
Compiled languages like C get written in source form in *.c
files. The compiler turns those into object code *.o
files. A linker mashes all those *.o
files together into an executable. At that stage, especially when third-party libraries get involved, a naïve linker runs into a “dead code” problem, whereby a bunch of useless cruft gets put in the executable.
The modern webapp build process emulates a lot of this same dynamic. You write TypeScript, it gets transpiled into JavaScript, that JavaScript gets munged further to shorten the names of variables and functions and so on, and then all that stuff gets mashed together by a bundler like webpack.
In *.o
files, every public entry point - any code that can conceivably be called from anywhere outside that file - is obvious. The compiler does this in order to make the linker’s job easier. That can’t be easily done in a webapp, because the “object code” format is still interpreted JavaScript, which doesn’t have proper access control built into the language like C does.
But developers still clamored for dead code removal, and therefore we the authors have to take responsibility for doing what a compiler should IMHO be doing. To make JavaScript amenable to the sort of static code analysis that is needed to say definitively “yep, we can safely leave this part out of the build entirely”, “nope, we need that around always”, or “well, we can stash this bit over here and avoid loading it until somebody clicks on the map tab” requires some help on the part of the author, and that’s where NgModule
was born.
The Angular people decided (and this probably makes sense) that putting the intermodule entry point documentation stuff into the @Component
decorator would be a bad idea, because it isn’t just individual components that need to get managed that way - pipes and services do as well, and in many cases, in groups.
If anybody’s still awake at this point, one last bit about overloaded terminology. There are two senses of “import” here, and to further complicate matters, “inject” often gets thrown into this blender as well.
When you import
something at the top of a TypeScript file, that’s a source code level thing that says to the transpiler “yo, in order to properly process this file, you’re gonna need to read that one over there”. This can be individual components if you wish.
When you add something to the imports
stanza of an NgModule
, the transpiler doesn’t really care. It’s the bundler that takes that into account when deciding which of your code gets put in the same .js
file that is loaded at runtime. This can’t be individual components, and must be other NgModule
s.
When you inject something into the constructor of a component or service, that’s a hint to the dependency injection system (DI) that operates at runtime. This predates the notion of NgModule
and doesn’t work for individual components because the semantics don’t really make any sense for it to.
Hope that sort of answers your question.