This is unreleased documentation for Yew Next version.
For up-to-date documentation, see the latest version on docs.rs.

yew/html/listener/
mod.rs

1#[macro_use]
2mod events;
3
4pub use events::*;
5use wasm_bindgen::JsCast;
6use web_sys::{Event, EventTarget};
7
8use crate::Callback;
9
10/// Cast [Event] `e` into it's target `T`.
11///
12/// This function mainly exists to provide type inference in the [impl_action] macro to the compiler
13/// and avoid some verbosity by not having to type the signature over and over in closure
14/// definitions.
15#[inline]
16pub(crate) fn cast_event<T>(e: Event) -> T
17where
18    T: JsCast,
19{
20    e.unchecked_into()
21}
22
23/// A trait to obtain a generic event target.
24///
25/// The methods in this trait are convenient helpers that use the [`JsCast`] trait internally
26/// to do the conversion.
27pub trait TargetCast
28where
29    Self: AsRef<Event>,
30{
31    /// Performs a dynamic cast (checked at runtime) of this events target into the type `T`.
32    ///
33    /// This method can return [`None`] for two reasons:
34    /// - The event's target was [`None`]
35    /// - The event's target type did not match `T`
36    ///
37    /// # Example
38    ///
39    /// ```
40    /// use web_sys::HtmlTextAreaElement;
41    /// use yew::prelude::*;
42    /// # enum Msg {
43    /// #   Value(String),
44    /// # }
45    /// # struct Comp;
46    /// # impl Component for Comp {
47    /// # type Message = Msg;
48    /// # type Properties = ();
49    /// # fn create(ctx: &Context<Self>) -> Self {
50    /// #   Self
51    /// # }
52    ///
53    /// fn view(&self, ctx: &Context<Self>) -> Html {
54    ///     html! {
55    ///         <div
56    ///             onchange={ctx.link().batch_callback(|e: Event| {
57    ///                 if let Some(input) = e.target_dyn_into::<HtmlTextAreaElement>() {
58    ///                     Some(Msg::Value(input.value()))
59    ///                 } else {
60    ///                     None
61    ///                 }
62    ///             })}
63    ///         >
64    ///             <textarea />
65    ///             <input type="text" />
66    ///         </div>
67    ///     }
68    /// }
69    /// # }
70    /// ```
71    /// _Note: if you can apply the [`Callback`] directly onto an element which doesn't have a child
72    /// consider using [`TargetCast::target_unchecked_into<T>`]_
73    #[inline]
74    fn target_dyn_into<T>(&self) -> Option<T>
75    where
76        T: AsRef<EventTarget> + JsCast,
77    {
78        self.as_ref()
79            .target()
80            .and_then(|target| target.dyn_into().ok())
81    }
82
83    #[inline]
84    /// Performs a zero-cost unchecked cast of this events target into the type `T`.
85    ///
86    /// This method **does not check whether the event target is an instance of `T`**. If used
87    /// incorrectly then this method may cause runtime exceptions in both Rust and JS, this should
88    /// be used with caution.
89    ///
90    /// A common safe usage of this method is within a [`Callback`] that is applied directly to an
91    /// element that has no children, thus `T` will be the type of the element the [`Callback`] is
92    /// applied to.
93    ///
94    /// # Example
95    ///
96    /// ```
97    /// use web_sys::HtmlInputElement;
98    /// use yew::prelude::*;
99    /// # enum Msg {
100    /// #   Value(String),
101    /// # }
102    /// # struct Comp;
103    /// # impl Component for Comp {
104    /// # type Message = Msg;
105    /// # type Properties = ();
106    /// # fn create(ctx: &Context<Self>) -> Self {
107    /// #   Self
108    /// # }
109    ///
110    /// fn view(&self, ctx: &Context<Self>) -> Html {
111    ///     html! {
112    ///         <input type="text"
113    ///             onchange={ctx.link().callback(|e: Event| {
114    ///                 // Safe to use as callback is on an `input` element so this event can
115    ///                 // only come from this input!
116    ///                 let input: HtmlInputElement = e.target_unchecked_into();
117    ///                 Msg::Value(input.value())
118    ///             })}
119    ///         />
120    ///     }
121    /// }
122    /// # }
123    /// ```
124    fn target_unchecked_into<T>(&self) -> T
125    where
126        T: AsRef<EventTarget> + JsCast,
127    {
128        self.as_ref().target().unwrap().unchecked_into()
129    }
130}
131
132impl<E: AsRef<Event>> TargetCast for E {}
133
134/// A trait similar to `Into<T>` which allows conversion of a value into a [`Callback`].
135/// This is used for event listeners.
136pub trait IntoEventCallback<EVENT> {
137    /// Convert `self` to `Option<Callback<EVENT>>`
138    fn into_event_callback(self) -> Option<Callback<EVENT>>;
139}
140
141impl<EVENT> IntoEventCallback<EVENT> for Callback<EVENT> {
142    fn into_event_callback(self) -> Option<Callback<EVENT>> {
143        Some(self)
144    }
145}
146
147impl<EVENT> IntoEventCallback<EVENT> for &Callback<EVENT> {
148    fn into_event_callback(self) -> Option<Callback<EVENT>> {
149        Some(self.clone())
150    }
151}
152
153impl<EVENT> IntoEventCallback<EVENT> for Option<Callback<EVENT>> {
154    fn into_event_callback(self) -> Option<Callback<EVENT>> {
155        self
156    }
157}
158
159impl<T, EVENT> IntoEventCallback<EVENT> for T
160where
161    T: Fn(EVENT) + 'static,
162{
163    fn into_event_callback(self) -> Option<Callback<EVENT>> {
164        Some(Callback::from(self))
165    }
166}
167
168impl<T, EVENT> IntoEventCallback<EVENT> for Option<T>
169where
170    T: Fn(EVENT) + 'static,
171{
172    fn into_event_callback(self) -> Option<Callback<EVENT>> {
173        Some(Callback::from(self?))
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    use super::*;
180
181    #[test]
182    fn supported_into_event_callback_types() {
183        let f = |_: usize| ();
184        let cb = Callback::from(f);
185
186        // Callbacks
187        let _: Option<Callback<usize>> = cb.clone().into_event_callback();
188        let _: Option<Callback<usize>> = (&cb).into_event_callback();
189        let _: Option<Callback<usize>> = Some(cb).into_event_callback();
190
191        // Fns
192        let _: Option<Callback<usize>> = f.into_event_callback();
193        let _: Option<Callback<usize>> = Some(f).into_event_callback();
194    }
195}