Building an Ionic Multi App Project with Shared Angular Library Last update: 2019-09-03

Building an Ionic Multi App Project with Shared Angular Library

There is not a lot of documentation around the topic of Ionic Multi app projects, but it can be a powerful way to create a white label solution for your clients when you have all the projects in one workspace.

Today is your lucky day as we will do exactly that: Combine the Angular CLI with the Ionic setup for a multi app project and additionally create a shared library that can be used from all of the apps - and everything in one repository!

ionic-multi-app.structure

Make sure you have the Angular CLI installed as we need some of those commands as well.

Creating a Multi App Project Workspace

We could start with an Ionic app and remove some of the files, but it’s actually easier to create a workspace with the Angular CLI for which we can also add a flag to not create any application. This will create just a re barebones project with some files, so start with that:

# Create a workspace
ng new devdacticMulti --createApplication=false
cd ./devdacticMulti

# Create our projects directory
mkdir projects

Because Angular creates new projects and libraries in the projects/ folder we will follow that structure for our endeavour as well. The result for now looks like the image below.

ionic-multi-repo-setup To prepare the workspace for our Ionic apps, go ahead and create a file at the root of the project called ionic.config.json (which is normally automatically in Ionic projects) and simply fill it with:

{
  "projects": {}
}

Now all the Ionic apps that we generate will be added to the projects key of that file, and we can alter simply select one of them to run by passing a flag. Make sure that you navigate your command line into the projects folder for the next commands as otherwise the apps would be in the wrong place.

cd ./projects
# Create our Ionic Apps
ionic start appOne blank
ionic start appTwo tabs

Now you have a projects folder with 2 Ionic projects, a good start so far. If you were to follow the official guide, it wouldn’t work yet as the projects would not be found.

The reason is that the automatically generated angular.json file contains a generic app keyword, and we have to change it to match the name of our projects.

In order to fix this you need to open both files, here’s an example of the projects/appOne/angular.json file and the replacements:

{
  "$schema": "./node_modules/@angular-devkit/core/src/workspace/workspace-schema.json",
  "version": 1,
  "defaultProject": "appOne",
  "newProjectRoot": "projects",
  "projects": {
    "appOne": {
      "root": "",
      "sourceRoot": "src",
      "projectType": "application",
      "prefix": "app",
      "schematics": {},
      "architect": {
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "appOne:build"
          },
          "configurations": {
            "production": {
              "browserTarget": "appOne:build:production"
            }
          }
        },
        "ionic-cordova-build": {
          "builder": "@ionic/angular-toolkit:cordova-build",
          "options": {
            "browserTarget": "appOne:build"
          },
          "configurations": {
            "production": {
              "browserTarget": "appOne:build:production"
            }
          }
        },
        "ionic-cordova-serve": {
          "builder": "@ionic/angular-toolkit:cordova-serve",
          "options": {
            "cordovaBuildTarget": "appOne:ionic-cordova-build",
            "devServerTarget": "appOne:serve"
          },
          "configurations": {
            "production": {
              "cordovaBuildTarget": "appOne:ionic-cordova-build:production",
              "devServerTarget": "appOne:serve:production"
            }
          }
        }
      }
    }
  }
}

I removed a bunch of lines, in general there are 2 places in the beginning where you need to replace “app” by “appOne” (or the name of your project), and then you can search & replace “app:” and replace that with “appOne:” which should be another 12 occurrences in the whole file.

Same procedure for the second project that we added, but I think you know how to do it now. And of course for all other Ionic apps that you add to that workspace again!

A final look at the root ionic.config.json shows that the apps were added:

{
  "projects": {
    "appOne": {
      "name": "appOne",
      "integrations": {},
      "type": "angular",
      "root": "projects/appOne"
    },
    "appTwo": {
      "name": "app",
      "integrations": {},
      "type": "angular",
      "root": "projects/appTwo"
    }
  }
}

Now the Ionic CLI can find and build the right project, so all you have to do to run one of your apps is call the standard serve command and add the --project flag with the name of the project you want to run!

ionic serve --project appOne

Easy as that, 2 Ionic apps in one workspace, both can be served within the same directory.

Enabling Cordova Integration in Ionic Apps

Because the browser preview is not everything, let’s add Cordova to the projects. If you want to see Capacitor as well, just let me know!

The beginning looks promising again, we simply use the flag again but then:

