Skip to content

Conditional App Activation

Conditional App Activation is a runtime feature in yuuvis Shell (available since version 2.8.0) that allows you to dynamically control which apps are available to users based on backend apps availability and user permissions. Instead of building different client versions for different deployments, you can configure a single client application that adapts its available functionality based on the environment and user context. The yuuvis client CLI supports this feature since version 19.6.0, automatically scaffolding new client projects with the necessary configuration.

The Shell evaluates app and extension availability at startup. Client apps and extensions that don’t meet their requirements are not loaded at all - they won’t appear in navigation and their code is never initialized. This works through a combination of client-side configuration (defining backend app requirements in app.requirements.ts and permission constraints in app.routes.ts) and backend-side administration (where administrators enable or disable backend apps for specific tenants).

Conditional App Activation solves several common challenges in enterprise application deployments:

Flexible Deployments Deploy the same client build across different environments with varying backend configurations. Client apps automatically adapt based on which backend apps administrators have enabled, without requiring code changes or separate builds.

Backend App Requirements Client apps and extensions can declare requirements for specific backend apps. If an administrator has not enabled a required backend app, or if it’s unavailable, the client app or extension is not loaded at all. This prevents users from accessing functionality that cannot work without its backend requirements and ensures that incomplete features don’t appear in the application.

Role-Based App Visibility Control which apps are visible based on user roles and permissions. Apps can define allowed or denied user authorities, ensuring users only see functionality they’re authorized to access.

Multi-Tenant Deployments Serve different feature sets to different customers or tenants using the same client application. Administrators can enable different sets of backend apps per tenant, determining which client apps are available without maintaining separate codebases.

Premium Features Implement tiered feature access where certain client apps require premium backend apps or specific license levels. Administrators control which backend apps are enabled based on the customer’s subscription level.

The conditional app activation mechanism operates through collaboration between client-side configuration and backend administration:

Configuration Side:

  • Client Development: Client developers define which client apps require the availability of which backend apps in app.requirements.ts. Permission constraints for each client app are defined separately in app.routes.ts.
  • Backend Administration: Administrators enable or disable backend apps for specific tenants based on deployment needs, license levels, or tenant requirements.

Runtime Evaluation:

  1. Initialization: During Shell bootstrap, the client fetches the list of enabled backend apps from the server and retrieves user authentication details including permissions.

  2. Evaluation: For each registered client app and extension, the framework evaluates two conditions:

    • Are the required backend apps (defined in app.requirements.ts) enabled and available?
    • Does the user have the required permissions (or lack forbidden permissions)?
  3. Loading Control: Client apps and extensions that don’t meet their requirements are not loaded at all:

    • Client apps: Neither the UI nor the extension service is initialized
    • Extensions: The extension service is not initialized
    • UI visibility: Apps are filtered from navigation menus and app launchers
    • Route protection: App routes are guarded to prevent direct navigation

This separation of concerns allows client developers to define which client apps require which backend apps, while administrators control backend app availability for tenants without modifying the client.

Conditional app activation provides two complementary mechanisms for controlling app availability. You can use either independently or combine them for fine-grained control.

Client apps (the apps in your Shell-based client application) can declare requirements for backend apps through a requirements mapping. This is useful when a client app needs specific backend capabilities to function.

For example, an advanced search client app might require a specific backend app for search services. If the administrator has not enabled that backend app, or if it’s unavailable, the client app is not loaded at all - neither its UI nor its extension service.

Requirements are defined in your client implementation in app.requirements.ts as a mapping from client app IDs to required backend app identifiers. The framework checks these requirements against backend-provided availability information during Shell bootstrap.

Key characteristics:

  • Client-side: Client developers define requirements in configuration files (app.requirements.ts)
  • Backend-side: Administrators enable/disable backend apps for tenants
  • Evaluated during Shell bootstrap against backend availability
  • Client apps and extensions are not loaded if their required backend apps aren’t enabled

Client apps can define which user authorities (roles) should have access through AppPermission configuration. This provides role-based access control at the app level.

Each app can specify:

  • Allowed authorities: User must have at least one of these roles to access the app
  • Denied authorities: User must NOT have any of these roles to access the app

For example, an administration client app might allow only users with the ADMIN role, while a reporting app might deny access to GUEST users.

Key characteristics:

  • Defined in client implementation (typically in route configuration files like app.routes.ts)
  • Evaluated against authenticated user’s authorities
  • Combined with backend app requirements using AND logic
  • Client apps are not loaded if permission requirements aren’t met

Combining both levels: When a client app or extension has both backend app requirements and permission constraints defined, both conditions must be satisfied for it to be loaded. A client app or extension is only loaded if its required backend apps are available (enabled by administrators) AND the user has appropriate permissions.

This section shows you how to implement conditional app activation in your Shell application using practical examples.

Backend app requirements define which backend apps must be enabled for a client app or extension to be loaded.

Create an app.requirements.ts file in your application’s source folder (or use the automatically generated one):

