Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setting the current value of an input? #233

Closed
LordZane opened this issue May 12, 2018 · 8 comments · Fixed by #1268
Closed

Setting the current value of an input? #233

LordZane opened this issue May 12, 2018 · 8 comments · Fixed by #1268

Comments

@LordZane
Copy link

If I have something like

<input type="text", value={self.text_state},/>

and change text_state, than the input dosen't update. value seems to only work for the inital value, when the component is constructed

@LordZane
Copy link
Author

LordZane commented May 13, 2018

Edit: Ignore the above post, turns out I had a different issue. What I'm trying to do is make it so you can type anything, but if the string contains 12345, anything you type after dosen't appear. What actually happens is it dosen't get set in self.seed which is good, but it still appears in the textbox, which is bad. Any idea whats wrong? https://gist.github.com/rust-play/e8adec2f576aae085f4aae4c8caa32cf. I would think that self.seed would stay the same, and since Msg::SetSeed returns true, it triggers an update for the value attribute on the textfield

@aidanhs
Copy link
Contributor

aidanhs commented Jan 27, 2019

I'm having this issue with a textarea - I want to record my thoughts (but I've not yet looked at the internals of this part of yew yet). All of the following is speculation:

My suspicion is that yew keeps around the last applied virtual dom (let's call it vdom_last). When editing a textarea or input, this gets applied to the dom first (dom_current), then gets fires oninput. You're refusing the change to your internal state, so vdom_next == vdom_last so nothing happens...but the change has already been applied to the dom! Your internal and visible state are now out of sync.

I think the solution is to take inspiration from react controlled components, where if you don't create an oninput handler which updates your element, the change is ignored completely - https://reactjs.org/docs/forms.html#controlled-components

@aidanhs
Copy link
Contributor

aidanhs commented Jan 27, 2019

Did a bit more research

  • Here's evidence of controlled components and their behaviour - if you've set value and not onChange the component is 'controlled' per this warning message. Here's a demo to play around with.
  • It appears the way it does this is not by preventing the event, but instead by restoring the state afterwards. Evidence in this comment and this bit of code which queues the state restore.
  • As an example of restoring state, here's the textarea exported function which calls the function that actually does the state restore.

Edit: here's the original implementation of a controlled input component in React, back in 2013!

@aidanhs
Copy link
Contributor

aidanhs commented Jan 27, 2019

I went down the list on https://github.com/flosse/rust-web-framework-comparison#frontend-frameworks-wasm

Of those:

  • stdweb isn't relevant (it's a 'lower level' api)
  • percy isn't relevant (it doesn't claim to do virtual dom diffing/rerendering)
  • smithy doesn't have any appropriate examples of input elements for me to repurpose (even 'smithy_test_site')
  • everything else (seed, ruukh, draco, willow, squark) has the same issue

I additionally came across dominator, which has a similar problem but it offers something a little different to dom diffing and there's a straightforward (if boilerplate-heavy) workaround.

@daniel-shimon
Copy link

Is there any progress done on this issue?
If not, is there a possible workaround to use?

@jstarry
Copy link
Member

jstarry commented Sep 27, 2019

There is a work-around, we should add documentation for this or make a better fix. The issue is that <input> will not update if value doesn't change. So you can change the value to a temporary intermediate value and then change to the original value to clear the user input value.

fn update(&mut self, msg: Self::Message) -> ShouldRender {
  match msg {
    Msg::OnInput(value) => {
      let old_value = self.input_value.clone();
      self.input_value = "".to_string();
      self.link.send_self(Msg::SetInput(old_value);
      true
    }
    Msg::SetInput(value) => {
      self.input_value = value;
      true
  }
}

fn view(&self) -> Html<Self> {
  html! { <input type="text" value={self.input_value} oninput=|e| Msg::OnInput(e.value) /> }
}

cc @LordZane @daniel-shimon @TitanThinktank

@ArthurFleischman
Copy link

whats the type of e in the input callback?

@guccialex
Copy link

<input oninput={ctx.link().callback(|e: web_sys::InputEvent| {
    let input: web_sys::HtmlTextAreaElement = e.target_unchecked_into();
    Msg::SetTypedUsername( input.value() )
})} />

or if the input type is range, to web_sys::HtmlInputElement instead of web_sys::HtmlTextAreaElement

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants