Pragmatic — \ prag-ˈma-tik \

Relating to matters of fact or practical affairs often to the exclusion of intellectual or artistic matters : practical as opposed to idealistic

In this case, pragmatic means a little hackish, but perfectly workable and much easier to set up than other solutions I’ve found. If you are a developer with a set of components that you want to share between a set of Ionic 3 applications, and you want each app to have access to all of those components, but you are not ready or willing to write your own TypeScript modules to include in each project, this approach may work for you.

To get right to the instructions, skip to “Step-by-step instructions” below.

Disclaimer

I am an experienced back end and web developer, but by no means an Angular or Ionic expert — it wasn’t until a few months ago that I began my first Ionic application, which was also my first Angular application ever. So I am certain I have lots to learn from more experienced developers. To those who are reading this: do tell me whatever you think I should know or learn about!

The problem

For your Ionic 3 application, you are writing some functionality for project A that you also want to use in project B, but you want to keep the files for that component in one location so you don’t have to keep copy-pasting code or merging edits from several versions. Also, you want to keep the code completion features of your editor working and you want edits in a shared component to still result in an automatic re-build of your development application so you don’t have to do anything extra while coding, just because you happen to be working in a shared component.

Sounds reasonable — but it’s surprisingly difficult to do well; you can’t simply write one file and include it from two locations as you would in PHP, or write a straightforward module and include it with npm as you would with JavaScript. It’s doable to reuse a file outside of your project folder for development builds, but then you do a build --prod call, the compiler switches to AOT compilation and you get strange errors, so you switch to doing a JIT build without the --prod flag for your production deployments — it’s just plain meh all around.

The official solution

The best solution seems to be to write a TypeScript module containing your shared code, and packaging and linking it to each project through npm. This is very possible, but far from trivial, as you can read for example in this fine article. I’m not sure if it allows you to edit the shared component from within the project you’re working on, though. But in the end, if you’re serious about maintaining multiple apps using some shared code base, or if you need to share different sets of shared components among more apps, this seems the way to go.

For those interested in doing this the right way by building a custom Angular 4 Component Library, this article should prove a valuable resource.

More experienced developers, please do chime in. Maybe I’m overestimating the complexity of this solution or missing a different approach!

My pragmatic solution

My solution is a little hackish but it’s easy to understand, setup and use, and ticks all the boxes for me. If you’re going to try it, be sure to read the “Caveats” section below.

My solution consists of:

Setting up a folder with shared components, alongside the Ionic projects. This is the single master copy of your shared components. My shared components folder is called ionic-shared-src and looks like this:

File structure of components shared between my Ionic applications

Creating a folder for shared components inside each Ionic project that uses them. We will not be symlinking the components into the projects, but copying them into (and from) there automatically using a script. For example, one project of mine looks like this:

File structure of an Ionic application using the shared componen

Creating the sync script to keep the files in those locations in sync, using the ionic-shared-src folder as the master copy.

Setting up git in that folder to make sure you never lose a change by accident.

Setting up a build hook in your Ionic project to automatically call the script, so you never have to think about it.

I’ve worked with this approach for a bit now, and for me it works beautifully. I have the code to all shared components available in each project, I can edit and use them with code completion, my Ionic project automatically rebuilds when I edit a shared component (because I am editing the copied version that lives right within the project) and I have a system in place that keeps everything in sync automatically.

Step-by-step instructions

I will assume you are using git for version control. If not, you’ll have to replace these basic git commands with the equivalent commands for your VCS. If you’re not using version control, start using version control now!

Create the ionic-shared-src directory anywhere you wish, put the components you want to share in there, following the structure of the first image above, and do a git init there to start a separate repo for your shared components. Create an executable file called sync-shared-src.sh somewhere in the Ionic project that’s going to use the shared components. You can find the contents of this file below this paragraph; just replace the paths at the beginning with your project and shared source path. Before you do this, make commits or backups of all your code. We don’t want to lose anything! Now call the script once by hand to verify that it’s working correctly. Did the shared component files show up in the src/shared directory in your project? Now add these two lines to the scripts section of your project’s package.json, so that the script will be called automatically everytime you run ionic serve or ionic build:

“ionic:build:before”: “./scripts/sync-shared-src.sh”,

“ionic:watch:before”: “./scripts/sync-shared-src.sh”

Adjust the path to the location of the script file within your project directory. Adjust the inclusion paths for the import statements in the shared components, and in the project files using them.

