Steering clear of using relative paths in Angular CLI

I am currently using the most up-to-date Angular CLI and have set up a custom components folder to store all of my components.

For instance, within the TextInputComponent, there is a TextInputConfiguration class located in src/components/configurations.ts. In the file

src/app/home/addnewuser/add.user.component.ts
where I am using it, you will find:

import { TextInputConfiguration } from "../../../components/configurations";

While this works for now, as my application grows larger and more complex, the ../ in the import path becomes cumbersome. How can I address this issue?

In the past, when working with SystemJS, I would configure the paths in the system.config.js file like so:

System.config({
..
 map: {'ng_custom_widgets': 'components'},
 packages: {'ng_custom_widgets': {main: 'configurations.ts', defaultExtension: 'ts'}},
)};

How can achieve similar functionality with webpack while utilizing the Angular CLI?

Answer №1

According to this particular comment, it is suggested that you include your application source using the paths property inside the tsconfig.json file:

{
  "compilerOptions": {
    ...,  
    "baseUrl": ".",
    "paths": {
      ...,
      "@app/*": ["app/*"],
      "@components/*": ["components/*"]
    }
  }
}

This allows you to import modules from app/ or components/ directly instead of relative to the current file:

import {TextInputConfiguration} from "@components/configurations";

Please note: It is necessary to specify a baseUrl if you are also using the paths property.

To learn more, refer to the official TypeScript documentation.

Answer №2

Big shoutout to jonrsharpe's response for guiding me in the right direction. Even though I added the recommended paths as per the suggestion, I still couldn't get it to work. In case someone encounters the same issue down the line, here's how I troubleshooted and resolved it.

I have a shared module with services utilized across multiple components, thus...

tsconfig.json:

{
    "compilerOptions": {
        ...
        "baseUrl": ".", //also included this
        "paths": {
            "@shared/*": ["src/app/modules/shared/*"]
        }
    }
}

With this update, VS Code successfully resolved the import, but during compilation, I encountered an error from webpack.

Module not found: Error: Can't resolve

To address this, I needed to incorporate

  1. baseUrl of tsconfig into webpack's resolve.modules
  2. paths of tsconfig within webpack's resolve.alias

webpack.config.js:

resolve: {
  extensions: ['*', '.js', '.ts'],
  modules: [
    rootDir,
    path.join(rootDir, 'node_modules')
  ],
  alias: {
    '@shared': 'src/app/modules/shared'
  }
},

component.ts:

import { FooService } from '@shared/services/foo.service'
import { BarService } from '@shared/services/bar.service'
import { BazService } from '@shared/services/baz.service'

To further streamline, I created an index.d.ts within the services directory and exported all services from there, like so:

index.d.ts:

export * from './foo.service';
export * from './bar.service';
export * from './baz.service';

and now within any component:

import { FooService, BarService, BazService } from '@shared/services';

Answer №3

After much searching and experimentation, I finally discovered the importance of understanding how baseUrl and Path work together to solve a problem.

If you set baseUrl:"." it may work in VScode, but not when compiling your code.