ionic cordova platform add ios --project=appOne
...
...
[OK] Integration cordova added!
[ERROR] Could not find cordova integration in the appOne project.

It ends with a failure, and at this point you can’t build your Cordova app in the multi app project. The problem is that the ionic.config.json file (currently) adds the integrations key to the top-level object, and somehow not to the respective problem.

To fix the problem, open the ionic.config.json and copy the block that was added to the bottom of the file into the according information of the project where you want to add Cordova:

{
  "projects": {
    "appOne": {
      "name": "appOne",
      "integrations": {
        "cordova": {}
      },
      "type": "angular",
      "root": "projects/appOne"
    },
    "appTwo": {
      "name": "app",
      "integrations": {},
      "type": "angular",
      "root": "projects/appTwo"
    }
  },
  "integrations": {
    "cordova": {}
  }
}

Now you can try to run your command once again:

ionic cordova build ios --project=appOne

You will see that it now works, and the platforms/ folder is created inside the projects directory!

ionic-multi-app-cordova

This also means you can simply have the according resources like icon and splashcreen in each of the projects and they don’t override each other. No additional magic or copy task needed to configure your client project!

Building a Shared Angular Library

Right now it’s a repository with two separate Ionic apps, but they don’t share any logic or components.

To do this, you can now go ahead and create an Angular library right within that project as well!

ng generate library academyLib --prefix=academy
ng build academyLib --watch

Running the commands will create a new library in your projects folder, and you should always give a prefix to your library in order to know where which components come from, just like all the -ion components.

You always have to run the build command at least once for the library, but if you are working on it, it makes sense to simply watch all changes and it will rebuild immediately on safe.

Actually,the Ionic project will then rebuild itself as well afterwards if you run both the watch and Ionic serve in 2 terminals - I love it when stuff works easy as that!

ionic-shared-angular-library

The standard lib comes with a few files, and it’s enough for testing.

More on building a library with some additional configuration in another post maybe?

Right now you can’t use the library in the Ionic projects as they don’t really know about it, and the import path would be wrong or not work at all.

To fix this new problem, you can open the projects Typescript config, for example the projects/appOne/tsconfig.json and change it to:

{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "module": "esnext",
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "importHelpers": true,
    "target": "es2015",
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2018",
      "dom"
    ],
    "paths": {
      "academy-lib": [
        "../../dist/academy-lib"
      ],
      "academy-lib/*": [
        "../../dist/academy-lib/*"
      ]
    }
  }
}

We have simply added the path to our shared library in here, just like they were already added to the tsconfig.json at the root of the project.

Now we can easily go ahead and use that library in our appOne project, so open the projects/appOne/src/app/home/home.module.ts and add it like this:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IonicModule } from '@ionic/angular';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';

import { HomePage } from './home.page';
import { AcademyLibModule } from 'academy-lib';

@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    IonicModule,
    RouterModule.forChild([
      {
        path: '',
        component: HomePage
      }
    ]),
    AcademyLibModule
  ],
  declarations: [HomePage]
})
export class HomePageModule {}

Note that you should use the name academy-lib without any path, which your IDE might want to tell you. If you added the TS config before correctly, this works now!

To finally see it in action, simply use the default component of the lib in the projects/appOne/src/app/home/home.page.html:

<ion-header>
  <ion-toolbar>
    <ion-title>
      Ionic App One
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>

  <academy-academyLib></academy-academyLib>

</ion-content>

Now you can make changes to the lib while running watch and Ionic serve, and you get a sense of why this approach can be really powerful!

Feeling Fancy?

If you don’t enjoy how Ionic looks as a website, perhaps you want a standard Angular website? No problemo!

We are anyways in an Angular environment, so you could now go ahead with the Angular CLI and generate a new application and then serve it like this:

ng generate application academyWeb --routing --style scss
ng serve --open --project=academyWeb

Now you would have an Angular application right next to your Ionic apps, living happy and friendly in the same project!

Conclusion

There’s not a lot of information on the topic of Ionic multi app projects, but they can be really powerful if you can make them work for your white label solution or perhaps simply for having the Ionic app and website in one project.

The shared library we used could also be another project, and we could share and use it through NPM as well - if you want to see more about building your custom Ionic library and how to use it, just let me know and I’ll share everything you need to know about it!

You can also find a video version of this article below.