src/app/app.requirements.ts
import { Requirements } from '@yuuvis/client-core';
export const requirements: Requirements = {
// Client app requires the "osdrive" backend app
'io.yuuvis.app.drive': ['osdrive'],
// Search app requires multiple backend apps (all must be enabled)
'com.example.app.inbox': ['inbox', 'workflows'],
// Extension also uses the same pattern
'com.example.extension.pdf': ['pdf-service']
};

Key points:

  • The key is the client app or extension ID under which the app/extension is registered (found in the app manifest)
  • The value is an array of backend app names (all must be enabled)
  • Backend app names must match the keys returned by /api/dms/apps
  • If any required backend app is missing or disabled, the client app/extension is not loaded

Step 2: Register Requirements at Bootstrap

Section titled “Step 2: Register Requirements at Bootstrap”

Import and pass the requirements to the bootstrap function in main.ts:

src/main.ts
import { bootstrapShellApplication } from '@yuuvis/client-shell-core';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';
import { requirements } from './app/app.requirements';
bootstrapShellApplication(
AppComponent,
appConfig,
{
enableFeatureConfig: true,
enableObjectTypeConfig: true
},
requirements // ← Pass requirements here
).catch((err) => console.error(err));

What happens:

  • During bootstrap, the Shell fetches enabled backend apps from /api/dms/apps
  • The requirements configuration is registered with the FeatureService
  • Apps and extensions are evaluated against these requirements before loading

User permissions control which users can access specific client apps based on their roles (authorities).

Define permission constraints directly in your app route configuration:

src/app/app.routes.ts
import { Route } from '@angular/router';
import { App, canMatchFeature } from '@yuuvis/client-shell-core';
export const apps: App[] = [
{
id: 'com.example.app.admin',
title: 'Administration',
path: 'admin',
iconName: 'admin_panel_settings',
canMatch: [canMatchFeature], // ← Route guard checks permissions
// Permission configuration
permissions: {
allow: ['YUUVIS_TENANT_ADMIN', 'CUSTOM_SUPERUSER'], // User must have one of these roles
deny: []
},
loadChildren: () => import('@custom/app-admin').then(m => m.AdminRoutes)
},
{
id: 'io.custom.app.reports',
title: 'Reports',
path: 'reports',
iconName: 'assessment',
canMatch: [canMatchFeature],
// Deny specific roles
permissions: {
allow: [], // Empty = all users allowed (unless denied)
deny: ['CUSTOM_GUEST'] // Users with GUEST role cannot access
},
loadChildren: () => import('@yuuvis/app-reports').then(m => m.ReportsRoutes)
},
{
id: 'io.yuuvis.app.drive',
title: 'yuuvis explorer',
path: 'drive',
iconName: 'cloud_circle',
canMatch: [canMatchFeature], // ← Always use this guard
// Combine both: allow AND deny
permissions: {
allow: ['YUUVIS_DEFAULT', 'YUUVIS_TENANT_ADMIN'],
deny: ['CUSTOM_GUEST', 'CUSTOM_EXTERNAL']
},
loadChildren: () => import('@yuuvis/app-drive').then(m => m.DriveRoutes)
}
];

Permission evaluation logic:

  • permissions.allow: User must have at least ONE of these authorities
  • permissions.deny: User must NOT have ANY of these authorities
  • Both conditions must be true: isAllowed AND NOT isDenied
  • If permissions.allow is empty, all users are allowed (unless denied)

The canMatchFeature guard is essential for conditional activation:

import { canMatchFeature } from '@yuuvis/client-shell-core';
{
id: 'com.example.app.myapp',
canMatch: [canMatchFeature], // ← Required for conditional activation
// ...
}

What the guard does:

  • Checks backend app requirements (from app.requirements.ts)
  • Checks user permissions (from permissions.allow/permissions.deny in the route)
  • Prevents route matching if conditions aren’t met
  • Blocks navigation even via direct URL

In your app component, use the FeatureService to filter apps based on requirements and permissions:

src/app/app.component.ts
import { Component, inject } from '@angular/core';
import { ClientShellComponent } from '@yuuvis/client-shell';
import { App, ClientShellConfig, FeatureService } from '@yuuvis/client-shell-core';
import { apps } from './app/app.routes';
@Component({
selector: 'app-root',
standalone: true,
imports: [ClientShellComponent],
template: `
<yuv-client-shell
[apps]="apps"
[config]="shellConfig">
</yuv-client-shell>
`
})
export class AppComponent {
#featureService = inject(FeatureService);
// Filter apps based on requirements and permissions
apps: App[] = this.#featureService.getAvailableApps(apps);
shellConfig: ClientShellConfig = {};
}

How getAvailableApps() works:

  • Takes all configured apps as input
  • Filters out apps where backend requirements aren’t met
  • Filters out apps where user permissions don’t allow access
  • Returns only apps that should be visible and accessible

Result:

  • Apps that don’t meet requirements/permissions are removed from navigation
  • The Shell only displays apps the user can actually use