Angular2 - root relative imports
Asked Answered
C

6

49

I have a problem with imports in angular2/typescript. I'd like to use imports with some root like 'app/components/calendar', instead only way I am able to use is something like:

//app/views/order/order-view.ts
import {Calendar} from '../../components/calendar 

where Calendar is defined like:

//app/components/calendar.ts
export class Calendar {
}

and this obviously gets much worse the lower in hierarchy you go, deepest is '../../..' but it is still very bad and brittle. Is there any way how to use paths relative to project root?

I am working in Visual Studio, and relative imports seem to be the only thing that makes VS able to recognize these imports.y

Collectivity answered 5/1, 2016 at 15:4 Comment(1)
"baseUrl" maybe should set "./src" and paths relate to it wiil get it to work, it origin to be "./" , but my is not work, I don't know why! and then add "paths": { "@app/*": ["app/*"] } to tsconfig.json and use like import { PageNotFoundComponent } from '@app/error-page/page-not-found.component';Redaredact
B
18

UPDATE

Just don't use this solution. Embrace node module resolution algorithm. Community rests on it, so everything will break apart if you try to do otherwise. Use aliases or some of the other provided solutions.

Short answer There's a way but you shouldn't do it. Set the compilerOption "moduleResolution" to "classic".

Long answer

Are you using tsconfig.json? I assume you are. I've been looking a way to make statements such as import someModule = require ("../../../../someModule" into import someModule=require("src/path/to/someModule"). I found after hours wasted that tsc may use different algorithms for module resolution. I'm using atom and it creates the tsconfig.json with the compilerOption property moduleResolution set to "node" and it uses the shitty (excuse my french) module resolution algorithm of node. I just put "classic" and started working the obvious way.

