How to query and update the DOM with yew?

2.4k views Asked by At

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.

1

There are 1 answers

0
Julius Lungys On BEST ANSWER

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);