angular2-template-loader
angular2-template-loader copied to clipboard
Does not work - produced paths for require are incorrect.
Please, observe the following TypeScript file:
import { Component } from "@angular/core";
@Component({
selector: 'app-root',
templateUrl: './app/app.component.html'
})
export class AppComponent {
}
And it works fine before I use angular2-template-loader. When I am trying to use the latter and run webpack it fails with the following message:
polyfills.js 283 kB 1 [emitted] polyfills
vendor.js 3.32 MB 2 [emitted] vendor
main.js.map 17.2 kB 0 [emitted] main
polyfills.js.map 353 kB 1 [emitted] polyfills
vendor.js.map 3.94 MB 2 [emitted] vendor
index.html 607 bytes [emitted]
+ 912 hidden modules
ERROR in ./src/client/app/app.component.ts
Module not found: Error: Cannot resolve 'file' or 'directory' ./app/app.component.html in C:\Users\markk\IdeaProjects\QuestionarySample2\src\client\app
@ ./src/client/app/app.component.ts 18:22-57
...
Child html-webpack-plugin for "index.html":
+ 1 hidden modules
C:\Users\markk\IdeaProjects\QuestionarySample2>
(there are more errors like that for other components, I replaced them with an ellipsis)
The loader converts the aforementioned source into:
import { Component } from "@angular/core";
@Component({
selector: 'app-root',
template: require('./app/app.component.html')
})
export class AppComponent {
}
And now it fails.
As I understand, the reason is that require works off the current directory, whereas the templateUrl is relative to the root. So, './app/app.component.html' is correct when taken relative to the root, but wrong when relative to the current location.
It is worth noting, that I do not use require anywhere in the source code, only the ES6 import statement, which is also relative to the current location. It is only the Angular2 templateUrl and styleUrls that are relative to the root.
Anyway, I must be doing something wrong, because nobody seems to have my problem. But what am I doing wrong?
The file name is src/client/app/app.component.ts. The source code directory structure is:
C:.
│ .gitignore
│ karma.conf.js
│ package.json
│ tsconfig.json
│ tslint.json
│ typings.json
│ webpack.config.js
│
├───config
│ karma-test-shim.js
│ karma.conf.js
│ webpack.common.js
│ webpack.dev.js
│ webpack.prod.js
│ webpack.scratch.js
│ webpack.test.js
│
└───src
├───client
│ │ global.css
│ │ index.html
│ │ main.ts
│ │ polyfills.ts
│ │ tsconfig.json
│ │ vendor.ts
│ │
│ └───app
│ │ app.component.html
│ │ app.component.ts
│ │ app.module.ts
│ │ app.routing.ts
│ │ settings.component.ts
│ │ signout.component.ts
│ │
│ ├───assets
│ │ ...
│ │
│ ├───questionnaire
│ │ ...
│ │
│ └───shared
│ ...
│
└───server
api.ts
main.ts
tsconfig.json
The webpack configuration is:
webpack.config.js
module.exports = require('./config/webpack.dev.js');
./config/webpack.dev.js
var webpackMerge = require('webpack-merge');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var commonConfig = require('./webpack.common.js');
const path = require('path');
module.exports = webpackMerge(commonConfig, {
// devtool: 'cheap-module-eval-source-map',
devtool: 'source-map',
output: {
publicPath: `http://localhost:${commonConfig.port}/`,
filename: '[name].js',
sourceMapFilename: '[name].js.map',
chunkFilename: '[id].chunk.js'
},
plugins: [
new ExtractTextPlugin('[name].css')
],
devServer: {
inline: true,
hot: true,
progress: true,
port: commonConfig.port,
proxy: {
'/api*': {
target: `http://localhost:${commonConfig.port - 1}`
}
},
historyApiFallback: true,
watchOptions: {
aggregateTimeout: 300,
poll: 1000
}
}
});
./config/webpack.common.js
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
const path = require('path');
module.exports = {
port: process.env.PORT || 3000,
entry: {
polyfills: './src/client/polyfills',
vendor: './src/client/vendor',
main: './src/client/main'
},
output: {
path: path.join(__dirname, '../dist/client'),
},
resolve: {
extensions: ['', '.js', '.ts']
},
module: {
loaders: [
{
test: /\.ts$/,
loaders: ['ts', 'angular2-template-loader']
},
{
test: /\.html$/,
// loader: 'html'
loader: 'raw'
},
{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
loader: 'file?name=assets/[name].[hash].[ext]'
},
// {
// test: /\.css$/,
// exclude: path.join(__dirname, '../src/client/app'),
// loader: ExtractTextPlugin.extract('style', 'css?sourceMap')
// },
{
test: /\.css$/,
// exclude: path.join(__dirname, '../src/client/app'),
loader: 'raw'
}
]
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: ['main', 'vendor', 'polyfills']
}),
new HtmlWebpackPlugin({
template: 'src/client/index.html'
})
]
};
It's not angular2-template-loader which is complaining it's webpack. i have post an answer on your question on stackoverflow you can find explanation there.
The alternative you give me is as follows:
- When using templateUrl and styleUrls in Angular2 use paths relative to the current directory. At this point Angular2 is no longer able to find the html and css associated with the component. The code is broken.
- Make sure to use this loader, otherwise the produced code will not run, because it is broken - see 1.
If this is my choice, then I will just use require myself and I do not need this loader.
@jorawarsingh is correct, your path can simply be expressed as './app.component.html'
. If you'd like to use the flat require that is fine also.
I mean, the whole point of this loader is to not have angular try and make hundreds of requests to access your templates at runtime, so avoiding using their non-cannonical resolution strategy is a given.
Also you could create a custom resolver plugin to emulate this behavior instead when a request is being called for x.component.html
, and alias it to be ./app/x.component.html
if it's that important to you.
I would also be more than happy to merge a PR that adds a loader option called useAngularResolution
that will switch this behavior based upon the option. Would need to have tests and coverage included ofcourse.
Angular2 expects templateUrl: './app/app.component.ts'
. This is how we write it in Angular 2. What you guys are telling me that I should be writing templateUrl: './app.component.ts'
. This will only work if I use this loader later on. As is the component is broken.
I think it is wrong. This loader carries angular2 in its name. For me it means it should use "angular resolution" by default. I.e. rewrite templateUrl: './app/app.component.ts'
into template: require('./app.component.ts')
.
No also, this is the relative path strategy for the angular AoT compiler as well. See https://github.com/angular/angular/issues/8953 for more information. Like I said, I'm more than happy to accept a PR for an added feature.
Candidly speaking maintaining webpack, working on the angular-cli is more of a priority right now, than edge cases. I hope you can understand.
I do understand. Thank you for your work and effort.
Can someone tell me how to resolve this? I agree that i do not see why i should have to change all my templateUrls when using this loader should really do it for me? What happens if later on down the line i do not want to use this loader, i will have to go all through my code and change the templateUrls
Has someone found a fix for this yet?
Anyone found a solution for this? spent days with no luck. Any input will be appreciated.