Open Closed

CMS Kit Angular admin: provide a component-free menu/route registration entry so the admin can be lazy-loaded #10746


User avatar
0
Sergei.Gorlovetsky created

Versions: @abp/ng.cms-kit + @volo/abp.ng.cms-kit-pro 10.4.1, Angular 21, esbuild/@angular/build.

Problem: provideCmsKitAdminConfig() / provideCmsKitProAdminConfig() (and eCmsKit*AdminRouteNames) live in …/admin/config, whose fesm has a top-level import { CmsSettingsComponent } from '…/admin'. ./admin is one monolithic, side-effectful fesm (statically imports ToastuiEditorComponent + CodeMirrorEditorComponent + all grids). Because the import is side-effectful, bundlers cannot tree-shake it, so registering the CMS menu eagerly forces ~1.1 MB of admin/editor code into the initial bundle, even for apps that lazy-load the /cms routes and rarely use the editor.

Request: Split a component-free menu/route-metadata registration (the RoutesService.add(...) calls + the route-name/policy enums) into its own entry point (e.g. …/admin/config/routes) that does not import CmsSettingsComponent/./admin. Then apps can register the menu eagerly and lazy-load the heavy admin (incl. the settings tab via a lazily-loaded component) without pulling the editor into the initial bundle.

Markdown supported.
Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)

