8

I'm building a component library for angular which we will share across projects. But when i build the package the html files don't get copied to the dist folder. I get the error angular Failed to load text-input.component.html.

This is my tsconfig.json:

{
  "compileOnSave": false,
  "compilerOptions": {
    "outDir": "./dist",
    "baseUrl": "src",
    "sourceMap": true,
    "declaration": false,
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "target": "es5",
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2016",
      "dom"
    ]
  },
  "exclude": [
    "node_modules",
    "dist",
    "**/*.spec.ts"
  ]
}

This is the compiled component:

"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
var core_1 = require("@angular/core");
var abstract_input_component_1 = require("./../abstract-input/abstract-input.component");
var TextInputComponent = /** @class */ (function (_super) {
    __extends(TextInputComponent, _super);
    function TextInputComponent() {
        var _this = _super.call(this) || this;
        _this.type = 'text';
        return _this;
    }
    TextInputComponent.prototype.ngOnInit = function () {
        //
    };
    __decorate([
        core_1.Input(),
        __metadata("design:type", Object)
    ], TextInputComponent.prototype, "type", void 0);
    TextInputComponent = __decorate([
        core_1.Component({
            moduleId: module.id,
            selector: 'hp-text-input',
            templateUrl: './text-input.component.html',
            styleUrls: ['./text-input.component.scss']
        }),
        __metadata("design:paramtypes", [])
    ], TextInputComponent);
    return TextInputComponent;
}(abstract_input_component_1.AbstractInputComponent));
exports.TextInputComponent = TextInputComponent;
//# sourceMappingURL=text-input.component.js.map

This is the uncompiled component with the template url:

import { Component, OnInit, Input } from '@angular/core';
import { AbstractInputComponent } from './../abstract-input/abstract-input.component';

@Component({
  moduleId: module.id,
  selector: 'hp-text-input',
  templateUrl: './text-input.component.html',
  styleUrls: ['./text-input.component.scss']
})
export class TextInputComponent extends AbstractInputComponent implements OnInit {
  @Input() type = 'text';

  constructor() {
    super();
  }

  ngOnInit() {
    //
  }
}

This is the compiled directory:

compiled directory

And this is the uncompiled directory:

uncompiled directory

These are my build scripts in package.json:

  "scripts": {
    "clean": "rm -rf dist/",
    "build": "npm run clean && tsc",
    "install": "npm run build"
  },

I've tried searching but most of the results are outdated or rely heavily on more dependencies. is there some config i'm missing?

3
  • Have you tried to inline html? How do you build your library? Commented Jan 11, 2018 at 15:44
  • try out templateUrl: 'text-input.component.html' Commented Jan 11, 2018 at 15:44
  • For creating a component library, ng-packagr works great: npmjs.com/package/ng-packagr. There's a guide over at medium.com/@nikolasleblanc/… Commented Jan 11, 2018 at 17:01

4 Answers 4

3

For angular 6 onwards you can tell the angular compiler to convert your template html file to inline template during build.

add "enableResourceInlining": true under angularCompilerOptions to your tsconfig.lib.json file inside your library folder to convert html files to inline template for angular components.

  "angularCompilerOptions": {
    "enableResourceInlining": true
  },
Sign up to request clarification or add additional context in comments.

1 Comment

this worked for me, the option is inside tsconfig.json, not tsconfig.lib.json
2

I've got an issue that is directly related to your one: Build Angular module with rollup.js: external html template file won't work (404). Then here I found a meaningful thread on this topic, and moving from link to link I saw the official document Angular Package Format (APF) v5.0, containing following:

Inlining of templates and stylesheets
Component libraries are typically implemented using stylesheets and html templates stored in separate files. While it's not required, we suggest that component authors inline the templates and stylesheets into their FESM files as well as *.metadata.json files by replacing the stylesheetUrls and templateUrl with stylesheets and template metadata properties respectively. This simplifies consumption of the components by application developers.

The way that allows you to use external templates/styles is based on deep customization of the build process, which should provide an artificially inlining of your external temlates/styles before compilation. The examples could be found on that GitHub thread (one man even published his solution on npm with 1k downloads per month). A useful approach is in my SO thread...

But I decided to take the middle path:

my.component.ts

import template from './my.component.temlate.html'
import style from './my.component.temlate.css'

@Component({
  selector: 'app-my-component',
  template: template + '',
  styles: [style + '']
})

my.component.temlate.html.ts

export default `
<div data-padding-top></div>
...
`

my.component.temlate.css.ts

export default `
:host {
  overflow-anchor: none;
  overflow-y: auto;
}
`

2 Comments

How did you get your import template from... to work? I'm getting an error: Cannot find module './cart.component.html'
this does only work in development build. not in production build.
0

You are using moduleId: module.id here so no need to use relative path,

Just remove ./ from templateUrl and styleUrls.

@Component({
  moduleId: module.id,
  selector: 'hp-text-input',
  templateUrl: 'text-input.component.html',
  styleUrls: ['text-input.component.scss']
})

2 Comments

Still doesn't compile the template
@SjoerddeWit, is it solved ? if not then try to remove moduleId: module.id, , it might work
0

I ended up using gulp to do this task since it's not possible with the angular CLI yet. I used this tutorial: How to set up an angular 2 component library

I must note that this probably isn't AOT compatible yet. but the project isn't in that phase yet, so that'll come when we need to be production ready. I'll update the answer at that time.

=======

UPDATE

I ended up using ng-packagr since angular also adopted it. Works nicely although you need to pay attention which version to use that corresponds with the angular version

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.