Skip to main content

ChoiceField

Struct ChoiceField 

Source
pub struct ChoiceField<F = ()>(/* private fields */);
Expand description

A choice field: Field::choice, or a record form’s choice control (F is the form).

Implementations§

Source§

impl<F: EditsRecords> ChoiceField<F>

Source

pub fn hidden_on_create(self) -> Self

Renders nothing for the field on the create form. The create stores the field’s default, else its blank answer: a required field with neither cannot mount. A field hidden on both forms only ever stores that: set it in the record instead.

Source

pub fn hidden_on_edit(self) -> Self

Renders nothing for the field on the edit form, and keeps its stored value whatever an edit posts: the create form sets the value, and no edit changes it, as disabled_on_edit does without showing it.

Source§

impl<F> ChoiceField<F>

Source

pub fn disabled_when( self, watched: &impl Watched<F>, values: impl IntoIterator<Item = impl Into<String>>, ) -> Self

Renders the control disabled while watched posts one of values: an option’s value, or true or false for a toggle. Disabled, the control posts nothing, so an edit keeps the stored value and a create takes the field’s default, else its blank answer.

let status = Field::choice(Order::fields().status()).options(["draft", "sent"]);
let total = Field::text(Order::fields().total()).disabled_when(&status, ["sent"]);
Schema::new((status, total));
  • The browser enables and disables the control as the watched field changes, with no request, through a disabled fieldset around it. An app’s own Control follows it only through the form controls it renders. Without JavaScript, the control starts as the form’s initial values set it, the stored ones on an edit, and stays that way.
  • The panel reads a submission the same way and drops the field’s posted key, and a file field’s carried upload, while the condition holds. The condition reads what the user may post, so it is no access check: ResourceDef::field_access is.
  • Mounting refuses what it refuses for visible_when: an unplaced watched field, a value it never posts, a multiple choice, and a watched field another condition hides while this one still shows. It also refuses a required field with no non-blank default, which a create could not fill while the condition holds, and a condition or a dependent choice watching this field: a disabled control posts nothing, so the browser and the server would read different values.
  • A later call replaces an earlier one.
Source§

impl<F> ChoiceField<F>

Source

pub fn options(self, options: impl IntoOptions) -> Self

Static options: values that are their own label (["draft", "published"]), (value, label) pairs whose label is a string or a Text ([("draft", text!("status-draft"))]), or a #[derive(Options)] enum’s options().

Source

pub fn multiple(self) -> Self

