Angular tabbed components using a CDK portal outlet

While upgrading and rewriting parts of our configuration AngularJS modals at OpenProject, I needed to replace multiple modals with a single one that splits the configuration into separated tabs. Users make changes to their configuration in each of these tabs, switching between them and save all changes alltogether.

Since each previous modal (now tab) already provided saving changes, It makes sense to keep that logic separated in the tabbed components and allow each tab to save their changes whenever the modal is closed.

For splitting a shared content area, Angular CDK offers us portals, and specifically, the DomPortalOutlet to attach different components to a shared host element.

This is perfectly suited for tabs with isolated logic and templates you wish to switch between.

However when using the CDK outlets, each ComponentOutlet creates and destroys component instances whenever attaching and detaching them. That means creating components each time you switch between tabs.

For performance reasons, this will likely be irrelevant due to the tab content being negligible. However, as components are reinstantiated, you will lose all logic isolation if you wish to modify and keep user-provided data in a tab which is being detached.

Of course you can work around that by passing and injecting a data store for each tab, but then processing that data (e.g. assume submitting changes when clicking save) again requires some level of isolation.

Instead, we can create an extended portal outlet which I dubbed TabPortalOutlet that instantiates components as they are required, and keeps their instances around until the outlet is disposed.

Continue reading

Wednesday, 11. April 2018

Sticky table headers in Chrome with position: sticky

There is a plethora of options available when trying to solve the sticky table header problem. There's position fixed in a cloned header, full JavaScript solutions to keep the scroll offset and a mixed bag with many packages doing something in between.

All these solutions share the same problem: They remove the table headers from the browser's own width computation. If you have dynamic tables with cells that may change their widths as we do with our inline-editing functionality in OpenProject, the changing cells width will break the header widths since they are not in sync.

There is a draft spec for a stickily positioning an element: position: sticky.

A few years ago, position: sticky was available in Chrome when it still ran on webkit, and Firefox introduced support with v32.0 back in 2014.

Chrome now recently introduced support for position: sticky again in their own engine: Read the original blog post here.

The interesting part is, in Chrome, sticky works on table header cells as well. This makes it the easiest CSS-only solution to sticky table headers that do not have the above width synchronization problem. If you plan to use it, there are some hefty caveats, however. Primarily, this concerns browser support.

Continue reading

Sunday, 20. August 2017

Rewriting History of a Subversion Repository

At a customer installation of OpenProject, Subversion is still widely as a document management solution. While I advocate against its use for large repositories of documents, its use in that area exists historically due to the availability of clients and well-known processes for many users in the area of software development.

User errors such as deleted files and folders occur regularly and can be fixed client side. The real trouble beings when we receive incidents of files being added to the repository by mistake.

Unlike Git, Subversion (by design) does not provide client-side means to rewrite history. On the server-side, there is an ancient change request on the Subversion project tracker with some discussions in the past decade, but it appears to be a hard problem and no real solution has been achieved thus far.

Instead of rewriting a single revision, you will have to play back the whole repository with a specific exclusion of the bad changesets.

Continue reading

Tuesday, 12. January 2016

Sharing Host Volumes with Docker Containers

For development environments with Docker, a fundamental requirement is the synchronization of host folders with a Docker container. With it, a developer can employ the full power of the host to work on code locally, and instantly see these changes reflected inside the Docker container. While Docker Compose includes a command to trivially define synchronized folders with the volume command, development performance is impaired when the docker daemon is not running on the host itself—such as on OS X.

This post aims at giving an overview on the host volume performance impacts when running Docker on a non-Linux host. This post assumes a basic understanding of the capabilities of Docker and Compose. This preceding post introduces the fundamentals of Docker if you are not familiar with the Docker stack yet.

Continue reading

Saturday, 30. May 2015

An Introduction to Docker (Compose) for Development Environments

Docker has gained significant traction and stability since its beginnings in 2013. My previous experience was that creating complex application containers with multiple services was complicated and full of workarounds. Since then, Docker has taken over the linking tool Fig into Docker Compose, and has gained an interesting set of abstractions towards container orchestration. Still, there are many pitfalls regarding Docker use in both development and production. This post aims at giving an introduction to the possibilities of Docker and its limitations for isolated development environments.

Continue reading

Sunday, 3. May 2015

Show all articles (13)