skip to content

In a typed Angular FormGroup for user settings, why is form.value typed as Partial, and when should you call getRawValue() instead?

level: middleimportance: should knowfreq 52%

answer

  1. what disabled controls do to value
  2. every key might be undefined
  3. a disabled field still has data
  4. value vs getRawValue()

basics

~10 s

An Angular FormGroup leaves disabled children out of its value, so every key is typed possibly undefined. getRawValue() includes disabled controls and returns the full, non-Partial type, which suits saving a whole settings record.

solid answer

~40 s

In Angular's strictly typed reactive forms, a `FormGroup`'s `value` omits any child control that is disabled, unless the whole group is disabled. Because any child can be disabled at runtime, the type of `form.value` is `Partial<{...}>`, so `form.value.email` is `string | undefined` even when the control is `FormControl<string>`. `getRawValue()` returns every child's value regardless of disabled state, typed without the `Partial`. So: use `value` when you deliberately want only what the user can edit, and `getRawValue()` when you need the complete record, such as saving a settings form whose email field is shown disabled. An optional key declared in the group's interface, like `bio?: FormControl<string>`, stays optional even in `getRawValue()`, because the control itself may be absent.

code

ts · 12 lines
ts
import {FormControl, FormGroup} from '@angular/forms';

const settings = new FormGroup({
  displayName: new FormControl('Ada', {nonNullable: true}),
  email: new FormControl({value: '[email protected]', disabled: true}, {nonNullable: true}),
});

console.log(settings.value);         // {displayName: 'Ada'}
console.log(settings.getRawValue()); // {displayName: 'Ada', email: '[email protected]'}

const name: string | undefined = settings.value.displayName; // Partial
const email: string = settings.getRawValue().email;          // full type

go deeper

for a junior

Remember that disabled controls drop out of a FormGroup's value and that getRawValue() brings them back.

for a middle

Explain how the enabled-or-group-disabled rule produces the Partial type, and how getRawValue() differs for groups, arrays and single controls.

for a senior

Pick value or getRawValue() per endpoint contract, model sometimes-present fields as optional interface keys, and reject non-null assertions that hide the disabled case.

for a principal

Decide whether the form's shape or the API contract is the source of truth, and keep payload mapping explicit so disabling a field can never silently change what is saved.

## The rule behind the type In Angular's reactive forms, a `FormGroup` computes its `value` by walking its children. A child contributes its value only if it is **enabled**, or if the group itself is disabled. A disabled child is simply left out of the object. Strict typing (since **Angular 14**) has to describe every possible runtime shape. Since any child can be disabled at any time, no key is guaranteed to be present. So the value type of a group is wrapped in `Partial`: ```ts import {FormControl, FormGroup} from '@angular/forms'; interface SettingsForm { displayName: FormControl<string>; email: FormControl<string>; bio?: FormControl<string>; // optional: only for public profiles } const settings = new FormGroup<SettingsForm>({ displayName: new FormControl('Ada', {nonNullable: true}), email: new FormControl({value: '[email protected]', disabled: true}, {nonNullable: true}), }); settings.value; // Partial<{displayName: string; email: string; bio: string}> settings.getRawValue(); // {displayName: string; email: string; bio?: string} ``` That is why `settings.value.displayName` has type `string | undefined` even though the control is `FormControl<string>`. ## `value` versus `getRawValue()` | | `value` | `getRawValue()` | |---|---|---| | Disabled children | Omitted (unless the group is disabled) | Included | | Type | `Partial<...>` of the child values | Declared keys without the `Partial` | | Optional interface keys | Optional | Still optional | | Nested groups | Nested `Partial` values | Nested raw values, recursively | | Typical use | Send only editable fields | Save or diff the whole record | Note the asymmetry in the API: `value` is a property that Angular keeps current as controls change, while `getRawValue()` is a method you call when you need the full picture. ## Choosing in the settings form The reserved scenario here is a user-settings form. The email is displayed but locked (disabled) because it is changed through a separate verification flow, and a `bio` field exists only for public profiles. - If the save endpoint expects the **complete** settings object, call `getRawValue()`. Using `value` would drop `email` silently and the type would push you to invent a fallback. - If the endpoint is a **patch** of editable fields only, `value` is the honest choice: the omission of `email` is exactly what you want. - The optional `bio` key remains optional in **both**, because it describes a control that might not exist in the group at all. `getRawValue()` removes `Partial` from declared-required keys; it does not invent controls. ## Where the `Partial` does not appear A few related types behave differently, which interviewers use as follow-ups: 1. A `FormControl`'s own `getRawValue()` is the same as its `value`; only containers distinguish them. 2. A `FormArray<T>`'s `value` is typed as a plain array, not `Partial`, but at runtime disabled elements are still filtered out, so indices in `value` can shift. `getRawValue()` keeps every element. 3. When the **whole group** is disabled, `value` includes every child, but the type stays `Partial` because the compiler cannot know the group's state. ## Common mistakes - Reaching for `form.value.email!` to silence the compiler. The non-null assertion hides a real runtime case: the field is disabled and absent. - Assuming a disabled control is invalid or cleared. It keeps its value; it just stops contributing to `value` and to the group's validity. - Declaring the group interface without `?` for a field that is only sometimes present. Then `getRawValue()` claims the key always exists, and code that reads it trusts a value that may be missing. - Copying `form.value` into a model type that has required fields and fixing the resulting compile error with a cast instead of switching to `getRawValue()`. ## How nested groups compose The rule applies recursively. A settings form often nests a group, for example `privacy: FormGroup<{showEmail: FormControl<boolean>; showBio: FormControl<boolean>}>`. In the parent's `value`, `privacy` is itself optional, and inside it each key is optional again, so reading `settings.value.privacy?.showEmail` needs two optional steps. `getRawValue()` walks the same tree and returns the raw value of every nested container, so `settings.getRawValue().privacy.showEmail` is a plain `boolean`. Interviewers like this because it shows whether a candidate understands that `Partial` is not a one-level cosmetic wrapper but a faithful description of which subtrees can be switched off at runtime. ## Summary `Partial` is the type system describing a runtime rule: disabled children vanish from `value`. `getRawValue()` is the opt-out that includes them, and an optional key in your interface is the separate, deliberate way to say a control may not exist.

  • If the whole FormGroup is disabled, what does its value contain?
    Every child's value. The group includes a child when the child is enabled or when the group itself is disabled, so disabling the parent brings all children back into `value`. The type is still `Partial`, because the compiler cannot know the group's runtime state.
  • Is a FormArray's value typed as Partial too?
    No. A typed `FormArray<T>`'s `value` is a plain array of element values. At runtime, though, disabled elements are still filtered out unless the array itself is disabled, so indices in `value` can differ from indices in `controls`. `getRawValue()` keeps every element in place.

Think of value as a team photo taken only of people currently on shift: anyone off duty (disabled) is missing from the picture, so no seat is guaranteed. getRawValue() is the full staff list, which names everyone on the roster whether or not they are working today.

saying these in an interview costs you the question

  • Partial means the group was built with some controls missing.
  • A disabled control's value is reset to null, which is why it is optional.
  • getRawValue() makes optional interface keys required as well.
  • form.value.email! is a safe fix because the field always exists.
  • value and getRawValue() differ only in type, not in content.