Buckjumper answered 12/1, 2016 at 0:40 Comment(7)
"classic" option completely breaks angular2, do you know any way how could this be fixed?Collectivity
have your source files be in a folder named "node_modules". It is explained in this answer: https://mcmap.net/q/87818/-how-can-i-make-node-js-39-require-39-absolute-instead-of-relativeBuckjumper
put all your code inside a folder called "node_modules"... that's the other optionBuckjumper
thats the most possible worst option i have ever heard of @BuckjumperMatazzoni
Do any of the 19 people that have so far upvoted @nottinhill have a better solution?Stgermain
Embrace node resolution algorithm (not joking, it's, after dealing with it a log, not that bad). You can otherwise start using alias for your modules.Buckjumper
@Stgermain look at Mareks answer belowRubbing
T
35

Answer

As of TypeScript 2.0 you can set tsconfig.json properties baseUrl as following:

{
  "compilerOptions": {
    "baseUrl": "app"
}

Then, you might use your desired manner of importing components, like:

import { Calendar } from 'components/calendar';

Appendix

Fallback paths resolution

An important consideration is that specifying baseUrl option causes TypeScript compilator to:

  1. Look up a path with regards to baseUrl
  2. On failed resolution (module not found), look up a path with regards to moduleResolution option

SystemJS

Since SystemJS is heavily used in Angular development stage, be sure to accordingly config also systemjs.config.js, so that it resolves paths correctly.


Source and further details: https://github.com/Microsoft/TypeScript/issues/5039

Tails answered 17/2, 2017 at 20:30 Comment(3)
@MerekDulwski , I referenced your solution in a question where I have issues with it and TSLint: #51292639Lambaste
I actually found this is NOT the ideal solution after I was having problems with it. I posted my new preferred solution here: https://mcmap.net/q/351111/-angular2-root-relative-imports Please give it a try.Lambaste
Actually this is the same solution. It's just the baseUrl option set to root project directory that makes it different from my suggestion, which, I agree, wasn't the best possible. I'll update my answer, glad you have found the perfect solution.Tails
B
18

UPDATE

Just don't use this solution. Embrace node module resolution algorithm. Community rests on it, so everything will break apart if you try to do otherwise. Use aliases or some of the other provided solutions.

Short answer There's a way but you shouldn't do it. Set the compilerOption "moduleResolution" to "classic".

Long answer

Are you using tsconfig.json? I assume you are. I've been looking a way to make statements such as import someModule = require ("../../../../someModule" into import someModule=require("src/path/to/someModule"). I found after hours wasted that tsc may use different algorithms for module resolution. I'm using atom and it creates the tsconfig.json with the compilerOption property moduleResolution set to "node" and it uses the shitty (excuse my french) module resolution algorithm of node. I just put "classic" and started working the obvious way.

Buckjumper answered 12/1, 2016 at 0:40 Comment(7)
"classic" option completely breaks angular2, do you know any way how could this be fixed?Collectivity
have your source files be in a folder named "node_modules". It is explained in this answer: https://mcmap.net/q/87818/-how-can-i-make-node-js-39-require-39-absolute-instead-of-relativeBuckjumper
put all your code inside a folder called "node_modules"... that's the other optionBuckjumper
thats the most possible worst option i have ever heard of @BuckjumperMatazzoni
Do any of the 19 people that have so far upvoted @nottinhill have a better solution?Stgermain
Embrace node resolution algorithm (not joking, it's, after dealing with it a log, not that bad). You can otherwise start using alias for your modules.Buckjumper
@Stgermain look at Mareks answer belowRubbing
N
13

Basically, in Angular 6 you can start the import from the top or bottom.

My two options

They are available with default config generated by angular CLI

  1. From the top
    I prefer this way if it's closer from the root of the app

    import { DateService } from "src/app/shared-services/context/date.service";

  2. From the bottom

    import { DateService } from "../../../../../../../shared-services/context/date.service";

Context:

TypeScript config: tsconfig.json

{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "target": "es5",
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2017",
      "dom"
    ]
  }
}


Angular's stack

ng -v

Angular CLI: 6.0.8
Node: 8.9.0
OS: win32 x64
Angular: 6.0.7
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.6.8
@angular-devkit/build-angular     0.6.8
@angular-devkit/build-optimizer   0.6.8
@angular-devkit/core              0.6.8

@angular-devkit/schematics        0.6.8
@angular/cli                      6.0.8
@ngtools/webpack                  6.0.8
@schematics/angular               0.6.8
@schematics/update                0.6.8
rxjs                              6.2.1
typescript                        2.7.2
webpack                           4.8.3
Norton answered 18/7, 2018 at 14:57 Comment(0)
C
7

One way would be to have files that re export and bundle the files with a shorter path.

You could have a components.ts folder in the root of your application with.

export {Calendar} from './components/calendar'
export {*} from './components/map'

And importing it from components

import {Calendar, Map} from '../../components';

This however will is better suited to exporting modules so others can use them, than the way to structure a project.

The alternative would be to forgo the use of import statements and use internal modules instead.

calendar.ts

module Components {
    export class Calendar {}
}

And you will be able to just use it in any of the files in your projects like this.

new Components.Calendar();

Or import it with an alias.

import Calendar = Components.Calendar;
new Calendar();
Chanson answered 5/1, 2016 at 16:37 Comment(0)
C
5

Tl;dr: you should now be able to use src as the root and it will "just work".

With Angular 2.0, when you run ng new my-proj, it auto-creates a file my-proj/src/tsconfig.app.json. This file contains a baseUrl line:

"baseUrl": "./",

Thus, you shouldn't have to change anything about your config and you can scope all of your imports rooted on src. Assuming your project structure looks something like:

my-proj/
  README.md
  src/
    app/
      sidebar/
        sidebar.component.ts
      user.service.ts

you can import your files with:

import { Sidebar } from 'app/sidebar/sidebar.component';
import { UserService } from 'app/user.service';

Edited to add: Sublime does not read src/tsconfig.json, though, it only reads the top-level tsconfig.json (see this issue). If you're getting Cannot find module 'app/...' errors on your import lines in Sublime, you can add:

"baseUrl": "./src",

to your top-level tsconfig.json file.

Cristacristabel answered 12/4, 2018 at 16:35 Comment(0)
L
4

You don't need to change anything from the default configuration of a new Angular project generated from Angular-CLI. Not sure about previous versions, but as of version 6, I know this is true. To do a root-relative import, you use src/ at the beginning of your imports like:

import { VideoPlayerComponent } from 'src/app/pages/shared/video-player/video-player.component';

This a better solution than @MarekDulowski's answer, because root-relative imports can be easily identifiable and easily found/searched for globally by querying "src/.

I learned this from the solution here: https://mcmap.net/q/356608/-typescript-tslint-tslint-not-recognizing-root-relative-imports-via-baseurl

Lambaste answered 30/7, 2018 at 17:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.