mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
Remove InputData & ChangeData (#2000)
This commit is contained in:
parent
0afc6952cf
commit
c6099cf875
@ -1,5 +1,8 @@
|
||||
use std::cell::Cell;
|
||||
use yew::{html, Callback, Component, ComponentLink, Html, InputData, Properties, ShouldRender};
|
||||
use yew::{
|
||||
html, web_sys::HtmlInputElement, Callback, Component, ComponentLink, Html, InputEvent,
|
||||
Properties, ShouldRender, TargetCast,
|
||||
};
|
||||
|
||||
thread_local! {
|
||||
static SLIDER_ID: Cell<usize> = Cell::default();
|
||||
@ -78,6 +81,11 @@ impl Component for Slider {
|
||||
10f64.powi(-(p as i32))
|
||||
});
|
||||
|
||||
let oninput = onchange.reform(|e: InputEvent| {
|
||||
let input: HtmlInputElement = e.target_unchecked_into();
|
||||
input.value_as_number()
|
||||
});
|
||||
|
||||
html! {
|
||||
<div class="slider">
|
||||
<label for={id.clone()} class="slider__label">{ label }</label>
|
||||
@ -85,8 +93,7 @@ impl Component for Slider {
|
||||
{id}
|
||||
class="slider__input"
|
||||
min={min.to_string()} max={max.to_string()} step={step.to_string()}
|
||||
oninput={onchange.reform(|data: InputData| data.value.parse().unwrap())}
|
||||
value={value.to_string()}
|
||||
{oninput}
|
||||
/>
|
||||
<span class="slider__value">{ display_value }</span>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
use crate::Client;
|
||||
use yew::{
|
||||
classes, html, Callback, Component, ComponentLink, Html, InputData, Properties, ShouldRender,
|
||||
classes, html,
|
||||
web_sys::{Event, HtmlInputElement, HtmlTextAreaElement},
|
||||
Callback, Component, ComponentLink, Html, Properties, ShouldRender, TargetCast,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -72,26 +74,36 @@ impl Component for AddClientForm {
|
||||
|
||||
fn view(&self) -> Html {
|
||||
let Self { link, client, .. } = self;
|
||||
|
||||
let update_name = |f: fn(String) -> Msg| {
|
||||
link.callback(move |e: Event| {
|
||||
let input: HtmlInputElement = e.target_unchecked_into();
|
||||
f(input.value())
|
||||
})
|
||||
};
|
||||
|
||||
let update_desc = link.callback(|e: Event| {
|
||||
let textarea: HtmlTextAreaElement = e.target_unchecked_into();
|
||||
Msg::UpdateDescription(textarea.value())
|
||||
});
|
||||
|
||||
html! {
|
||||
<>
|
||||
<div class="names">
|
||||
<input
|
||||
class={classes!("new-client", "firstname")}
|
||||
placeholder="First name"
|
||||
value={client.first_name.clone()}
|
||||
oninput={link.callback(|e: InputData| Msg::UpdateFirstName(e.value))}
|
||||
onchange={update_name(Msg::UpdateFirstName)}
|
||||
/>
|
||||
<input
|
||||
class={classes!("new-client", "lastname")}
|
||||
placeholder="Last name"
|
||||
value={client.last_name.clone()}
|
||||
oninput={link.callback(|e: InputData| Msg::UpdateLastName(e.value))}
|
||||
onchange={update_name(Msg::UpdateLastName)}
|
||||
/>
|
||||
<textarea
|
||||
class={classes!("new-client", "description")}
|
||||
placeholder="Description"
|
||||
value={client.description.clone()}
|
||||
oninput={link.callback(|e: InputData| Msg::UpdateDescription(e.value))}
|
||||
onchange={update_desc}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use yew::{html, ChangeData, Component, ComponentLink, Html, ShouldRender};
|
||||
use web_sys::{Event, HtmlInputElement};
|
||||
use yew::{html, html::TargetCast, Component, ComponentLink, Html, ShouldRender};
|
||||
|
||||
use gloo::file::callbacks::FileReader;
|
||||
use gloo::file::File;
|
||||
@ -87,9 +88,11 @@ impl Component for Model {
|
||||
<div>
|
||||
<div>
|
||||
<p>{ "Choose a file to upload to see the uploaded bytes" }</p>
|
||||
<input type="file" multiple=true onchange={self.link.callback(move |value| {
|
||||
<input type="file" multiple=true onchange={self.link.callback(move |e: Event| {
|
||||
let mut result = Vec::new();
|
||||
if let ChangeData::Files(files) = value {
|
||||
let input: HtmlInputElement = e.target_unchecked_into();
|
||||
|
||||
if let Some(files) = input.files() {
|
||||
let files = js_sys::try_iter(&files)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use wasm_bindgen::prelude::*;
|
||||
use yew::prelude::*;
|
||||
use yew::{prelude::*, web_sys::HtmlTextAreaElement};
|
||||
|
||||
mod bindings;
|
||||
|
||||
@ -57,7 +57,10 @@ impl Component for Model {
|
||||
<>
|
||||
<textarea
|
||||
class="code-block"
|
||||
oninput={self.link.callback(|input: InputData| Msg::Payload(input.value))}
|
||||
oninput={self.link.callback(|e: InputEvent| {
|
||||
let input: HtmlTextAreaElement = e.target_unchecked_into();
|
||||
Msg::Payload(input.value())
|
||||
})}
|
||||
value={self.payload.clone()}
|
||||
/>
|
||||
<button onclick={self.link.callback(|_| Msg::Payload(bindings::get_payload()))}>
|
||||
|
||||
@ -2,7 +2,7 @@ use instant::Instant;
|
||||
use person::PersonType;
|
||||
use yew::prelude::*;
|
||||
use yew::utils::NeqAssign;
|
||||
use yew::web_sys::HtmlElement;
|
||||
use yew::web_sys::{HtmlElement, HtmlInputElement};
|
||||
|
||||
mod person;
|
||||
mod random;
|
||||
@ -10,7 +10,7 @@ mod random;
|
||||
pub enum Msg {
|
||||
CreatePersons(usize),
|
||||
CreatePersonsPrepend(usize),
|
||||
ChangeRatio(String),
|
||||
ChangeRatio(f64),
|
||||
DeletePersonById(usize),
|
||||
DeleteEverybody,
|
||||
SwapRandom,
|
||||
@ -70,7 +70,6 @@ impl Component for Model {
|
||||
true
|
||||
}
|
||||
Msg::ChangeRatio(ratio) => {
|
||||
let ratio: f64 = ratio.parse().unwrap_or(0.5);
|
||||
if self.build_component_ratio.neq_assign(ratio) {
|
||||
log::info!("Ratio changed: {}", ratio);
|
||||
true
|
||||
@ -169,8 +168,10 @@ impl Model {
|
||||
{ self.build_component_ratio }
|
||||
</p>
|
||||
<input name="ratio" type="range" class="form-control-range" min="0.0" max="1.0" step="any"
|
||||
value={self.build_component_ratio.to_string()}
|
||||
oninput={self.link.callback(|e: InputData| Msg::ChangeRatio(e.value))}
|
||||
oninput={self.link.callback(|e: InputEvent| {
|
||||
let input: HtmlInputElement = e.target_unchecked_into();
|
||||
Msg::ChangeRatio(input.value_as_number())
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::{CanvasRenderingContext2d, Document, HtmlCanvasElement};
|
||||
use yew::{html, Component, ComponentLink, Html, InputData, ShouldRender};
|
||||
use web_sys::{
|
||||
CanvasRenderingContext2d, Document, HtmlCanvasElement, HtmlInputElement, InputEvent,
|
||||
};
|
||||
use yew::{html, Component, ComponentLink, Html, ShouldRender, TargetCast};
|
||||
|
||||
pub enum Msg {
|
||||
UpdateName(String),
|
||||
@ -40,7 +42,10 @@ impl Component for Model {
|
||||
<div>
|
||||
<input
|
||||
value={self.name.clone()}
|
||||
oninput={self.link.callback(|e: InputData| Msg::UpdateName(e.value))}
|
||||
oninput={self.link.callback(|e: InputEvent| {
|
||||
let input = e.target_unchecked_into::<HtmlInputElement>();
|
||||
Msg::UpdateName(input.value())
|
||||
})}
|
||||
/>
|
||||
<p>{ self.name.chars().rev().collect::<String>() }</p>
|
||||
</div>
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use weblog::web_sys::HtmlInputElement;
|
||||
use yew::prelude::*;
|
||||
|
||||
pub enum Msg {
|
||||
SetText(String),
|
||||
Submit,
|
||||
Submit(String),
|
||||
}
|
||||
|
||||
#[derive(Properties, Clone, PartialEq)]
|
||||
@ -31,12 +31,7 @@ impl Component for TextInput {
|
||||
|
||||
fn update(&mut self, msg: Self::Message) -> ShouldRender {
|
||||
match msg {
|
||||
Msg::SetText(text) => {
|
||||
self.text = text;
|
||||
true
|
||||
}
|
||||
Msg::Submit => {
|
||||
let text = std::mem::replace(&mut self.text, self.props.value.clone());
|
||||
Msg::Submit(text) => {
|
||||
self.props.onsubmit.emit(text);
|
||||
true
|
||||
}
|
||||
@ -54,15 +49,23 @@ impl Component for TextInput {
|
||||
}
|
||||
|
||||
fn view(&self) -> Html {
|
||||
let onkeydown = self.link.batch_callback(|e: KeyboardEvent| {
|
||||
e.stop_propagation();
|
||||
if e.key() == "Enter" {
|
||||
let input: HtmlInputElement = e.target_unchecked_into();
|
||||
let value = input.value();
|
||||
input.set_value("");
|
||||
Some(Msg::Submit(value))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
html! {
|
||||
<input
|
||||
placeholder={self.props.value.clone()}
|
||||
type="text"
|
||||
value={self.text.clone()}
|
||||
oninput={self.link.callback(|e: InputData| Msg::SetText(e.value))}
|
||||
onkeydown={self.link.batch_callback(move |e: KeyboardEvent| {
|
||||
e.stop_propagation();
|
||||
if e.key() == "Enter" { Some(Msg::Submit) } else { None }
|
||||
})}
|
||||
{onkeydown}
|
||||
/>
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,9 @@ use gloo::storage::{LocalStorage, Storage};
|
||||
use state::{Entry, Filter, State};
|
||||
use strum::IntoEnumIterator;
|
||||
use yew::web_sys::HtmlInputElement as InputElement;
|
||||
use yew::{classes, html, Component, ComponentLink, Html, InputData, NodeRef, ShouldRender};
|
||||
use yew::{
|
||||
classes, html, Component, ComponentLink, FocusEvent, Html, NodeRef, ShouldRender, TargetCast,
|
||||
};
|
||||
use yew::{events::KeyboardEvent, Classes};
|
||||
|
||||
mod state;
|
||||
@ -10,10 +12,8 @@ mod state;
|
||||
const KEY: &str = "yew.todomvc.self";
|
||||
|
||||
pub enum Msg {
|
||||
Add,
|
||||
Edit(usize),
|
||||
Update(String),
|
||||
UpdateEdit(String),
|
||||
Add(String),
|
||||
Edit((usize, String)),
|
||||
Remove(usize),
|
||||
SetFilter(Filter),
|
||||
ToggleAll,
|
||||
@ -38,7 +38,6 @@ impl Component for Model {
|
||||
let state = State {
|
||||
entries,
|
||||
filter: Filter::All,
|
||||
value: "".into(),
|
||||
edit_value: "".into(),
|
||||
};
|
||||
let focus_ref = NodeRef::default();
|
||||
@ -51,31 +50,20 @@ impl Component for Model {
|
||||
|
||||
fn update(&mut self, msg: Self::Message) -> ShouldRender {
|
||||
match msg {
|
||||
Msg::Add => {
|
||||
let description = self.state.value.trim();
|
||||
Msg::Add(description) => {
|
||||
if !description.is_empty() {
|
||||
let entry = Entry {
|
||||
description: description.to_string(),
|
||||
description: description.trim().to_string(),
|
||||
completed: false,
|
||||
editing: false,
|
||||
};
|
||||
self.state.entries.push(entry);
|
||||
}
|
||||
self.state.value = "".to_string();
|
||||
}
|
||||
Msg::Edit(idx) => {
|
||||
let edit_value = self.state.edit_value.trim().to_string();
|
||||
self.state.complete_edit(idx, edit_value);
|
||||
Msg::Edit((idx, edit_value)) => {
|
||||
self.state.complete_edit(idx, edit_value.trim().to_string());
|
||||
self.state.edit_value = "".to_string();
|
||||
}
|
||||
Msg::Update(val) => {
|
||||
println!("Input: {}", val);
|
||||
self.state.value = val;
|
||||
}
|
||||
Msg::UpdateEdit(val) => {
|
||||
println!("Input: {}", val);
|
||||
self.state.edit_value = val;
|
||||
}
|
||||
Msg::Remove(idx) => {
|
||||
self.state.remove(idx);
|
||||
}
|
||||
@ -180,17 +168,23 @@ impl Model {
|
||||
}
|
||||
|
||||
fn view_input(&self) -> Html {
|
||||
let onkeypress = self.link.batch_callback(|e: KeyboardEvent| {
|
||||
if e.key() == "Enter" {
|
||||
let input: InputElement = e.target_unchecked_into();
|
||||
let value = input.value();
|
||||
input.set_value("");
|
||||
Some(Msg::Add(value))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
html! {
|
||||
// You can use standard Rust comments. One line:
|
||||
// <li></li>
|
||||
<input
|
||||
class="new-todo"
|
||||
placeholder="What needs to be done?"
|
||||
value={self.state.value.clone()}
|
||||
oninput={self.link.callback(|e: InputData| Msg::Update(e.value))}
|
||||
onkeypress={self.link.batch_callback(|e: KeyboardEvent| {
|
||||
if e.key() == "Enter" { Some(Msg::Add) } else { None }
|
||||
})}
|
||||
{onkeypress}
|
||||
/>
|
||||
/* Or multiline:
|
||||
<ul>
|
||||
@ -226,6 +220,20 @@ impl Model {
|
||||
}
|
||||
|
||||
fn view_entry_edit_input(&self, (idx, entry): (usize, &Entry)) -> Html {
|
||||
let edit = move |input: InputElement| {
|
||||
let value = input.value();
|
||||
input.set_value("");
|
||||
Msg::Edit((idx, value))
|
||||
};
|
||||
|
||||
let onblur = self
|
||||
.link
|
||||
.callback(move |e: FocusEvent| edit(e.target_unchecked_into()));
|
||||
|
||||
let onkeypress = self.link.batch_callback(move |e: KeyboardEvent| {
|
||||
(e.key() == "Enter").then(|| edit(e.target_unchecked_into()))
|
||||
});
|
||||
|
||||
if entry.editing {
|
||||
html! {
|
||||
<input
|
||||
@ -234,11 +242,8 @@ impl Model {
|
||||
ref={self.focus_ref.clone()}
|
||||
value={self.state.edit_value.clone()}
|
||||
onmouseover={self.link.callback(|_| Msg::Focus)}
|
||||
oninput={self.link.callback(|e: InputData| Msg::UpdateEdit(e.value))}
|
||||
onblur={self.link.callback(move |_| Msg::Edit(idx))}
|
||||
onkeypress={self.link.batch_callback(move |e: KeyboardEvent| {
|
||||
if e.key() == "Enter" { Some(Msg::Edit(idx)) } else { None }
|
||||
})}
|
||||
{onblur}
|
||||
{onkeypress}
|
||||
/>
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -5,7 +5,6 @@ use strum_macros::{EnumIter, ToString};
|
||||
pub struct State {
|
||||
pub entries: Vec<Entry>,
|
||||
pub filter: Filter,
|
||||
pub value: String,
|
||||
pub edit_value: String,
|
||||
}
|
||||
|
||||
|
||||
@ -27,11 +27,11 @@ help: consider importing one of these items
|
||||
83 | use crate::t9::foo;
|
||||
|
|
||||
|
||||
error[E0277]: the trait bound `t1::Value: std::default::Default` is not satisfied
|
||||
error[E0277]: the trait bound `Value: std::default::Default` is not satisfied
|
||||
--> $DIR/fail.rs:9:21
|
||||
|
|
||||
9 | #[derive(Clone, Properties)]
|
||||
| ^^^^^^^^^^ the trait `std::default::Default` is not implemented for `t1::Value`
|
||||
| ^^^^^^^^^^ the trait `std::default::Default` is not implemented for `Value`
|
||||
|
|
||||
= note: required by `Option::<T>::unwrap_or_default`
|
||||
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
@ -9,7 +9,7 @@ use std::{cell::RefCell, rc::Rc};
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// # use yew::prelude::*;
|
||||
/// # use yew::{prelude::*, web_sys::{Event, HtmlInputElement}};
|
||||
/// # use std::rc::Rc;
|
||||
/// # use std::cell::RefCell;
|
||||
/// # use std::ops::{Deref, DerefMut};
|
||||
@ -32,10 +32,9 @@ use std::{cell::RefCell, rc::Rc};
|
||||
///
|
||||
/// let onchange = {
|
||||
/// let message = message.clone();
|
||||
/// Callback::from(move |e| {
|
||||
/// if let ChangeData::Value(value) = e {
|
||||
/// message.set(value)
|
||||
/// }
|
||||
/// Callback::from(move |e: Event| {
|
||||
/// let input: HtmlInputElement = e.target_unchecked_into();
|
||||
/// message.set(input.value())
|
||||
/// })
|
||||
/// };
|
||||
///
|
||||
|
||||
@ -6,7 +6,7 @@ impl_action! {
|
||||
oncancel(name: "cancel", event: Event) -> web_sys::Event => |_, event| { event }
|
||||
oncanplay(name: "canplay", event: Event) -> web_sys::Event => |_, event| { event }
|
||||
oncanplaythrough(name: "canplaythrough", event: Event) -> web_sys::Event => |_, event| { event }
|
||||
onchange(name: "change", event: Event) -> ChangeData => |this: &Element, _| { onchange_handler(this) }
|
||||
onchange(name: "change", event: Event) -> web_sys::Event => |_, event| { event }
|
||||
onclick(name: "click", event: MouseEvent) -> web_sys::MouseEvent => |_, event| { event }
|
||||
onclose(name: "close", event: Event) -> web_sys::Event => |_, event| { event }
|
||||
oncontextmenu(name: "contextmenu", event: MouseEvent) -> web_sys::MouseEvent => |_, event| { event }
|
||||
@ -29,7 +29,7 @@ impl_action! {
|
||||
onfocusout(name: "focusout", event: FocusEvent) -> web_sys::FocusEvent => |_, event| { event }
|
||||
// web_sys doesn't have a struct for `FormDataEvent`
|
||||
onformdata(name: "formdata", event: Event) -> web_sys::Event => |_, event| { event }
|
||||
oninput(name: "input", event: InputEvent) -> InputData => |this: &Element, event| { oninput_handler(this, event) }
|
||||
oninput(name: "input", event: InputEvent) -> web_sys::InputEvent => |_, event| { event }
|
||||
oninvalid(name: "invalid", event: Event) -> web_sys::Event => |_, event| { event }
|
||||
onkeydown(name: "keydown", event: KeyboardEvent) -> web_sys::KeyboardEvent => |_, event| { event }
|
||||
onkeypress(name: "keypress", event: KeyboardEvent) -> web_sys::KeyboardEvent => |_, event| { event }
|
||||
|
||||
@ -3,89 +3,128 @@ mod macros;
|
||||
mod events;
|
||||
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::{
|
||||
Element, FileList, HtmlInputElement as InputElement, HtmlSelectElement as SelectElement,
|
||||
HtmlTextAreaElement as TextAreaElement, InputEvent,
|
||||
};
|
||||
use web_sys::{Event, EventTarget};
|
||||
|
||||
use crate::Callback;
|
||||
pub use events::*;
|
||||
|
||||
/// A type representing data from `oninput` event.
|
||||
#[derive(Debug)]
|
||||
pub struct InputData {
|
||||
/// Inserted characters. Contains value from
|
||||
/// [InputEvent](https://developer.mozilla.org/en-US/docs/Web/API/InputEvent/data).
|
||||
pub value: String,
|
||||
/// The InputEvent received.
|
||||
pub event: InputEvent,
|
||||
}
|
||||
/// A trait to obtain a generic event target.
|
||||
///
|
||||
/// The methods in this trait are convenient helpers that use the [`JsCast`] trait internally
|
||||
/// to do the conversion.
|
||||
pub trait TargetCast
|
||||
where
|
||||
Self: AsRef<Event>,
|
||||
{
|
||||
/// Performs a dynamic cast (checked at runtime) of this events target into the type `T`.
|
||||
///
|
||||
/// This method can return [`None`] for two reasons:
|
||||
/// - The event's target was [`None`]
|
||||
/// - The event's target type did not match `T`
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use yew::{prelude::*, web_sys::{Event, HtmlTextAreaElement}};
|
||||
/// # enum Msg {
|
||||
/// # Value(String),
|
||||
/// # }
|
||||
/// # struct Comp {
|
||||
/// # link: ComponentLink<Self>,
|
||||
/// # }
|
||||
/// # impl Component for Comp {
|
||||
/// # type Properties = ();
|
||||
/// # type Message = Msg;
|
||||
/// # fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
|
||||
/// # Self { link }
|
||||
/// # }
|
||||
/// # fn update(&mut self, _: Self::Message) -> ShouldRender { false }
|
||||
/// # fn change(&mut self, _: Self::Properties) -> ShouldRender { false }
|
||||
///
|
||||
/// fn view(&self) -> Html {
|
||||
/// html! {
|
||||
/// <div
|
||||
/// onchange={self.link.batch_callback(|e: Event| {
|
||||
/// if let Some(input) = e.target_dyn_into::<HtmlTextAreaElement>() {
|
||||
/// Some(Msg::Value(input.value()))
|
||||
/// } else {
|
||||
/// None
|
||||
/// }
|
||||
/// })}
|
||||
/// >
|
||||
/// <textarea />
|
||||
/// <input type="text" />
|
||||
/// </div>
|
||||
/// }
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
/// _Note: if you can apply the [`Callback`] directly onto an element which doesn't have a child
|
||||
/// consider using [`TargetCast::target_unchecked_into<T>`]_
|
||||
#[inline]
|
||||
fn target_dyn_into<T>(&self) -> Option<T>
|
||||
where
|
||||
T: AsRef<EventTarget> + JsCast,
|
||||
{
|
||||
self.as_ref()
|
||||
.target()
|
||||
.and_then(|target| target.dyn_into().ok())
|
||||
}
|
||||
|
||||
// There is no '.../Web/API/ChangeEvent/data' (for onchange) similar to
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/InputEvent/data (for oninput).
|
||||
// ChangeData actually contains the value of the InputElement/TextAreaElement
|
||||
// after `change` event occured or contains the SelectElement (see more at the
|
||||
// variant ChangeData::Select)
|
||||
|
||||
/// A type representing change of value(s) of an element after committed by user
|
||||
/// ([onchange event](https://developer.mozilla.org/en-US/docs/Web/Events/change)).
|
||||
#[derive(Debug)]
|
||||
pub enum ChangeData {
|
||||
/// Value of the element in cases of `<input>`, `<textarea>`
|
||||
Value(String),
|
||||
/// SelectElement in case of `<select>` element. You can use one of methods of SelectElement
|
||||
/// to collect your required data such as `value` and `selected_index`.
|
||||
/// You can also iterate throught `selected_options` yourself, this does require adding the
|
||||
/// [web-sys](https://crates.io/crates/web-sys) crate with the `HtmlCollection` feature.
|
||||
Select(SelectElement),
|
||||
/// Files
|
||||
Files(FileList),
|
||||
}
|
||||
|
||||
fn oninput_handler(this: &Element, event: InputEvent) -> InputData {
|
||||
// Normally only InputElement or TextAreaElement can have an oninput event listener. In
|
||||
// practice though any element with `contenteditable=true` may generate such events,
|
||||
// therefore here we fall back to just returning the text content of the node.
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event.
|
||||
let (v1, v2) = (
|
||||
this.dyn_ref().map(|input: &InputElement| input.value()),
|
||||
this.dyn_ref().map(|input: &TextAreaElement| input.value()),
|
||||
);
|
||||
let v3 = this.text_content();
|
||||
let value = v1.or(v2).or(v3)
|
||||
.expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener");
|
||||
InputData { value, event }
|
||||
}
|
||||
|
||||
fn onchange_handler(this: &Element) -> ChangeData {
|
||||
match this.node_name().as_ref() {
|
||||
"INPUT" => {
|
||||
let input = this.dyn_ref::<InputElement>().unwrap();
|
||||
let is_file = input
|
||||
.get_attribute("type")
|
||||
.map(|value| value.eq_ignore_ascii_case("file"))
|
||||
.unwrap_or(false);
|
||||
if is_file {
|
||||
let files: FileList = input.files().unwrap();
|
||||
ChangeData::Files(files)
|
||||
} else {
|
||||
ChangeData::Value(input.value())
|
||||
}
|
||||
}
|
||||
"TEXTAREA" => {
|
||||
let tae = this.dyn_ref::<TextAreaElement>().unwrap();
|
||||
ChangeData::Value(tae.value())
|
||||
}
|
||||
"SELECT" => {
|
||||
let se = this.dyn_ref::<SelectElement>().unwrap().clone();
|
||||
ChangeData::Select(se)
|
||||
}
|
||||
_ => {
|
||||
panic!("only an InputElement, TextAreaElement or SelectElement can have an onchange event listener");
|
||||
}
|
||||
/// Performs a zero-cost unchecked cast of this events target into the type `T`.
|
||||
///
|
||||
/// This method **does not check whether the event target is an instance of `T`**. If used
|
||||
/// incorrectly then this method may cause runtime exceptions in both Rust and JS, this should
|
||||
/// be used with caution.
|
||||
///
|
||||
/// A common safe usage of this method is within a [`Callback`] that is applied directly to an
|
||||
/// element that has no children, thus `T` will be the type of the element the [`Callback`] is
|
||||
/// applied to.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use yew::{prelude::*, web_sys::{Event, HtmlInputElement}};
|
||||
/// # enum Msg {
|
||||
/// # Value(String),
|
||||
/// # }
|
||||
/// # struct Comp {
|
||||
/// # link: ComponentLink<Self>,
|
||||
/// # }
|
||||
/// # impl Component for Comp {
|
||||
/// # type Properties = ();
|
||||
/// # type Message = Msg;
|
||||
/// # fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
|
||||
/// # Self { link }
|
||||
/// # }
|
||||
/// # fn update(&mut self, _: Self::Message) -> ShouldRender { false }
|
||||
/// # fn change(&mut self, _: Self::Properties) -> ShouldRender { false }
|
||||
///
|
||||
/// fn view(&self) -> Html {
|
||||
/// html! {
|
||||
/// <input type="text"
|
||||
/// onchange={self.link.callback(|e: Event| {
|
||||
/// // Safe to use as callback is on an `input` element so this event can
|
||||
/// // only come from this input!
|
||||
/// let input: HtmlInputElement = e.target_unchecked_into();
|
||||
/// Msg::Value(input.value())
|
||||
/// })}
|
||||
/// />
|
||||
/// }
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
fn target_unchecked_into<T>(&self) -> T
|
||||
where
|
||||
T: AsRef<EventTarget> + JsCast,
|
||||
{
|
||||
self.as_ref().target().unwrap().unchecked_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: AsRef<Event>> TargetCast for E {}
|
||||
|
||||
/// A trait similar to `Into<T>` which allows conversion of a value into a [`Callback`].
|
||||
/// This is used for event listeners.
|
||||
pub trait IntoEventCallback<EVENT> {
|
||||
|
||||
@ -279,7 +279,7 @@ pub use web_sys;
|
||||
|
||||
/// The module that contains all events available in the framework.
|
||||
pub mod events {
|
||||
pub use crate::html::{ChangeData, InputData};
|
||||
pub use crate::html::TargetCast;
|
||||
|
||||
#[doc(no_inline)]
|
||||
pub use web_sys::{
|
||||
|
||||
@ -48,7 +48,7 @@ If you need the component to be re-rendered on state change, consider using [`us
|
||||
```rust
|
||||
#[function_component(UseRef)]
|
||||
fn ref_hook() -> Html {
|
||||
let (message, set_message) = use_state(|| "".to_string());
|
||||
let message = use_state(|| "".to_string());
|
||||
let message_count = use_ref(|| 0);
|
||||
|
||||
let onclick = Callback::from(move |e| {
|
||||
@ -62,11 +62,13 @@ fn ref_hook() -> Html {
|
||||
}
|
||||
});
|
||||
|
||||
let onchange = Callback::from(move |e| {
|
||||
if let ChangeData::Value(value) = e {
|
||||
set_message(value)
|
||||
}
|
||||
});
|
||||
let onchange = {
|
||||
let message = message.clone();
|
||||
Callback::from(move |e: Event| {
|
||||
let input: HtmlInputElement = e.target_unchecked_into();
|
||||
message.set(input.value());
|
||||
})
|
||||
};
|
||||
|
||||
html! {
|
||||
<div>
|
||||
|
||||
@ -222,7 +222,7 @@ end up using a version which conflicts with the version that Yew specifies.
|
||||
| `oncancel` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `oncanplay` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `oncanplaythrough` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onchange` | [ChangeData](https://docs.rs/yew/latest/yew/events/enum.ChangeData.html) |
|
||||
| `onchange` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onclick` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
|
||||
| `onclose` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `oncontextmenu` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
|
||||
@ -244,7 +244,7 @@ end up using a version which conflicts with the version that Yew specifies.
|
||||
| `onfocusin` | [FocusEvent](https://docs.rs/web-sys/latest/web_sys/struct.FocusEvent.html) |
|
||||
| `onfocusout` | [FocusEvent](https://docs.rs/web-sys/latest/web_sys/struct.FocusEvent.html) |
|
||||
| `onformdata` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `oninput` | [InputData](https://docs.rs/yew/latest/yew/events/struct.InputData.html) |
|
||||
| `oninput` | [InputEvent](https://docs.rs/web-sys/latest/web_sys/struct.InputEvent.html) |
|
||||
| `oninvalid` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onkeydown` | [KeyboardEvent](https://docs.rs/web-sys/latest/web_sys/struct.KeyboardEvent.html) |
|
||||
| `onkeypress` | [KeyboardEvent](https://docs.rs/web-sys/latest/web_sys/struct.KeyboardEvent.html) |
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user