npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@smartbit4all/ng-client

v7.3.5

Published

Angular client library for the smartbit4all platform — a server-driven UI framework that renders views, pages, forms, grids, trees and dialogs from backend (ViewApi/PageApi) definitions. Provides reactive view-context state management, dynamic component l

Readme

@smartbit4all/ng-client

The Angular client of the smartbit4all platform: a backend-driven UI layer where the server owns the view lifecycle and the client renders it. Screen components extend SmartComponent, talk to the platform's BFF (ViewApi / PageApi), and the widgets inside them — grid, tree, form, filter, map, diagram, toolbars — reload themselves when the backend says their part of the model changed.

Requires Angular 22 and Material 22. Everything is standalone; there are no NgModules.

  • Upgrading from 6.x? MIGRATION-7.0.md, shipped next to this file, is the whole change list, and there is a codemod for the mechanical part.
  • Writing your own widget? WIDGETS.md, also next to this file.

Installation

npm i @smartbit4all/ng-client
npm i @angular/material-date-fns-adapter@^22.0.0 date-fns@^4

Wiring it up

One call provides the whole library. It is deliberately not optional per module: the session and header interceptors used to be easy to leave out, and leaving them out failed silently — with no Authorization header.

bootstrapApplication(AppComponent, {
  providers: [
    provideAnimations(),
    provideRouter(ROUTES),

    provideSmartNgClient(
      {
        gridMenuIcon: 'more_horiz',
        aclEditingViewName: Pages.ACL_MATRIX_PAGE,
        namedValidators: [MY_VALIDATOR_FACTORY],
      },
      withSmartMap({ engine: MapEngine.LEAFLET }),   // optional: pulls in a map engine
      withSmartDiagram({ customOptions: [MY_CHART] }) // optional: pulls in chart.js
    ),

    { provide: HTTP_INTERCEPTORS, useClass: MyLoadingInterceptor, multi: true },
  ],
});

SmartNgClientConfig is the entire configuration surface — see its doc comments for the fields. Only the map and the diagram are opt-in, because they are the only features that pull heavy third-party code.

Components are standalone: import the ones your templates use in the component that uses them.

@Component({
  selector: 'app-my-page',
  templateUrl: './my-page.component.html',
  imports: [SmartGridComponent, UiActionToolbarComponent, SmartEmbeddedSlotDirective],
})
export class MyPageComponent extends SmartComponent<MyModel> { … }

Session and view context

SmartBackendBootstrapService owns the startup sequence. Configure it once, then choose one entry point — start() for a normal web app, bootstrapWith(creds) when a native shell hands the session over. They are mutually exclusive; reset() switches.

@Injectable({ providedIn: 'root' })
export class AppAuthBootstrap {
  constructor(
    private bootstrap: SmartBackendBootstrapService,
    defaultErrorUi: SmartDefaultErrorUiService
  ) {
    this.bootstrap.configure({
      url: 'https://my-host/api',
      cookieName: 'my-app',
      viewHandlers: HANDLERS,
      actionDescriptors: ACTION_DESCRIPTORS,
      ...defaultErrorUi.hooks({ language: 'hu' }),
    });
    this.bootstrap.start();
  }
}
providers: [provideAppInitializer(() => { inject(AppAuthBootstrap); })]

Every screen component awaits bootstrap.whenReady() before its first BFF call — SmartComponentApiClient.run() does it for you. The config hooks (onStartError, onSessionError, onViewContextLost, onSmartLink) are where host-specific behaviour goes; the error interceptor routes backend session errors into them.

View handlers

The backend names the view; the host says what renders it.

export const HANDLERS: SmartViewHandlerModel[] = [
  { name: Pages.ANY_PAGE, route: 'any' },                        // ViewType.NORMAL
  { name: Pages.ANY_DIALOG, component: AnyDialogComponent },     // ViewType.DIALOG
  { name: Pages.ANY_COMPONENT, route: 'any', component: AnyComponent },
];

A view the host did not register still renders, through the library's default components; add your own with defaultViewComponents.

Smartlinks

Route /redirect/:channel/:uuid to SmartViewRedirect (or to your own component extending it) and handle the hand-off in configure({ onSmartLink }).

const routes: Routes = [
  {
    path: `redirect/:${SmartLinkChannelVariableInPath}/:${SmartLinkUuidVariableInPath}`,
    component: SmartViewRedirect,
  },
];

UiActions

The backend sends the actions; the host supplies their descriptors — caption, icon, button type, confirm/input dialog, feedback:

this.viewContext.setActionDescriptors(ACTION_DESCRIPTORS);
this.viewContext.commonFeedbackText = 'Done.';

A toolbar renders the actions addressed to its id (uiAction.toolbar == id), pulling them from the screen component above it; an explicit [uiActionModels] binding wins. Without an id and without a binding it shows nothing.

<smart-ui-action-toolbar [id]="'myToolbar'"></smart-ui-action-toolbar>
<smart-ui-action-toolbar [uiActionModels]="myActions" [executor]="myService"></smart-ui-action-toolbar>

[executor] defaults to the screen component the toolbar sits under. Bind it only when the actions belong to another API — a tree service, a dialog service of your own — and implement UiActionExecutor there:

export class MyDialogService implements UiActionExecutor {
  submitForm(validate: boolean): void { … }
  getInvalidFields(): SmartFormInvalidFields { … }
  getAdditionalParams(uiAction: UiAction): UiActionAdditionalParams { … }
  getModel(): any { … }
  performUiActionRequest(request: UiActionRequest): Promise<any> { … }
  handleSpecificDemandsAsynchronously(…): Promise<UiActionSpecificDemandResponse> { … }
}

A UiActionModel is frozen: build an entry, never edit one. To change how an action looks, rebuild the array — actions.map(a => a.uiAction.code === c ? { ...a, cssClass: 'x' } : a) — because it is the array reference changing that re-renders the toolbar.

Keybindings

The server binds key chords (Mod+Shift+S, Mod = Cmd/Ctrl) to a view's actions, from a keymap file and from code; the client fires them in the focused view and shows them in the button's tooltip. Nothing to wire on the client. The keymap format, the chord grammar and the rules for which view gets a key: MIGRATION-7.0.md, New in 7.3.2: keybindings.

Change detection

7.0 runs without zone.js and every library component is OnPush. Nothing forces your host to drop zone.js — the library works either way — but if you do, the same rule applies to your own components: state written from a subscription, a promise or a timer needs a signal or a markForCheck(); state written from a template event or an input does not.

Dates

The browser's timezone on screen, Zulu (…Z) on the wire, the server converts. Date widgets hold plain Date values and render yyyy.MM.dd.; the adapter is date-fns. If you provide MAT_DATE_LOCALE, the string 'hu-HU' keeps working — see MIGRATION-7.0.md, Dates.

Dev tool

localStorage.setItem('smartDevToolActive', 'true');  // active
localStorage.setItem('useDevTool', 'true');          // button visible

Further reading

| File | What it is | |---|---| | MIGRATION-7.0.md | the 6.x → 7.0 change list, and the codemod that does the mechanical part | | WIDGETS.md | the widget-authoring protocol | | MIGRATION-6.0.md, MIGRATION-4.5.md | the previous two majors |

Per-module notes and version logs live next to the sources, in src/lib/<module>/README.md and src/lib/<module>/versionLogs.md.