Member Junction
    Preparing search index...

    Modern form field component that renders entity fields in both read-only and edit modes.

    Read mode: Clean text display with optional hyperlinks (email, URL, record). Edit mode: Native HTML inputs with custom-built select and autocomplete components.

    Uses:

    • Native <input> for text, number, date
    • Custom styled dropdown for value list selections
    • Custom autocomplete with floating dropdown for foreign key search
    • Custom-styled <input type="checkbox"> for booleans

    All navigation (clicking FK links, email links, URL links) is emitted as events.

    <mj-form-field
    [Record]="record"
    [EditMode]="editMode"
    FieldName="CustomerName"
    Type="textbox"
    [FormContext]="formContext"
    (Navigate)="onNavigate($event)"
    (ValueChange)="onValueChange($event)">
    </mj-form-field>

    Hierarchy (View Summary)

    Implements

    • OnChanges
    • OnDestroy
    Index

    Constructors

    Properties

    Accessors

    Methods

    Constructors

    Properties

    AllowFKCreate: boolean = true

    Whether to offer inline "create new" for the related record when the one you need isn't found. The affordance only renders when this is true AND the current user can create records of the related entity (entity allows creation + role permits it).

    DisplayNameOverride: string = ''

    Override display name (defaults to entity field metadata DisplayName)

    DropdownLeft: number = 0
    DropdownMaxWidth: number = 0
    DropdownMinWidth: number = 0

    Computed: dropdown is at least the trigger width, at most DropdownMaxWidth.

    DropdownTop: number = 0

    Calculated position for fixed-position dropdowns

    EditMode: boolean = false

    Whether the form is in edit mode

    FieldName: string = ''

    The field code name (must match a field on the entity)

    FKActiveIndex: number = -1

    Index of the keyboard-highlighted active suggestion (-1 = none).

    FKColumnFields: string[] = []

    Field code names for the extra columns, index-aligned with FKColumnHeaders.

    FKColumnHeaders: string[] = []

    Headers for the extra (DefaultInView) columns shown in the dropdown.

    FKCreatePresentation: "dialog" | "slide-in" = 'dialog'

    Surface used for the inline create form: a modal dialog (default) or a slide-in.

    FKDropdownMaxWidth: number | null = null

    Max width (px) of the FK suggestion dropdown. The panel grows to fit its columns up to this cap (then scrolls horizontally). When null, defaults to 2× the trigger width so multi-column rows aren't cramped by a narrow field. Always additionally bounded by the viewport's right edge.

    FKFocused: boolean = false

    Whether the FK input is focused — the scope pill only appears while focused.

    FKHasIconColumn: boolean = false

    Whether rows render a leading icon column (drives the header's empty icon cell).

    FKHighlightMatches: boolean = true

    Whether to highlight the typed query substring within each suggestion's name in the FK dropdown. On by default — uses the muted, theme-aware --mj-status-warning highlight (not the old bright yellow). Set false for plain, un-marked names.

    FKIsMatched: boolean = false

    Whether the current FK value is a confirmed match (user selected from dropdown)

    FKLoading: boolean = false

    Whether a DB search is currently in flight (drives the loading indicator).

    FKNameField: string = ''

    Code name of the name column (used as its sort key).

    FKNoMatches: boolean = false

    True when a search completed with zero results (drives the "No matches" row).

    FKSearchableFields: FKSearchableField[] = []

    Fields the user can scope the search to (name + shown columns + other fields).

    FKSearchField: string = ''

    Related-entity field code name currently searched against (defaults to the name field).

    FKSearchFieldLabel: string = ''

    Friendly label of FKSearchField, shown on the scope pill.

    FKSelectedSuggestion: FKSuggestion | null = null

    The currently-linked record, pinned as a sticky "Currently selected" section at the top of the dropdown (browse mode only). Null when there's no selection or the user is actively filtering. Removed from FKSuggestions so it isn't listed twice.

    FKShowScopeMenu: boolean = false

    Whether the scope-picker menu is open.

    FKSortDir: "asc" | "desc" = 'asc'

    Active sort direction; only meaningful when FKSortField is set.

    FKSortField: string | null = null

    Active sort column (field code name), or null for natural (unsorted) order.

    FKSuggestions: FKSuggestion[] = []

    Suggestions returned from the FK entity search

    FormContext?: FormContext

    Form-level context (search filter, showEmptyFields)

    HideWhenEmptyInReadOnlyMode: boolean = true

    Whether to hide this field when empty in read-only mode. Default: true

    LinkType: "Email" | "URL" | "Record" | "None" = 'None'

    Link type for read-only rendering.

    • 'Email': renders as mailto link
    • 'URL': renders as external hyperlink
    • 'Record': renders as FK link to related entity record
    • 'None': renders as plain text
    Navigate: EventEmitter<FormNavigationEvent> = ...

    Emitted when a navigation action is triggered (FK link click, email click, etc.)

    OpenAbove: boolean = false
    PossibleValuesOverride: string[] | null = null

    Override possible values for select/autocomplete

    Provider: IMetadataProvider | null

    If specified, this provider will be used for communication and for all metadata purposes. By default, if not provided, the Metadata and RunView classes are used for this and the default GraphQLDataProvider is used which is connected to the same back-end MJAPI instance as the Metadata and RunView classes. If you want to have this component connect to a different MJAPI back-end, create an instance of a ProviderBase sub-class like GraphQLDataProvider/etc, and configure it as appropriate to connect to the MJAPI back-end you want to use, and then pass it in here.

    Record: BaseEntity

    The entity record containing this field

    ShowFKDropdown: boolean = false

    Whether the FK dropdown is visible

    ShowLabel: boolean = true

    Whether to show the field label

    ShowSelectDropdown: boolean = false

    Whether the custom select dropdown is visible

    ShowValueListDropdown: boolean = false

    Whether the value list dropdown is visible

    ValueChange: EventEmitter<
        { FieldName: string; NewValue: unknown; OldValue: unknown },
    > = ...

    Emitted when the field value changes

    "ɵdir": unknown
    "ɵfac": ɵɵFactoryDeclaration<BaseAngularComponent, never>

    Accessors

    • get CanCreateFK(): boolean

      Whether to show the inline-create affordance: the field opts in (AllowFKCreate), the related entity resolves, and the current user can create records of it (entity allows creation + role permits — GetUserPermisions().CanCreate folds in AllowCreateAPI).

      Returns boolean

    • get ExtendedDisplay(): "image" | "color" | "json" | null

      Runtime display kind from EntityField.ExtendedType. Honored ahead of the generated LinkType input so PhotoURL forms still tagged LinkType=URL render as images once metadata says Image.

      Returns "image" | "color" | "json" | null

    • get FKDisplayName(): string | null

      For FK fields, returns the human-readable name to display instead of the raw ID.

      Resolution order:

      1. If RelatedEntityNameFieldMap exists, read the name from the joined field on the record (sync).
      2. Otherwise, fall back to an async GetEntityRecordName lookup (result cached in _resolvedFKName).

      Returns string | null

    • get FKGridTemplateColumns(): string

      CSS grid-template-columns for the dropdown grid: optional icon column, then one track per ordered column. The LAST column grows to fill any remaining width (1fr) so the grid never leaves dead space on the right; earlier columns size to content. A user-resized column uses its saved pixel width as the floor.

      Returns string

    • get FKHasLinkedValue(): boolean

      True when this FK currently points at a real record (has a value AND we can show its name). Drives the "linked" affordance — the chain icon + accent — so the user can tell at a glance this is a resolved link, not free text.

      Returns boolean

    • get IsFieldReadableByUser(): boolean

      Whether field-level security permits the current user to READ this field.

      The template gates the whole field on this BEFORE anything reads a value, because BaseEntity.Get() throws for a denied field — so rendering one at all would take out the entire form rather than hiding a column. Everything downstream (Value, ShouldHideField, FK resolution) is safe only because this check runs first.

      Memoized: template getters are evaluated on every change-detection cycle, and GetDeniedReadFields walks every field on the entity and aggregates permissions across the user's roles. ngOnChanges clears the memo when Record or FieldName changes.

      Fails OPEN — on a missing entity, missing user, or an entity with field security switched off — matching BaseEntity's own gate. A form that hid fields because no user had resolved yet would be worse than one that shows them, since the server is the real boundary.

      Returns boolean

    • get IsFieldWritableByUser(): boolean

      Whether field-level security permits the current user to WRITE this field, against the verb that actually applies to the record in hand: Create on a new record, Update on a saved one.

      Read access is handled separately and earlier (IsFieldReadableByUser) — a field the user cannot see is not rendered at all. This is the narrower case of a field they CAN see but may not change, which should render as a read-only value rather than an editable control.

      Both verbs matter and they fail differently, which is why this picks by record state rather than checking update alone:

      • Update denied — the server rejects the save. Without this, the user types, saves, and gets an error naming a field they had every reason to think was editable.
      • Create denied — the server does NOT reject. It silently drops the value and takes the column default, matching the read path where a denied field is simply absent. So an editable control on a new record quietly discards what was typed, with no error anywhere. That is the worse of the two, and the only signal the user gets is this.

      Memoized alongside the read check and invalidated by the same ngOnChanges, for the same reason: template getters run every change-detection cycle and the underlying walk covers every field on the entity.

      Returns boolean

    • get IsRichTextEligible(): boolean

      Whether this field is large enough to be a candidate for rich-text (markdown/html) auto-detection. Generic & multi-platform: keys off the TS type and character length rather than a specific SQL type, so nvarchar(max)/ntext (MaxLength === 0 means unlimited) and longer fixed fields (nvarchar(400)/varchar(500)/...) all qualify, while short strings, UUIDs, numbers, dates, etc. do not.

      Returns boolean

    • get ProviderToUse(): IMetadataProvider

      Returns either the default Metadata provider or the one specified in the Provider property, if it was specified

      Returns IMetadataProvider

    • get RunQueryToUse(): IRunQueryProvider

      Returns either the default RunQuery provider or the one specified in the Provider property, if it was specified

      Returns IRunQueryProvider

    • get RunViewToUse(): IRunViewProvider

      Returns either the default RunView provider or the one specified in the Provider property, if it was specified

      Returns IRunViewProvider

    • get StoredDateIsUnreadable(): boolean

      The field holds SOMETHING, and the date editor cannot show it.

      WHY A SEPARATE SIGNAL IS THE ONLY OPTION. DateInputValue returns '' for a value it cannot render, and an <input type="date"> renders '' as an empty box — the same empty box it shows for a field that was never set. There is no string that makes the element display not-a-date, so the control physically cannot surface the problem itself. The most the getter can do is refuse to emit garbage; saying WHY has to happen beside it.

      The consequence of not saying it is not cosmetic. The field reads as "no date", and the next save writes that emptiness over whatever was actually stored — so an unreadable value becomes a destroyed one, silently, by a user who was never told anything was wrong.

      READ MODE ALREADY DOES THIS and needs nothing: FormatValue() shows Invalid Date for a bad Date and the raw text for a bad string. It is only the EDITOR that hides the problem, which is why this is scoped to EditMode rather than to the field.

      An ABSENT value is not unreadable. Empty means empty, and must never be decorated as a fault.

      Returns boolean

    • get Type(): | "number"
      | "code"
      | "textbox"
      | "textarea"
      | "datepicker"
      | "checkbox"
      | "select"
      | "autocomplete"

      Returns
          | "number"
          | "code"
          | "textbox"
          | "textarea"
          | "datepicker"
          | "checkbox"
          | "select"
          | "autocomplete"

    • set Type(
          value:
              | "number"
              | "code"
              | "textbox"
              | "textarea"
              | "datepicker"
              | "checkbox"
              | "select"
              | "autocomplete"
              | "dropdownlist"
              | "numerictextbox",
      ): void

      The control type to render in edit mode. Determines which input component is used.

      Also accepts deprecated Kendo-style type names for backward compatibility:

      • 'dropdownlist''select'
      • 'numerictextbox''number'

      Parameters

      • value:
            | "number"
            | "code"
            | "textbox"
            | "textarea"
            | "datepicker"
            | "checkbox"
            | "select"
            | "autocomplete"
            | "dropdownlist"
            | "numerictextbox"

      Returns void

    Methods

    • Move a column one slot up (delta -1) or down (delta +1) and persist. mousedown + stopPropagation so the row's select-scope handler doesn't fire and the menu stays open.

      Parameters

      • event: MouseEvent
      • field: string
      • delta: -1 | 1

      Returns void

    • A callback method that is invoked immediately after the default change detector has checked data-bound properties if at least one has changed, and before the view and content children are checked.

      Parameters

      • changes: { [propName: string]: SimpleChange<any> }

        The changed properties.

      Returns void

    • Ask the host (app layer) to create a new related record, prefilled with the typed text as the name. We only EMIT the request — opening the form is the app's job (the generic forms layer must not depend on the app-level form presenter). The host calls Complete(record) when saved and we select it.

      Parameters

      • event: MouseEvent

      Returns void

    • Show dropdown on focus. With a cached-unfiltered entity we immediately show the first N rows (sorted by name) even on empty input — zero DB round-trips. Without a cache we keep prior behavior: only re-open if we already have suggestions.

      Parameters

      • event: FocusEvent

      Returns void