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

@n0n3br/ngx-form-dependency-engine

v0.0.5

Published

Reactive dependency engine for Angular FormGroup: declare when/then rules that show, hide, enable, disable, validate and populate controls.

Readme

@n0n3br/ngx-form-dependency-engine

npm version License: MIT

Reactive dependency engine for Angular FormGroup: declare when → effect rules between controls — "when these conditions hold against the form's value, apply these effects to a target control."

No visual builder, no schema framework. One engine class, pure functions, real Angular signals.

Features

  • 15 condition operators (equals, in, contains, matches, greaterThan, isEmpty, custom predicates…)
  • AND / OR groups with arbitrary nesting
  • Effects: show · hide · enable · disable · setRequired · unsetRequired · addValidators / removeValidators (namespaced) · setValue · setOptions
  • Per-dependency and per-key validator namespacing — strictly additive over baseline validators
  • Chained rules propagate inside one pass; genuine cycles are capped (maxIterations) and warned, never hang
  • Emit-free internal writes (emitEvent: false) — no re-entrancy surprises
  • hiddenFields / fieldOptions exposed as real signals; zoneless-ready

Requires Angular ≥ 22 with Reactive Forms.

Install

npm install @n0n3br/ngx-form-dependency-engine

Quick start

import { Component, DestroyRef, inject } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import {
  FormDependencyEngine,
  dep,
  f,
  allOf,
  show,
  hide,
  setRequired,
} from '@n0n3br/ngx-form-dependency-engine';

@Component({/* ... */})
export class QuoteComponent {
  readonly form = new FormGroup({
    kind: new FormControl<'PF' | 'PJ'>('PF'),
    companyDoc: new FormControl(''),
    personDoc: new FormControl(''),
    income: new FormControl(0),
  });

  readonly engine = new FormDependencyEngine(this.form, [
    dep('doc-type', 'companyDoc')
      .when(f('kind').equals('PJ'))
      .then(show(), setRequired())
      .otherwise(hide())
      .build(),
    dep('premium', 'bonus')
      .when(allOf(f('kind').equals('PF'), f('income').greaterThan(5000)))
      .then(show())
      .build(),
  ]);

  constructor() {
    inject(DestroyRef).onDestroy(() => this.engine.destroy());
    this.engine.activate(); // runs an initial pass, then follows form.valueChanges
  }
}
@if (!engine.isHidden('companyDoc')) {
<input [formControl]="form.controls.companyDoc" />
}

Directive usage

Prefer configuring rules straight from the template? Attach fdeDependencies to the <form> element. The directive owns the engine lifecycle (build, activate, rebuild on input change, destroy) and is exported as fde so templates read state directly.

import { Component } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import {
  Dependency,
  dep,
  f,
  hide,
  setOptions,
  setRequired,
  show,
  unsetRequired,
} from '@n0n3br/ngx-form-dependency-engine';

@Component({/* ... */})
export class SignupComponent {
  readonly form = new FormGroup({
    accountType: new FormControl('personal'),
    companyName: new FormControl(''),
    plan: new FormControl('Starter'),
  });

  readonly deps: Dependency[] = [
    dep('business-company', 'companyName')
      .when(f('accountType').equals('business'))
      .then(show(), setRequired())
      .otherwise(hide(), unsetRequired())
      .build(),
    dep('plan-options', 'plan')
      .when(f('accountType').equals('business'))
      .then(setOptions(['Team', 'Enterprise']))
      .build(),
  ];
}
<form [formGroup]="form" [fdeDependencies]="deps" #fde="fde">
  @if (!fde.isHidden('companyName')) {
  <input formControlName="companyName" />
  }

  <select formControlName="plan">
    @for (plan of fde.optionsFor('plan') ?? []; track plan) {
    <option [value]="plan">{{ plan }}</option>
    }
  </select>
</form>

Directive API:

| Member | Description | | ------------------ | ---------------------------------------------------------------------------------------------------------- | | fdeDependencies | Dependency[] input; pass null to run with no rules. Swapping the array at runtime rebuilds the engine. | | fdeConfig | Optional FormDependencyEngineConfig (e.g. maxIterations). | | engine | Readonly signal with the live FormDependencyEngine instance (or null before init). | | isHidden(path) | Null-safe visibility read for templates. | | optionsFor(path) | Null-safe option list read. | | reevaluate() | Force an evaluation pass. |

Works with [formGroup] (reactive) and template-driven forms — it resolves whatever ControlContainer hosts the element. Reactive forms are recommended since the engine maps controls at construction time.

Core API

new FormDependencyEngine(form, dependencies, config?)