{
  "compileOnSave": false,
  "compilerOptions": {
    "outDir": "./dist/out-tsc",
    "baseUrl": ".",
    "paths": {
      "@myproject/*": ["src/app/*"]
    }    
}

Based on my experience and analyzing angular aio code, I recommend setting baseUrl:"src" like this:

{
  "compileOnSave": false,
  "compilerOptions": {
    "outDir": "./dist/out-tsc",
    "baseUrl": "src",
    "paths": {
      "@myproject/*": ["app/*"],
      "testing/*": ["testing/*"]
    }    
}

Setting the base url as the source (src directory) allows the compiler to properly resolve modules.

I hope this information will assist others in resolving similar issues.

Answer №4

For some reason, when I tested the other solutions in VS2017, Angular compiled without any errors. However, I still encountered issues with VS showing "Cannot find Module ...". Changing the baseUrl from "." to "src" resolved this issue.

tsconfig.json

{
  "compileOnSave": false,
  "compilerOptions": {
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "target": "es5",
    "typeRoots": [
      "node_modules/@types"
    ],
    "baseUrl": "src",                 // Main source directory same level as tsconfig
    "paths": {
      "app/*": [ "app/*" ],           // src/app
      "ui/*": [ "ui/*" ],             // src/ui       
      "services/*": [ "services/*" ], // src/services
      "assests/*": [ "assests/*" ],     // src/assests
      "models/*": [ "models/*" ]      // src/models
    },
    "lib": [
      "es2017",
      "dom"
    ]
  }
}

To import:

import { AppMenuComponent } from 'ui/app-menu/app-menu.component';

Note: If Visual Studio continues to display errors, try closing and reopening the file or restarting Visual Studio to recognize the updated paths.

Answer №5

When working with Angular 8, there is no longer a need for the *. If you use the *, it may result in an error message saying Cannot find module. To resolve this issue, simply include the following configuration in your tsconfig.json file:

"baseUrl": "./",
"paths": {
      "@test": [ "src/app/test/" ],
      "@somthing": [ "src/app/something/" ],
      "@name": [ "src/app/name/" ]
    },

Answer №6

Simplicity is Key

To keep things simple, we only need to understand a few key concepts in order to apply this solution to any upcoming projects.

Angular takes a different approach by not setting up the traditional @/ or ~/ path mappings, as it prefers to be less prescriptive in this area.

Instead, Angular assigns a baseUrl for all imports, starting with ./ (indicating the directory where the tsconfig.json file is located).

This implies that any imports without relative paths (not starting with ./ or ../) automatically include the baseUrl.

By using paths that originate from this base URL for your imports, you already have a solution in place! Refactoring should also be seamless if your tool recognizes the base URL and can apply import path refactoring accurately.

Therefore, with an unchanged TS config, you can use imports like the following example without worrying about managing relative paths:

// Sample import considering the preset baseUrl for automatic prefixing:

import { ComingSoonComponentModule } from 'src/app/components/coming-soon/coming-soon.module';

An Alternative Approach

If you prefer to implement a prefix like @/, you can achieve this easily by creating a simple path mapping, such as the one below:

// tsconfig.json

{
  // ...
  "compilerOptions": {
    "baseUrl": "./",
    // Note that the mapping below is relative to the baseUrl: `src/app/*` actually translates to `{baseUrl}src/app/*`, meaning `./src/app/*`.
    "paths": { "@/*": ["src/app/*"] },
    // ...
  },
  // ...
}

You can then import using either of these methods:

import { ComingSoonComponentModule } from 'src/app/components/coming-soon/coming-soon.module';
import { ComingSoonComponentModule } from '@/components/coming-soon/coming-soon.module';

Points to Keep in Mind:

  1. The paths option in tsconfig is relative to the baseUrl setting (therefore, ensure you use relative paths for the paths keys).
  2. The * symbol denotes the specific module path (as defined in your code), while everything else acts as a prefix or suffix added according to your specifications in the tsconfig file.

Answer №7

I've tried various solutions on Stackblitz, but none of them seem to work, even when using paths like src/app/... as suggested by Paul. It seems that Stack Blitz does not support paths specified in tsconfig.json, as highlighted in this issue.

The only workaround I discovered was to use ../, which allows you to navigate back one folder from the current file. You can stack multiple instances of ../ to go back multiple folders.

For instance, if my component is located in src/app/components/mycomponent, and the file I need to include is in src/app/shared/myservice, simply specifying src/app/shared/myservice/service wouldn't work on Stackblitz. In this case, I had to use ../../shared/myservice/service instead. The first ../ takes me back to src/app/components, while the second one resolves the directory to src/app, allowing access to the shared folder.

Answer №8

I have a more streamlined solution for this problem. Simply adjust the "baseURL" in your "tsconfig.json" file to an empty string:

"baseUrl": ""

With this change, you can now reference paths between files from the root directory like this:

import { ProductService } from 'src/app/services/product.service'; 

While these paths may appear relative, they are actually absolute paths that work efficiently. Personally, I prefer using absolute paths as it gives me a clear understanding of where each component is located physically.

It's worth noting that this resolves path issues related to module and component imports during Angular project building. However, it does not address problems with template, style, or image paths, which follow a different system when compiling the project.

To understand why the path system works the way it does in Angular, consider the "angular.json" workspace file in the project root. This file sets the "sourceRoot" folder, typically configured as "src". Therefore, when referencing "src/app/..." in your code, remember that "src" serves as the starting point for source files in relation to angular.json's location.

In Angular, the default "src" folder is crucial for resolving build paths effectively. It seems that webpack also looks at the "sourceRoot" value in angular.json during resolution processes.

After some trial and error, I discovered that setting the baseURL in tsconfig to "" enables the use of full paths starting from the "src" folder. These absolute paths offer a more intuitive way to visualize file locations from the top-level down, eliminating the need to navigate through complex file trees.

Personally, I find the unconventional "./" paths used by Angular developers to be cumbersome and counterintuitive. Understanding the limitations of relative paths in various environments could lead to more practical solutions that improve overall development workflows.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Using Typescript in a definition file requires classes and interfaces to be included in the compiled .js file

I am currently working on a Typescript project that consists of two crucial files: app.ts models.d.ts The initial lines of code in app.ts are as follows: ///<reference path="models.d.ts"/> 'use strict'; import * as fs from 'async-f ...

What could be the reason behind TypeScript automatically appending .default to a global import declaration?

Having a global definition within an external library file named thing.d.ts: declare var thing: ThingStatic; export default thing; Importing the npm module in TypeScript: import thing from 'thing'; ... thing.functionOnThing(); After transpili ...

Discovering duplicates for properties within an array of objects in React.js and assigning a sequential number to that specific field

I am working with an array of objects where each object contains information like this: const myArr=[{name:"john",id:1}{name:"john",id:2}{name:"mary",id:3}] In the first 2 elements, the "name" property has duplicates with the value "john". How can I updat ...

What is the best way to strip strings and special characters from a text, displaying only the numerical values without any commas, by

Using React Native TypeScript, I am looking to extract only the numbers from a string group without any commas. Is there a way to achieve this using regex match or replace? taskname = TASK_XC0.0.0.0.89t_abc_test let task = taskname.match( /[0-9]+/g, &apo ...

Ways to retrieve data types from the values of a different interface

Imagine having an interface called TableHeader: interface TableHeader{ key: string, value: string, } Next, we need to create an interface for TableData, where: interface TableData{ // ???? } In this table data, the key should be of the type that ma ...

Explain the concept of utilizing curried state handlers within a React and Typescript application

I am currently working on defining the function that will handle change events to update the state value accordingly. This is what I envision the implementation to look like: handleChange: ChangeHandler<State> = field => value => this.set ...

Almost at the finish line with Angular version 14 and .NET Core 6 WebAPI setup on IIS!

Earlier someone pointed out that I had not added the hosting model, which I have now taken care of. When building the source code: Using Angular ng build , the output directory is set to /API (the dotnet core 6 project). This results in wwwroot being loc ...

Struggling with setting container height to 100% in Angular/Material and encountering difficulties

Having some trouble creating a new UI mockup using Angular and Material design elements. I can't seem to get the area below my toolbar to fill the remaining vertical height of the page. Check out my code example here Despite setting html, body { hei ...

Assign Angular FromControl value to set the value of a select input

Seeking assistance on setting the initial value of the select dropdown below. I have attempted to do so through my formControl, but the value in the form is accurate while not reflecting in the view. HTML : <mat-form-field> <mat-select name="an ...

When using this.$refs in Vue, be mindful that the object may be undefined

After switching to TypeScript, I encountered errors in some of my code related to: Object is possibly 'undefined' The version of TypeScript being used is 3.2.1 Below is the problematic code snippet: this.$refs[`stud-copy-${index}`][0].innerHTM ...

Removing a portion of an item with the power of RxJS

I possess the subsequent entity: const myObject = { items:[ { name: 'John', age: 35, children: [ { child: 'Eric', age: 10, sex: 'M' }, { ...

The formBuilder controls' setValue() and patchValue() methods are not functional when called within a method of a Cordova plugin

In developing my Ionic tabs app, I have integrated the cordova-plugin-datepicker and utilized Angular Material to design the form fields. One issue I encountered is setting the text input's value with the date obtained from the plugin’s calendar in ...

Changing the position of the custom tooltip in Ag-grid

Is there a way to adjust the placement of my custom tooltip? I want it to always appear in the upper right corner of the table, rather than below the cursor. Here is an example of how it currently displays: pic1 I have a specific image of how I would like ...

Is it possible to alter CSS attributes dynamically without setting a limit on the number of changes that can be made?

In my current project, I am looking to add a feature that allows users to choose the color scheme of the software. While the choices are currently limited, I plan to implement a color picker in the future to expand the options available. So far, I have ex ...

Looking for guidance on locating Typescript type definitions?

As a newcomer to Typescript, I have recently delved into using it with React. While I have grasped the fundamentals of TS, I find myself perplexed when it comes to discovering or deriving complex types. For example, in React, when dealing with an input el ...

Reorganize mat-table rows using Angular Material's drag-and-drop feature

One of the new features that came with Angular 7 is the powerful DragDropModule: https://material.angular.io/cdk/drag-drop/examples The documentation covers rearranging items in lists and transferring them between different lists, but it doesn't ment ...

Tips for modifying a nested reactive form while ensuring validation is maintained?

When retrieving data from a service in the form of an array, one might wonder how to bind this data in a nested form if there are potentially endless entries in the array. The form is expected to be pre-filled with this data, including validation for each ...

Exploring the functionality of angular reactive forms in creating intricate JSON structures

After numerous attempts to resolve the issue on my own, I am reaching out to an Angular developer for assistance. My goal is to display a JSON object in the UI: Here is the JSON Object : items={"departure":"New York","arrival":"California","stations":[ ...

Having trouble accessing data through Angular's subscription?

UPDATE How I accomplished this task is detailed below (click on the link to view my answer): I am currently working on developing an APIService in Angular 7. My approach involves using subscribe to add data to an array that seems inaccessible through anot ...

Exporting enum in TypeScript declaration file (`.d.ts`) results in a "Unable to resolve" error

yesterday I was updating an enum in our app.d.ts file, which contains most of the app-specific types. The contents of the file are as follows: // app.d.ts export module App { // … more definitions enum MyEnum { A, B, ...