Router should pass precompile array to component to avoid unneeded coupling
See original GitHub issueI’m submitting a … (check one with “x”)
[ ] bug report
[*] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
Current behavior
With the new version of the router (currently using “@angular/router”: “3.0.0-beta.1”), I’m getting this error when walking through the Router links section of the tutorial:
'HeroesComponent' not found in precompile array. To ensure all components referred to by the RouterConfig are compiled, you must add 'HeroesComponent' to the 'precompile' array of your application component. This will be required in a future release of the router.
I found the answer at this slack overflow post.
It seems the precompile array should define components that will be included in the current component. BUT if we use the router, we’d have to list all the components the router directs to AGAIN, needlessly making a list of all the components in the router.
The last statement on this slack overflow question is that this issue in Angular Router version: 3.0.0-beta.1 seems to be fixed in Angular Router version: 3.0.0-beta.2. Is this true and what’s the new behavior for the precompile array with routers?
Expected/desired behavior
What I’d like to see here is to have the router handle the precompile array if used. Perhaps we could pass the router to the precompile array, or search the bootstrap for a routerProvider. But if a router is used, I’d like to see all of the control given to the router, instead of coupling all the component views to the containing component, skipping the abstraction of the router.
Reproduction of the problem If the current behavior is a bug or you can illustrate your feature request better with an example, please provide the steps to reproduce and if possible a minimal demo of the problem via https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5).
In order to get this behavior, I walked through the quickstart and tutorial with the following package.json file and webpack setup:
package.json
{
"name": "angular2-webpack",
"version": "1.0.0",
"description": "A webpack starter for angular 2",
"scripts": {
"start": "webpack-dev-server --config ./webpack.dev.js --inline --progress --port 3000",
"es6start": "webpack-dev-server --config ./webpack.es6.dev.js --inline --progress --port 4000",
"build": "webpack --config ./webpack.config.js --progress --profile --bail",
"postinstall": "typings install"
},
"license": "MIT",
"dependencies": {
"@angular/common": "2.0.0-rc.4",
"@angular/compiler": "2.0.0-rc.4",
"@angular/core": "2.0.0-rc.4",
"@angular/forms": "0.2.0",
"@angular/http": "2.0.0-rc.4",
"@angular/platform-browser": "2.0.0-rc.4",
"@angular/platform-browser-dynamic": "2.0.0-rc.4",
"@angular/router": "3.0.0-beta.1",
"rxjs": "5.0.0-beta.6",
"core-js": "^2.4.0",
"reflect-metadata": "0.1.2",
"zone.js": "0.6.12"
},
"devDependencies": {
"babel-core": "^6.10.4",
"babel-loader": "^6.2.4",
"babel-preset-es2015": "^6.9.0",
"typescript": "^1.8.10",
"typings": "^1.0.4",
"webpack": "^1.13.0",
"ts-loader": "^0.8.1",
"webpack-dev-server": "^1.14.1",
"html-loader": "^0.4.3",
"html-webpack-plugin": "^2.22.0",
"extract-text-webpack-plugin": "^1.0.1",
"css-loader": "^0.23.1",
"style-loader": "^0.13.1"
}
}
webpack.dev.js
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var path = require('path');
var _root = path.resolve(__dirname, '.');
function root(args) {
args = Array.prototype.slice.call(arguments, 0);
return path.join.apply(path, [_root].concat(args));
}
module.exports = {
context: __dirname,
entry: {
app: './src/main.ts',
vendor: './src/vendor.ts',
polyfills: './src/polyfills.ts'
},
devtool: 'source-map',
output: {
path: root('dist/js'),
publicPath: 'http://localhost:3000/',
filename: '[name].bundle.js',
sourceMapFilename: "[name].map.js",
chunkFilename: '[id].chunk.js'
},
resolve: {
extensions: ['', '.js', '.ts']
},
module: {
loaders: [
{
test: /\.ts(x?)$/,
exclude: /node_modules/,
loaders: ['babel?presets[]=es2015', 'ts-loader'] //'angular2-template-loader'
},
{
test: /\.html$/,
loader: 'html'
},
{
test: /\.css$/,
exclude: root('src', 'app'),
loader: ExtractTextPlugin.extract('style', 'css?sourceMap')
// This must be used with the corresponding plugin
}
]
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: ['app', 'vendor', 'polyfills']
}),
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new ExtractTextPlugin('[name].css') // This must be used with the corresponding loader
],
devServer: {
historyApiFallback: true,
stats: 'minimal'
}
};
I encountered the problem at this point: https://angular.io/docs/ts/latest/tutorial/toh-pt5.html#!#router-links
What is the motivation / use case for changing the behavior?
Avoiding unneeded coupling seems to be a theme throughout angular. If this can be fixed before the router is out of beta, it would avoid a large coupling problem when using routing.
Please tell us about your environment:
- Angular version: 2.0.0-rc.4
- Angular Router version: 3.0.0-beta.1
- Browser: [Chrome 51.0.2704.103 (64-bit)]
- Language: [TypeScript 1.8.10 | ES6/7]
Issue Analytics
- State:
- Created 7 years ago
- Comments:9 (5 by maintainers)
Thank you ericmartinezr, that clears a lot up for me! So, piecing together some of these docs:
The new property
precompile
on the @Component decorator was introduced to refer to component types whose ComponentFactory should be resolved during runtime.Based on this information, a FactoryResolver is generated that maps component types to ComponentFactory per component. This mapping can be used inside of the component and in all of the components used in the template of the component. The FactoryResolver can be accessed via DI from within the component.
The
precompile
metadata will help when: we want to automatically populate a templateCache for runtime compilation we want to generate a single stylesheet for an applicationAs of router version 3.0.0-beta.2, support was added for calculating precompile based on providers (needed for the router), so called ANALYZE_FOR_PRECOMPILE
The router configuration is usually provided via the helper function
provideRoutes(someConfig)
. This creates a provider for the special tokenANALYZE_FOR_PRECOMPILE
viauseValue
andmulti:true
. When the compiler encounters this token, it will analyze the value deeply and look for referenced components. These component references will then be treated as if the user had put them into theprecompile
property.See https://github.com/angular/angular.io/issues/1745, it’s not yet documented, that is why that warning was commented out. You can track the issue I referenced and see all thr issues involved, there are a few docs you can read with all the thinking behind the precompile property.