Ionic Multi-App

As part of upgrading from Ionic 3 to Ionic 4, the opportunity to improve the multi-app experience presents itself.

Overview

  • This generally follows along with the procedure worked out here: https://devdactic.com/ionic-multi-app-shared-library/.
  • This procedure sets up a monorepo: all applications and shared code within a single `git` repo. It should be possible to nest the repos such that each app has its own repo.
  • Shared code is compiled into the dist folder, and then that compiled code is placed on the path of each of the apps that reference the shared code.

Details

Attempt to introduce service method referencing HttpClient from the HttpClientModule

Components, models and services introduced

  • App-specific component named outing-summary
  • Reference to Outing type along with its dependency on the LatLon type.
  • Injection of OutingService
  • Dependency on HttpClient

Steps

  1. Bring in the HTML for the home.page.html file. This references a component named outing-summary.
  2. Bring in the app-specific outing-summary component to the App project.
    1. The ng generate code will prepend the app's name (app by default) to the name turning this into app-outing-summary.
    2. The component must be declared by the Module. In this case, this adds a line to the home.module.ts file:
  declarations: [
    HomePage,
    OutingSummaryComponent
  ]

# This brings in:
  • Reference to a type defined in the shared code (Outing)
  • Injected instance of global service (OutingService).

At this point, the library should be found, but it won't have exports for the Outing or the OutingService.

import {OutingService, Outing} from 'cr-lib';
  1. Add the OutingService and Outing into the shared lib.
    1. For Components and Services, use ng generate to make sure the structure is properly added to the library. We expect that the export file is updated with the added components and services. Unfortunately, it appears that the new components need to be added to the export list manually.
    2. Classes must be brought over directly and then manually added to the public API file (public-api.ts in our case).
  • The OutingService references HttpService which is imported as part of a module at the root of the App. This is commented out in the interest of limiting the scope.

Providing Library as a Module

The steps below follow along with https://devdactic.com/angular-ionic-library/ for setting up a Library of shared code as an NgModule. This appears to need to kick in at the point services defined within the library that depend on other services are referenced within the app.

  1. Create the Module file for the Library. Since this is a proof-of-concept at this time, simply calling it the crLibApiModule and letting it hang out in the api directory.

Issue

This was encountered after getting a successful compile and run (ionic serve) on the browser.

Inject()

  • After getting the OutingService to be recognized by using the providedIn: root construct of the Injector decoration, I get an error about the
Error: inject() must be called from an injection context
  • First attempt — which didn't work — was to add @angular/* to the paths:
      "@angular/*": [
        "../node_modules/@angular/*"
      ]

Big Clue

There are two different versions of Angular installed:

  1. At the top-level of the workspace which was created using the Angular CLI (ng command).
  2. At the project level of the projects/player-with-deps project which was created using ionic start command.

Each command has a different idea about which version of Angular should be installed. This was revealed in a few ways:

  • The version number inside the node_modules/@angular/common/http/http.d.ts file for each respective level.
  • There was a command that warned that I had two different Angular CLI installations.
  • The result of running ionic info with the two different projects:
[jett@tucana explore-cr-multi (master *+)]$ ionic info --project player
[ERROR] Error loading @ionic/angular package.json: Error: Cannot find module '@ionic/angular/package'
[ERROR] Error loading @ionic/angular-toolkit package.json: Error: Cannot find module '@ionic/angular-toolkit/package'

Ionic:

   ionic (Ionic CLI)             : 4.12.0 (/home/jett/.nvm/versions/node/v10.17.0/lib/node_modules/ionic)
   Ionic Framework               : not installed
   @angular-devkit/build-angular : 0.803.19 (/home/jett/new-git/explore-cr-multi/node_modules/@angular-devkit/build-angular)
   @angular-devkit/schematics    : 8.3.19 (/home/jett/new-git/explore-cr-multi/node_modules/@angular-devkit/schematics)
   @angular/cli                  : 8.3.19 (/home/jett/new-git/explore-cr-multi/node_modules/@angular/cli)
   @ionic/angular-toolkit        : not installed

System:

   NodeJS : v10.17.0 (/home/jett/.nvm/versions/node/v10.17.0/bin/node)
   npm    : 6.13.0
   OS     : Linux 4.4

[jett@tucana explore-cr-multi (master *+)]$ ionic info --project player-with-deps

Ionic:

   ionic (Ionic CLI)             : 4.12.0 (/home/jett/.nvm/versions/node/v10.17.0/lib/node_modules/ionic)
   Ionic Framework               : @ionic/angular 4.11.5
   @angular-devkit/build-angular : 0.801.3
   @angular-devkit/schematics    : 8.1.3
   @angular/cli                  : 8.1.3
   @ionic/angular-toolkit        : 2.1.1

System:

   NodeJS : v10.17.0 (/home/jett/.nvm/versions/node/v10.17.0/bin/node)
   npm    : 6.13.0
   OS     : Linux 4.4

I see it working now

  • No paths in the tsconfig.json
  • Using npm link between cr-lib's dist location and the projects/player-with-deps.
  • Updated the two versions of angular to match (8.1.3)
  • Recompiled the crLib

I had found that when I commented out the HttpClient import in the outing.service.ts, I was not getting the NullInjector error, but I had not recompiled the crLib code, so how was it picking up the changes? The outing-summary.component.ts was importing the OutingService by going straight to the directory instead of using the npm link.

Two paths forward:

  • Take another cut at re-building the env from scratch.
  • Modify the existing workspace where I've got most of my work done so that it aligns with the working explore workspace.

Leaning toward the latter, and here are the steps I took to move existing workspace over to a working workspace.

Steps to get Original Workspace working

  1. Removed paths entries from all tsconfig.json files.
  2. Changed the references for the shared library over to cr-lib and commented out several unused services until I've gotten them prepared.
  3. Added npm link cr-lib to the projects/player directory. (It took a while for IntelliJ to pick-up on this addition to the ./node_modules/ directory).
  4. I found out later that you need to add the following line to the angular.json file:
            "preserveSymlinks": true,

You get the following error in the console log if you miss this (link to the post — https://github.com/angular/angular/issues/25813):
Error: inject() must be called from an injection context

Because this is linking to the cr-lib from the other project, I haven't yet tested:

  • if a mis-match between compilers or angular components is significant
  • If the presence or absence of the @angular-devkit/build-ng-packagr is significant.

Next steps

  1. npm unlink cr-lib from projects/player
  2. npm unlink from explore workspace's dist (doesn't do anything, actually)
  3. npm r -g cr-lib — the recommended way to remove a global link (unlink doesn't work).
  4. Check that links have been removed
npm ls -g --depth=0 --link=true
  1. Rename 'shared' to 'cr-lib' (this will be interesting) and get it to compile.
  2. npm link from original workspace's dist.
  3. npm link cr-lib from projects/player.
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License