reactjsrustyewweb-sys

How to query and update the DOM with yew?


Is there any way to make DOM action via use_node_ref? or alternatively, how to do document.query_selector() in Rust using yew?

use web_sys::HtmlInputElement;
use yew::{
    function_component, functional::*, html,
    NodeRef, Html
};

#[function_component(UseRef)]
pub fn ref_hook() -> Html {
    let input_ref = use_node_ref();
    let all_editables =  input_ref.query_selector("[contenteditable]")
    web_sys::console::(all_editables)

    html! {
        <div>
            <input ref={input_ref} type="number" />
        </div>
    }
}

Goal: I have a rich text editor app. And I have a minified html in form of string like this <h1> this is title</h1><p>hello world</> and I need to get the DOM and change the innerHTML to set it to this value.

Goal2: I will also need to update the innerHtml as the user write things in the contenteditable elements. Fore example when the user type @john smith I will make a create an <a> element with href the have the link to John smith's profile.


Solution

  • Many things to tackle with your question.

    #1 Do not set inner html

    More to read about this in Alternative for innerHTML?

    But instead create text nodes. So using web-sys you would do something like:

    let txtNode: Node = window()
        .unwrap_throw()
        .document()
        .unwrap_throw()
        .create_text_node("Hello")
        .dyn_into()
        .unwrap_throw();
    
    myDomElement.append_hild(&txtNode).unwrap_throw();
    

    #2 How to query data from an input

    There are many ways to do this so ill just show you one of them - controlled input

    core idea is keep your input value in use_state and sync it with the input element using value and oninput attributes.

    #[function_component(ControlledInputComponent)]
    pub fn controlled_input_component() -> Html {
        let my_text_handle = use_state(|| "".to_string());
        let my_text = (*my_text_handle).clone();
        
        let handle_input = Callback::from(move |input_event: InputEvent| {
            let event: Event = input_event.dyn_into().unwrap_throw();
            let input_elem: HTMLInputElement = event.target().unwrap_throw().dyn_into().unwrap_throw();
            let value = input_elem.value();
            my_text_handle.set(value); // update as user types
            
        });
    
        html! {
            <div>
                <input type="text" value={my_text} oninput={handle_input} />
            </div>
        }
    }
    

    #3 Update DOM

    **External to yew as you should generally avoid updating DOM that is controlled by yew

    You can then use use_effec_with_deps to react to your input changing and update your external preview there

    let my_text_handle = use_state(|| "".to_string());
    let my_text = (*my_text_handle).clone();
    
    use_effect_with_deps(move |my_text| {
        // run all the code from my tip #1 like:
        // myDomElement.append_hild(&txtNode).unwrap_throw();
        ||{}
    }, my_text);