| Member | Description | | ------------------------------------------------------ | -------------------------------------------------------------------------------------- | | activate(): Subscription | Runs the first evaluation pass, then re-evaluates on every form.valueChanges. | | destroy(): void | Unsubscribes. | | reevaluate(): void | Forces a manual evaluation pass. | | isHidden(field): boolean | Reads the hiddenFields signal set. | | optionsFor(field): unknown[] \| undefined | Reads the fieldOptions signal map. | | hiddenFields: Signal<ReadonlySet<string>> | Fields currently hidden by hide effects. | | fieldOptions: Signal<ReadonlyMap<string, unknown[]>> | Option lists written by setOptions. | | settled: Signal<number> | Bumps after every evaluation pass — use it in computed()s to track emit-free writes. |

config: { maxIterations?: number } — how many internal passes a single evaluation may take while still changing state (default 5). Hitting the cap logs a circular-dependency warning.

Conditions

interface Condition {
  field: string; // dot-path into the form value, e.g. "endereco.cidade"
  operator: ConditionOperator;
  value?: unknown;
  custom?: (formValue: any) => boolean; // wins over operator entirely
}

Operators: equals · notEquals · in · notIn · contains · notContains · greaterThan · greaterOrEqual · lessThan · lessOrEqual · isEmpty · isNotEmpty · truthy · falsy · matches.

Groups combine with { logic: 'AND' | 'OR', conditions: [...] }; empty AND is vacuously true.

Effects

Every effect accepts an optional target override (defaults to the owning dependency's target).

| Effect | Extra payload | Behavior | | ------------------------------------ | --------------------------------------------- | ---------------------------------------------------------------------------------------------- | | show / hide | — | Toggles membership in hiddenFields. Never clears values. | | enable / disable | — | Native control enable/disable; guarded no-op if already in that state. | | setRequired / unsetRequired | — | Namespaced by the dependency id — two dependencies can require the same field independently. | | addValidators / removeValidators | key, validators? | Arbitrary validator sets under your own stable key. | | setValue | value literal or (formValue) => value | Writes with emitEvent: false. | | setOptions | options literal or (formValue) => options | Writes into the fieldOptions signal only. |

Validator management is strictly additive: validators present on a control before the engine ever touched it are preserved through every attach/detach cycle.

Evaluation loop

Because internal writes do not emit events, the engine loops internally: evaluate all dependencies → apply effects → compare state by content (never by reference) → repeat until nothing changes or maxIterations is reached. A chain A→B→C fully propagates within a single pass series; a true cycle caps out and warns.

Fluent rule builder

The builder layer produces plain, frozen contract objects — identical to hand-written literals.

import { allOf, anyOf, dep, f, not } from '@n0n3br/ngx-form-dependency-engine';
import { Validators } from '@angular/forms';

const dependency = dep('show-guarantee', 'guarantee')
  .when(
    allOf(f('amount').greaterThan(50000), anyOf(f('profile').equals('gold'), f('vip').truthy())),
  )
  .then(show(), setRequired(), addValidators('min-len', [Validators.minLength(3)]))
  .otherwise(hide(), unsetRequired())
  .build();
  • f(path) — every operator is a method: .equals(), .in([...]), .isEmpty(), .matches(/re/)
  • .and() / .or() — chain groups: f('a').equals(1).and(f('b').equals(2)).or(f('c').equals(3))
  • allOf(...) / anyOf(...) / not(condition) — combinators
  • Effect factories: show(target?), hide(), enable(), disable(), setRequired(), unsetRequired(), addValidators(key, validators, target?), removeValidators(key, target?), setValue(valueOrFn, target?), setOptions(optionsOrFn, target?)
  • dep(id, target).when(...).then(...).otherwise(...).build() — throws without when, freezes its output

Demo application

This repository ships an interactive showcase covering every feature:

git clone https://github.com/rogeriolaa/ngx-form-dependency-engine
cd ngx-form-dependency-engine
npm install
npm start          # serves the demo at http://localhost:4200

Sections: Playground · Cascade · Validators Lab · Chains & Cycles · State Inspector · Builder Playground.

Development

| Command | Description | | ----------------------- | --------------------------------------------------------------- | | npm run build:lib | Build the publishable library to dist/. | | npm test | Run the library unit tests (Vitest). | | npm run test:coverage | Unit tests with a V8 coverage report (kept at ~99% statements). | | ng test demo | Run demo tests. | | npm run lint | ESLint across workspace. | | npm run build:demo | Production build of the demo (Pages base href). |

License

MIT © rogeriolaa