Shared components can import other shared components from ../ , because they live in the same directory in your project. For example, my shared component AuthServiceProvider imports another shared component called AlertServiceProvider like this:

import { AlertServiceProvider } from ‘../alert-service/alert-service’;

Project files can import shared components by simply adding ‘shared’ to the path that you’d normally use. Before I set this up, a page would import the AlertServiceProvider like this:

import { AlertServiceProvider } from ‘../../providers/alert-service/alert-service’;

Using the sync script and the shared folder, this simply changes into:

import { AlertServiceProvider } from ‘../../shared/providers/alert-service/alert-service’;

The sync script

The logic of this script is very straightforward; just read the 3 steps that are placed in the script as a comment.

#!/bin/bash # Set your paths here

PATH_LOCAL="/path/to/this/project/src/shared/"

PATH_SHARED="/path/to/shared/source/ionic-shared-src/"

##########################################

# No need to edit anything below this line

##########################################

currentWorkingDir=`pwd`

FILES_LOCAL=${PATH_LOCAL}*

FILES_SHARED=${PATH_SHARED}* # Step 1: If necessary, make a commit in ionic-shared-src to prevent loss of valuable changes before syncing printf "

> Committing current status of shared source

"

cd $PATH_SHARED

git add *

git diff --cached --quiet --exit-code if [ $? -eq 0 ]

then

printf "

>>> No uncommitted changes in shared src files, continuing.

"

else

printf "

>>> Uncommitted changes in shared src files; committing them before back-sync.

"

git commit -a -m "Auto-commit from ${PATH_LOCAL} before back-sync; the changes in this commit already existed before any sync operations took place."

fi # Step 2: Copy updates to shared files from the project back to ionic-shared-src and create a new git commit if there were changes printf "

> Copying shared source from project to shared directory where newer

"

rsync --progress -r -u $FILES_LOCAL $PATH_SHARED

git add *

git diff --cached --quiet --exit-code

if [ $? -eq 0 ]

then

printf "

>>> Back-sync didn't result in changes to shared files, continuing.

"

else

printf "

>>> Back-sync resulted in changes to shared files, committing.

"

git commit -a -m "Auto-commit from ${PATH_LOCAL} after back-sync; the changes in this commit were caused by the back-sync from that path."

fi # Step 3: Finally, copy any files from ionic-shared-src back to the project directory, if they are newer printf "

> Syncing shared src files to project where newer than the project version...

"

rsync --progress -r -u $FILES_SHARED $PATH_LOCAL # Exit nicely cd $currentWorkingDir

printf "

"

exit 0

I am not a Bash guru by any means, let me know if you see room for improvement in this script!

Caveats

The shared components live in their own git repo, and a copy of them live in each project’s git repo. As you will usually do edits in the project copies of your shared components, and not in the master version in ionic-shared-src, the commits in your project repository will describe the changes you made to them, while the commits created automatically by the sync script in the shared source repo are not descriptive at all. If this bothers you, you could update the script to ask for a commit message instead of using the default ones I put in there. This would be a nice addition! Let me know if you improve the script in this (or any other) way.

It is not very tempting to edit the shared components directly in the ionic-shared-src directory, because your editor will have no idea of the context in which the files will be used and will probably not be able to perform code completion and static code analysis. If you figure out a way to set this up using this approach, let me know!

My setup breaks down if you start working on shared components through multiple projects at the same time, because they will each be copying their own local copies over the master copy instead of doing smart merges. It would be possible to use git to solve this, putting the shared source in a git submodule in each project directory, but if you really need to go that far you’re probably better off setting up shared components in a more official way. In any case, you will never lose changes because of the automatic git commits (and because the project copies of the shared components should also be committed to your project’s VCS), but in case of missing code you might have to search and cherry-pick through the commit history of the shared source repo. In practice, this has never been a problem for me because I don’t tend to work on different projects at exactly the same time (maybe on the same day, yes, but as long as you’re not actively ionic serve-ing and ionic build-ing the two projects while making edits to shared components in both of them, that’s not a problem — see how rare that is?). Besides, I don’t make that many changes to the shared source files anyway. That’s what they’re shared components for ;).

I hope this article helped you set up easy component sharing for your Ionic applications. When you find that this approach becomes too limiting for your workflow, you should really switch to doing this the right way and split off the shared components into separate projects.

Let me know if you see room for improvement anywhere! Also, I would love to know exactly how to write and use shared components in a better way, so Angular and Ionic gurus: do chime in.

Happy coding!