Takes several options: a checkbox per option, posting the field’s key once per checked box. A record form’s many-to-many field (Vec<K> with #[form(relationship = R)]) is one, and so is its Vec with #[form(options = T)], stored in a list column; parse_list reads one in an action’s input.

It offers at most MAX_RELATIONSHIP_OPTIONS records: past them it lists none but the chosen ones, and refuses a submission choosing another. A record form’s edit re-checks only the keys the record does not link yet. searchable filters the listed options as the user types. A condition cannot watch it, it cannot depend on another field, and its default is a list.

Source

pub fn tags(self) -> Self

Takes any values the user types, as tags: a multiple choice whose options, if any, are suggestions.

  • It renders a checkbox per suggestion and per value it holds, checked while it holds it, then a text input adding values, several at once separated by commas.
  • Each value is trimmed, and a blank one or a repeat is dropped, so a value cannot hold a comma. Any other value is accepted: max_items caps how many.
  • Mounting refuses it on a relationship’s choice, whose values name records.

A record form’s Vec<String> marked #[form(tags)] is one.

Source

pub fn radio(self) -> Self

Renders a radio per option rather than a <select>, so every option shows without a click: for a few options, such as a status or a visibility. A multiple choice keeps its checkboxes.

The options load as a select’s do, and past the option cap only the chosen one lists. searchable filters the listed options as the user types. A radio choice cannot depend on another field, since the browser lists its options once, but another choice can depend on it.

Field::choice(lens!(Post.visibility))
    .options(vec![
        ("public".to_string(), "Public".to_string()),
        ("private".to_string(), "Private".to_string()),
    ])
    .radio();
Source

pub fn buttons(self) -> Self

Renders a button per option, pressed while its option is chosen: a compact radio list, or with multiple, a compact checkbox list. It takes the same modifiers and limits as radio.

Field::choice(lens!(Task.priority))
    .options(["low", "normal", "high"])
    .buttons();
Source

pub fn min_items(self, count: usize) -> Self

Refuse a submission choosing fewer than count options. A multiple choice’s blank answer is the empty list, which no other rule refuses, so min_items(1) is how one requires a choice. Only the server checks it. A choice that is not multiple fails the mount, as does a minimum above max_items or above the count of static options.

Source

pub fn max_items(self, count: usize) -> Self

Refuse a submission choosing more than count options; min_items says which choices take it.

Source

pub fn searchable(self) -> Self

Filters options as the user types, fetching from the relationship past the option cap.

Source

pub fn relationship<R>(self) -> Self
where R: OptionSource + 'static,

Loads options from a related source’s tenant-scoped query, each valued by its primary key and labelled by the source’s label, degrading to type-to-search past the option cap.

A resource’s options carry its record_title, the title its detail page shows. relationship_labelled labels one field’s options otherwise.

  • Options come from the related resource’s tenant-scoped query and follow its policy: the list is empty and the field shows “not available” unless the related resource’s policy allows ViewAny, and each record must pass its View.
  • A submitted key must be one of those records, so a hand-crafted POST cannot point at another tenant’s row. The write checks the key again inside its transaction, so a record deleted, moved to another tenant or hidden since the form validated refuses the write with the same field error.
  • At most 200 options load. Past that, a .searchable() choice turns into type-to-search against GET {list_url}/-/options, which searches the related table’s searchable() columns; a non-searchable choice shows an error instead. .searchable() also filters a short list as you type. Without JavaScript the plain select remains.
Source

pub fn creatable(self) -> Self

Offers to create the related record from the form: a link beside the select opens the relationship’s resource’s create page in a dialog, and the record it saves becomes the choice, while the form keeps what was typed in it.

  • The dialog frames the related resource’s own create page, so the record passes that resource’s policy, tenancy, field access, rules and hooks, as a create from its list does. The link renders only while the panel mounts the resource with a create page and its policy allows ViewAny and Create, and not while the choice renders disabled.
  • The saved record becomes the choice only when the related resource’s scoped query holds it, as its options would; otherwise the dialog says it was created and the choice stays as it was. The submission checks the key as for any relationship.
  • The option shows the record’s record_title until the form renders again, even under relationship_labelled.
  • The dialog takes the height of the form, up to the screen’s, and titles it. In the dialog a Section has no panel and does not fold, and a section alone in the form shows no title.
  • Without JavaScript the link opens the create page in a new tab, and the record is offered once the form renders again.
  • Mounting refuses a creatable choice that is not a single select over a resource’s records: a multiple choice, radios or buttons, a dependent or filtered choice, a source that is not a resource, a choice a condition disables, and a choice in a repeater’s rows.
Source

pub fn depends_on<M, T>( self, parent: &impl Watched<F>, column: impl Into<Path<M, T>>, ) -> Self
where M: Model + Send + Sync + 'static, T: FormScalar + IntoExpr<T> + Send + Sync + 'static,

Offers only the related rows whose column column equals the value parent posts: the cities of the chosen country. The browser fetches the options again when parent changes, and a submission must name a row of the posted parent value. A blank parent offers none.

column belongs to the relationship’s source model; mounting refuses another model, a choice without a relationship, and a parent the schema does not place.

  • The form renders the options of the parent’s current value, and none while it is blank. When the parent changes, the browser fetches the new options from GET {list_url}/-/options and keeps the current choice only if they still offer it. A choice they drop changes too, so a choice depending on it, or a condition watching it, follows.
  • A submission must name a record of the parent value it posts, or, on an edit that does not post the parent, of its stored value. The write checks the key again inside its transaction, as for any relationship.
  • The 200-option cap counts the parent’s records. Past it, a .searchable() dependent choice searches among them, and any other offers no option. A failed fetch offers none either.
  • Without JavaScript the options stay those of the value the page rendered with, and a submission naming another value’s record is refused.
  • column must belong to the relationship’s model, and the parent must be placed in the same schema: mounting refuses either mistake. Only a resource’s form serves the options, so mounting also refuses a dependent choice in an action’s input.
  • A multiple choice neither depends on another field nor narrows one: mounting refuses both.
Source

pub fn options_where( self, filter: impl Fn(&Cx) -> Expr<bool> + Send + Sync + 'static, ) -> Self

Offers only the related rows filter selects, among those the source’s scoped query and a depends_on parent offer: the categories without a parent, or the users of one role. filter builds the predicate for each request, so it may read the request’s user or tenant.

Field::choice(lens!(Category.parent_id))
    .relationship::<CategoryResource>()
    .options_where(|_cx| Category::fields().parent_id().is_none());
  • The form, the search past the option cap and a dependent choice’s fetch offer only the rows it selects, and a submission must name one: the server checks the key on submit and again inside the write’s transaction, so a hand-crafted POST cannot name another row.
  • A multiple choice keeps the links it holds to rows the filter drops, since its form never offered them.
  • The predicate must read the relationship’s source model; one over another model fails the load, and the field says its options failed to load. Field access does not see the fields it reads: a predicate over a restricted field tells the user which rows pass it.
  • Mounting refuses a filter on a choice without a relationship, and on a creatable choice, whose dialog may save a record the filter drops.
Source

pub fn relationship_labelled<R>( self, label: impl Fn(&R::Model) -> String + Send + Sync + 'static, ) -> Self
where R: OptionSource + 'static,

Loads options as relationship does, labelling each by label instead of the source’s label: to tell apart records that share a title. The option’s record loads without its relations.

Source§

impl<F> ChoiceField<F>

Source

pub fn default(self, value: impl IntoFormValue) -> Self

Fills the create form’s control with value: any FormScalar or a &str in the form spelling, or, for a multiple choice, an array or Vec of them. On a disabled field it is the value a create stores. A schema refuses a default the control never posts: one a typed field does not parse, one not among a choice’s options, a list on a control posting one value, and one value on a multiple choice.

Field::choice(Post::fields().status())
    .options(["draft", "published"])
    .default("draft");
Field::text(Post::fields().views()).default(0_i64).disabled();
Field::choice_input("channels")
    .options(["email", "sms"])
    .multiple()
    .default(["email"]);
  • The control names its help text in aria-describedby, beside its error.
  • A disabled control renders disabled, so the browser posts nothing for it, and the panel drops a key posted anyway, with a file field’s carried upload. An edit keeps the stored value. A create stores the field’s default, else its blank answer: .disabled() on a field with neither refuses to mount, since no create could fill it.
  • .disabled_on_edit() leaves the create form’s control enabled: the create sets the value, and no edit changes it.
  • A condition watching a disabled field reads what the browser shows: its default on a create, its stored value on an edit, never what the submission posts.
  • A default seeds the create form and an action’s input form; an edit form shows the stored value. The blank answer is different: it is what an empty submission stores. Mounting refuses a default the control never posts: one a typed field does not parse, or not one of a choice’s options.
  • A repeater’s row controls take neither disabled nor default: mounting refuses both.
Source§

impl<F> ChoiceField<F>

Source

pub fn label(self, label: impl Into<Text>) -> Self

Source

pub fn name(&self) -> &str

Source

pub fn visible_when( self, watched: &impl Watched<F>, values: impl IntoIterator<Item = impl Into<String>>, ) -> Self

Shows the field only while watched posts one of values: an option’s value, or true for a checked toggle. A hidden field posts nothing, so an edit keeps its stored value and a create takes its blank answer; a field with no blank answer cannot be conditional. A field shows under one condition: a later call replaces an earlier one, so a field depending on two watched fields sits in a Section guarded by the other.

let kind = Field::choice(Customer::fields().kind()).options(["person", "company"]);
let vat = Field::text(Customer::fields().vat_number()).visible_when(&kind, ["company"]);
Schema::new((kind, vat));
  • The browser shows and hides the field as the watched one changes, with no request. A hidden field’s control is disabled, so the browser neither validates nor posts it. Without JavaScript, the fields start as the stored values show them and stay that way.
  • The panel reads a submission the same way and drops a hidden field’s posted key, and a file field’s carried upload: an edit keeps the stored value, and a create stores the blank answer. A conditional field therefore needs a blank answer, and mounting refuses one without.
  • Mounting also refuses a value the watched field never posts (a choice’s option or a toggle’s true and false are checked), and a condition watching a field that another condition hides while the guarded field still shows: place the guarded field inside the block that hides the watched one. A schema also refuses a condition watching a field it does not place, which only a page’s or an action’s schema can do: a resource’s form places every record-form field.
  • A toggle is followed through its checkbox. An app’s own checkbox Control posts the same value checked or not, so a condition cannot follow it.
  • A multiple choice’s key holds a list, which no condition value spells: mounting refuses a condition watching one.
Source

pub fn span(self, columns: u8) -> Self

Spans columns of the Grid holding the field, clamped to 1..12. While the grid stacks its children, or lays out fewer columns, the field spans them all. Outside a grid, the span does nothing.

Source

pub fn span_full(self) -> Self

Spans every column of the Grid holding the field.

Source§

impl<F> ChoiceField<F>

Source

pub fn help(self, text: impl Into<Text>) -> Self

Renders text under the control, which the control names as its description.

Source

pub fn disabled(self) -> Self

Renders the control disabled on the create and edit forms. The server drops a value posted for it anyway, so an edit keeps the stored value and a create takes the field’s default, else its blank answer; a field with neither cannot be created, and its panel refuses to mount.

Source

pub fn disabled_on_edit(self) -> Self

Renders the control disabled on the edit form only: the create form sets the value, and no edit changes it.

Source

pub fn custom(self, control: impl Control + 'static) -> CustomField<F>

Renders the field with an app’s Control instead, as Field::custom does: how a record form’s control takes a custom input. The field keeps its key and label; the replaced control’s own modifiers, such as email, unique or options, are dropped with it.

Trait Implementations§

Source§

impl<F> From<ChoiceField<F>> for Field

Source§

fn from(builder: ChoiceField<F>) -> Field

Converts to this type from the input type.
Source§

impl<F> IntoSchema<F> for ChoiceField<F>

Source§

impl<F> Watched<F> for ChoiceField<F>

Auto Trait Implementations§

§

impl<F = ()> !RefUnwindSafe for ChoiceField<F>

§

impl<F = ()> !UnwindSafe for ChoiceField<F>

§

impl<F> Forget for ChoiceField<F>

§

impl<F> Freeze for ChoiceField<F>
where PhantomData<fn() -> F>: Freeze,

§

impl<F> Send for ChoiceField<F>
where PhantomData<fn() -> F>: Send,

§

impl<F> Sync for ChoiceField<F>
where PhantomData<fn() -> F>: Sync,

§

impl<F> Unpin for ChoiceField<F>
where PhantomData<fn() -> F>: Unpin,

§

impl<F> UnsafeUnpin for ChoiceField<F>
where PhantomData<fn() -> F>: UnsafeUnpin,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> ErasedDestructor for T
where T: 'static,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more