3 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    You're right — provideCmsKitAdminConfig() and provideCmsKitProAdminConfig() both drag the heavy admin entry into the initial bundle, exactly as you described. The chain runs through setting-tab.provider.ts, which top-level imports CmsSettingsComponent from @abp/ng.cms-kit/admin / @volo/abp.ng.cms-kit-pro/admin. Those entries export * from ./components, and a few of those components (page-form, blog-post-form, global-resources, and Pro's faq-question-modal) statically import ToastuiEditorComponent and CodeMirrorEditorComponent. So registering the menu eagerly is enough to anchor the editor + grids in the initial graph, even when the actual /cms routes are lazy-loaded.

    We'll track this and move the menu/route registration out of the heavy entry in a future release — the model @volo/abp.ng.identity/config already uses, where IdentitySettingsComponent lives inside the config entry itself.

    In the meantime, drop this file into your app and call these in place of provideCmsKitAdminConfig() / provideCmsKitProAdminConfig():

    import { eLayoutType, RoutesService } from '@abp/ng.core';
    import {
      EnvironmentProviders,
      inject,
      makeEnvironmentProviders,
      provideAppInitializer,
    } from '@angular/core';
    
    // Enum values inlined from @abp/ng.cms-kit/admin/config and
    // @volo/abp.ng.cms-kit-pro/admin/config. Importing any symbol from those
    // entries pulls setting-tab.provider.ts in, which top-level imports
    // CmsSettingsComponent from the heavy admin entry — that's the import that
    // drags the editor + grids into the initial bundle.
    const CmsKitRouteNames = {
      Cms:             'CmsKit::Menu:CMS',
      BlogPosts:       'CmsKit::BlogPosts',
      Blogs:           'CmsKit::Blogs',
      Comments:        'CmsKit::CmsKit.Comments',
      GlobalResources: 'CmsKit::GlobalResources',
      Menus:           'CmsKit::Menus',
      Pages:           'CmsKit::Pages',
      Tags:            'CmsKit::CmsKit.Tags',
    } as const;
    
    const CmsKitPolicyNames = {
      BlogPosts:       'CmsKit.BlogPosts',
      Blogs:           'CmsKit.Blogs',
      Comments:        'CmsKit.Comments',
      GlobalResources: 'CmsKit.GlobalResources',
      Menus:           'CmsKit.Menus',
      Pages:           'CmsKit.Pages',
      Tags:            'CmsKit.Tags',
    } as const;
    
    const CmsKitProRouteNames = {
      Cms:           'CmsKit::Menu:CMS',
      Newsletters:   'CmsKit::NewsletterUsers',
      Polls:         'CmsKit::Polls',
      Faqs:          'CmsKit::Faqs',
      PageFeedbacks: 'CmsKit::PageFeedbacks',
      UrlShorting:   'CmsKit::UrlForwarding',
    } as const;
    
    const CmsKitProPolicyNames = {
      Cms:           'CmsKit.Newsletter || CmsKit.Poll || CmsKit.Faq || CmsKit.PageFeedback || CmsKit.UrlShorting',
      Newsletters:   'CmsKit.Newsletter',
      Polls:         'CmsKit.Poll',
      Faqs:          'CmsKit.Faq',
      PageFeedbacks: 'CmsKit.PageFeedback',
      UrlShorting:   'CmsKit.UrlShorting',
    } as const;
    
    export function provideCmsKitAdminRoutesLight(): EnvironmentProviders {
      return makeEnvironmentProviders([
        provideAppInitializer(() => {
          const routes = inject(RoutesService);
          routes.add([
            { path: '/cms/blog-posts',       name: CmsKitRouteNames.BlogPosts,       parentName: CmsKitRouteNames.Cms, order: 1,  layout: eLayoutType.application, iconClass: 'fa fa-file-alt', requiredPolicy: CmsKitPolicyNames.BlogPosts },
            { path: '/cms/blogs',            name: CmsKitRouteNames.Blogs,           parentName: CmsKitRouteNames.Cms, order: 2,  layout: eLayoutType.application, iconClass: 'fa fa-blog',     requiredPolicy: CmsKitPolicyNames.Blogs },
            { path: '/cms/comments',         name: CmsKitRouteNames.Comments,        parentName: CmsKitRouteNames.Cms, order: 3,  layout: eLayoutType.application, iconClass: 'fa fa-comments', requiredPolicy: CmsKitPolicyNames.Comments },
            { path: '/cms/global-resources', name: CmsKitRouteNames.GlobalResources, parentName: CmsKitRouteNames.Cms, order: 5,  layout: eLayoutType.application, iconClass: 'fa fa-globe',    requiredPolicy: CmsKitPolicyNames.GlobalResources },
            { path: '/cms/menus',            name: CmsKitRouteNames.Menus,           parentName: CmsKitRouteNames.Cms, order: 6,  layout: eLayoutType.application, iconClass: 'fa fa-bars',     requiredPolicy: CmsKitPolicyNames.Menus },
            { path: '/cms/pages',            name: CmsKitRouteNames.Pages,           parentName: CmsKitRouteNames.Cms, order: 9,  layout: eLayoutType.application, iconClass: 'fa fa-file',     requiredPolicy: CmsKitPolicyNames.Pages },
            { path: '/cms/tags',             name: CmsKitRouteNames.Tags,            parentName: CmsKitRouteNames.Cms, order: 11, layout: eLayoutType.application, iconClass: 'fa fa-tags',     requiredPolicy: CmsKitPolicyNames.Tags },
          ]);
        }),
      ]);
    }
    
    export function provideCmsKitProAdminRoutesLight(): EnvironmentProviders {
      return makeEnvironmentProviders([
        provideAppInitializer(() => {
          const routes = inject(RoutesService);
          routes.add([
            { path: '/cms',                name: CmsKitProRouteNames.Cms,                                                       order: 100, layout: eLayoutType.application, iconClass: 'fa fa-newspaper',     requiredPolicy: CmsKitProPolicyNames.Cms },
            { path: '/cms/faqs',           name: CmsKitProRouteNames.Faqs,           parentName: CmsKitProRouteNames.Cms, order: 4,  layout: eLayoutType.application, iconClass: 'fa fa-question-circle', requiredPolicy: CmsKitProPolicyNames.Faqs },
            { path: '/cms/newsletters',    name: CmsKitProRouteNames.Newsletters,    parentName: CmsKitProRouteNames.Cms, order: 7,  layout: eLayoutType.application, iconClass: 'fa fa-newspaper',     requiredPolicy: CmsKitProPolicyNames.Newsletters },
            { path: '/cms/page-feedbacks', name: CmsKitProRouteNames.PageFeedbacks,  parentName: CmsKitProRouteNames.Cms, order: 8,  layout: eLayoutType.application, iconClass: 'fa fa-comment',       requiredPolicy: CmsKitProPolicyNames.PageFeedbacks },
            { path: '/cms/polls',          name: CmsKitProRouteNames.Polls,          parentName: CmsKitProRouteNames.Cms, order: 10, layout: eLayoutType.application, iconClass: 'fa fa-poll',          requiredPolicy: CmsKitProPolicyNames.Polls },
            { path: '/cms/url-shorting',   name: CmsKitProRouteNames.UrlShorting,    parentName: CmsKitProRouteNames.Cms, order: 12, layout: eLayoutType.application, iconClass: 'fa fa-forward',       requiredPolicy: CmsKitProPolicyNames.UrlShorting },
          ]);
        }),
      ]);
    }
    

    A few things to keep in mind when wiring this up:

    • Do not import anything from @abp/ng.cms-kit/admin/config or @volo/abp.ng.cms-kit-pro/admin/config in this file (or anywhere else loaded eagerly). The moment you import any symbol from those entries — even the enum — the whole fesm comes in and brings CmsSettingsComponent (and the heavy admin entry) with it.
    • Only the Pro provider above registers the /cms parent menu node (the OSS provider only registers child items hanging off parentName: 'CmsKit::Menu:CMS'). If you ever stop calling provideCmsKitProAdminRoutesLight(), add an equivalent top-level entry yourself, otherwise the OSS children won't have a parent to attach to. Both Cms names resolve to the same key, so the two route sets render as a single "CMS" group when both are registered.
    • You lose the "Settings → CMS Kit" tab in both modules. The OSS tab renders the Require comment approval toggle; the Pro tab renders Contact settings and Page Feedback settings. The easiest replacement is to expose the switches you actually use from your own settings page, or call the underlying APIs (CommentAdminService.updateSettings from @abp/ng.cms-kit/proxy, ContactSettingService.update and PageFeedbackAdminService.updateSettings from @volo/abp.ng.cms-kit-pro/proxy) directly.
    • Keep your existing loadChildren for @abp/ng.cms-kit/admin / @volo/abp.ng.cms-kit-pro/admin — those still serve the actual pages lazily.

    In a stock 10.4.1 + Angular 21.2 + @angular/build project this brings the production initial bundle from 3.13 MB / 738 kB transfer down to 1.84 MB / 424 kB transfer (−1.29 MB / −42%), with the 940 kB toastui chunk moving from initial to lazy.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    Sergei.Gorlovetsky created

    thanks! We'll try that

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    ok

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.8.0-preview. Updated on September 28, 2026, 11:44
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.