Password field how to retain value in Surface

When we want to retain the value of a password field across events, we use

value: input_value(f, :password)

for the password field in the LiveView. Otherwise, after validation, the field becomes empty. How should we specify the same in a surface form?

1 Like

It works the same way in Surface. Can you provide the Surface code snippet that isn’t behaving properly?

         <.form let={f} for={@changeset} action={Routes.user_registration_path(@socket, :create)} phx-change="validate" phx_trigger_action={@trigger_submit} phx-submit="save">
            <.text f={f}, id={:username} />
            <.email f={f}, id={:email} />
            <.password f={f}, id={:password} />
            <%= submit "Save" %>
          </.form>
  def password(assigns) do
    ~H"""
        <div class="flex flex-col">
            <%= label @f, @id %>
            <%= password_input @f, @id, value: input_value(@f, @id) %>
            <%= error_tag @f, @id %>
        </div>
    """
  end

That is how would I write in plain LiveView using heex components. Now, in surface

<Form for={@changeset} opts={phx_submit: "save", phx_change: "validate"}>
              <FormField type="text" opts={placeholder: "Enter your email"} label="Email" field={:email} />
              <FormField
                type="text"
                opts={placeholder: "Enter your username"}
                label="Username"
                field={:username}
              />
              <FormField
                type="password"
                opts={placeholder: "Enter your password"}        
                label="Password"
                field={:password}
              />

Here there is no f variable to identify the form - so can we use the changeset to extract the field value?

What is <FormField>? That doesn’t ship with Surface.

If you use <PasswordInput> that ships with Surface under the hood, you should be able to set value from the changeset:

value={get_change(@changeset, :password)}

Using Ecto.Changeset.get_change.

1 Like

Got it.
Yes - is a component I have written with styling.
Did not occur to me to use get_change.

Thanks, the problem is resolved.

1 Like
              <Form
                for={@changeset}
                action={Routes.user_registration_path(@socket, :create)}
                trigger_action={@trigger_submit}
                opts={phx_submit: "save", phx_change: "validate"}
              >
                <FormField type="email" opts={placeholder: "Enter your email"} field={:email} />
                <FormField type="text" opts={placeholder: "Username"} field={:username} />
                <FormField
                  type="password"
                  value={Ecto.Changeset.get_change(@changeset, :password)}
                  opts={placeholder: "Password"}
                  field={:password}
                />
                <Button class="w-full mt-5 blue" phx_disable_with="Welcome...">
                  Register
                </Button>
              </Form>

@paulstatezny This is the code. The password field is getting empty on submit. So, it is raising validation error. Any idea what I am missing?

It sounds like it’s now behaving properly on change, but emptying the field on submit. Is that correct?

Does your “submit” function emit a @changeset with the password field set? Or does it set something like hashed_password and leave password empty?

1 Like

Thanks. You gave the right pointer. The issue is in the FormField component.

<PasswordInput
          :if={@type == "password"}
          value={input_value(form, field)}
          opts={generate_text_field_options(assigns)}
          class={input_css(field_has_errors?(form, field)), @input_additional_class}
        />

I added the 3rd line now, and it started working.
Thanks.

1 Like