Skip to main content

TextField

Struct TextField 

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

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

Implementations§

Source§

impl<F: EditsRecords> TextField<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> TextField<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> TextField<F>

Source

pub fn gt(self, other: &TextField<F>) -> Self

Refuse a value not above other’s, a field of the same form and type that orders: a number, or a jiff date, time, date and time, or timestamp.

let starts = Field::text(lens!(Trip.starts_on));
let ends = Field::text(lens!(Trip.ends_on)).gte(&starts);
Schema::new((starts, ends));
  • Only the server checks it, in the same round as the controls’ other rules, and the error renders on this field, naming the other by its label.
  • The other’s value is its posted one, else the one the record stores, as a field disabled on edit posts none. A blank value on either side, or another field a condition hides, skips the comparison: whether a value is required is the field’s type to say.
  • An edit that changes neither field does not check them again, as a value the record already stores is not; changing either one does.
  • An edit checks a repeater row’s comparisons again unless the row’s two fields keep the values the record stores at the same row position.
  • A field of Option<T> compares with one of T. Mounting refuses the field itself, another field the schema does not place, one of another type or control, and a type without an order. A repeater’s row field compares with another field of the same row.
  • The refusal tells what the other field holds, so mounting also refuses one that a FieldAccess view rule hides from some user: FieldAccessFault::Watched.
Source

pub fn gte(self, other: &TextField<F>) -> Self

Refuse a value below other’s: gt has the rules.

Source

pub fn lt(self, other: &TextField<F>) -> Self

Refuse a value not below other’s: gt has the rules.

Source

pub fn lte(self, other: &TextField<F>) -> Self

Refuse a value above other’s: gt has the rules.

Source

pub fn different(self, other: &TextField<F>) -> Self

Refuse a value equal to other’s, as the field’s type reads both: 01 equals 1 for an integer, but text compares as typed, so Ada differs from ada. gt has the rules, but any type compares.

Source§

impl<F> TextField<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> TextField<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> TextField<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.

Source§

impl<F> TextField<F>

Source

pub fn choice(self) -> ChoiceField<F>

Renders the field as a choice instead, as Field::choice does, ready for options or relationship. The replaced control’s own modifiers are dropped with it.

Source§

impl<F> TextField<F>

Source

pub fn rich_text(self) -> RichTextField<F>

Renders the field as a rich text editor instead, storing HTML. The field’s type must be String or Option<String>, else mounting the panel fails, and the text control’s own modifiers are dropped.

use tablo_core::Tool;

let c = PostForm::controls();
c.body
    .rich_text()
    .toolbar(&[&[Tool::Bold, Tool::Italic], &[Tool::BulletList]]);
Source

pub fn email(self) -> Self

Validate the value as an email address, and render type="email": the domain needs two labels (a@b is refused), a display name (Ada <ada@example.com>) is refused, and the address is capped at 254 bytes.

Source

pub fn url(self) -> Self

Validate the value as an absolute http or https URL naming a host, and render type="url". The browser’s own check takes any scheme, so the server refuses the others, such as javascript:, which a link to the value would run.

Source

pub fn tel(self) -> Self

Render type="tel", which brings up a phone keypad. No format is checked, since numbers vary by country: add a pattern to require one.

Source

pub fn password(self) -> Self

Render type="password": the browser masks the value, a button beside it shows what was typed, and a refused form renders the control empty rather than echoing the password back into the page. It renders the one-line input even when multiline.

Source

pub fn unique(self) -> Self

Probes a unique index before the write, and reports a taken value inline. A text field over a column with a unique index probes without it; mounting refuses it on a column no unique index covers, and on a non-nullable column whose record-form field has a blank answer, since every empty submission would store one value.

The probe runs before the write, so two concurrent submissions can both pass it; the index stays the final check. When a write fails on a database error, the form probes again once the transaction is rolled back, so the losing submission re-renders with the same inline error, and a failure the probe does not explain answers 500. That needs the database to refuse the losing write once the winning one commits, as PostgreSQL and MySQL do; SQLite can refuse it as busy before then, and Turso’s concurrent transactions conflict at commit, so either can still answer 500. The probe reads through the resource’s scoped query, so an index broader than the scope, such as one wider than the tenancy, also answers 500.

Source

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

The control’s placeholder text.

Source

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

Text shown before the one-line input, joined to it, such as a currency: .prefix("$"). It is not part of the value, and a multiline textarea renders none.

Source

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

Text shown after the one-line input, joined to it, such as a unit: .suffix("kg"). Like a prefix, it is not part of the value.

Source

pub fn suggestions( self, values: impl IntoIterator<Item = impl Into<String>>, ) -> Self

Values the one-line input suggests as the user types, through a <datalist>. The user may still enter any other value: use a choice to restrict them. A password suggests none.

Field::text(lens!(Product.weight)).suffix("kg");
Field::text(lens!(Product.brand)).suggestions(["Acme", "Globex", "Initech"]);
Source

pub fn multiline(self, rows: u32) -> Self

Render a <textarea> of rows lines rather than a one-line input, for a column holding prose.

Source

pub fn min_length(self, chars: usize) -> Self

Refuse a value of fewer than chars characters, and render minlength.

Like every rule here, the browser checks it before it submits, and the server checks it again on the trimmed value once the field’s type parses it. A blank value is the field’s blank answer, which no rule refuses, and a value the record already stores is not checked again. A length counts characters, where a browser counts UTF-16 units.

Source

pub fn max_length(self, chars: usize) -> Self

Refuse a value of more than chars characters, and render maxlength, which stops the browser’s input there.

Source

pub fn min(self, bound: impl Display) -> Self

Refuse a value below bound, and render min.

The bound is a value of the field’s type, which orders: a number, or a jiff date, time, date and time, or timestamp. .min(0) on an i64 field, .min(date(2024, 1, 1)) on a jiff::civil::Date one; a bound the type does not parse or order fails the mount. A time’s control edits it to the minute, so give a time bound in whole minutes.

Source

pub fn max(self, bound: impl Display) -> Self

Refuse a value above bound, and render max; min says which bounds a field takes.

Source

pub fn pattern(self, pattern: impl Into<String>) -> Self

Refuse a value the regular expression pattern does not match whole, and render it as the pattern attribute.

The server matches it with the regex crate and the browser as a JavaScript expression in its v mode, both against the whole value, so write it in the syntax they share: classes, groups, alternation and repetition. Spell digits [0-9], since the server’s \d matches other scripts’ digits too; escape a - inside a class, which the browser otherwise refuses along with the whole pattern; and leave out lookaround, backreferences and inline flags. One the regex crate does not compile fails the mount. A .multiline textarea renders no pattern, so only the server checks it there.

Field::text(lens!(Product.sku)).pattern("[A-Z]{3}-[0-9]{4}");

Trait Implementations§

Source§

impl<F> From<TextField<F>> for Field

Source§

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

Converts to this type from the input type.
Source§

impl<F> IntoSchema<F> for TextField<F>

Source§

impl<F> Watched<F> for TextField<F>

Auto Trait Implementations§

§

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

§

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

§

impl<F> Forget for TextField<F>

§

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

§

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

§

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

§

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

§

impl<F> UnsafeUnpin for TextField<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