Skip to content

Commit f492ed0

Browse files
committed
Allow the virtual DOM to take over the original contents of its mount point.
1 parent 6979326 commit f492ed0

File tree

1 file changed

+69
-32
lines changed

1 file changed

+69
-32
lines changed

src/vdom.rs

Lines changed: 69 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -367,49 +367,73 @@ impl<Ms: Clone, Mdl, ElC: View<Ms> + 'static, GMs: 'static> App<Ms, Mdl, ElC, GM
367367
}
368368
}
369369

370-
/// App initialization: Collect its fundamental components, setup, and perform
371-
/// an initial render.
372-
pub fn run(self) -> Self {
373-
self.process_cmd_and_msg_queue(
374-
self.cfg
375-
.initial_orders
376-
.replace(None)
377-
.expect("initial_orders should be set in AppBuilder::finish")
378-
.effects,
379-
);
380-
// Our initial render. Can't initialize in new due to mailbox() requiring self.
381-
// "new" name is for consistency with `update` function.
382-
// this section parent is a placeholder, so we can iterate over children
383-
// in a way consistent with patching code.
384-
let mut new = El::empty(dom_types::Tag::Section);
385-
new.children = (self.cfg.view)(self.data.model.borrow().as_ref().unwrap()).els();
370+
/// Bootstrap the dom with the vdom by taking over all children of the mount point and
371+
/// replacing them with the vdom.
372+
fn bootstrap_vdom(&self) -> El<Ms> {
373+
let mut new = {
374+
// Construct the vdom from the root element. Subsequently strip the workspace so that we
375+
// can recreate it later. There could be missing nodes here.
376+
// TODO: Optimize by utilizing a patching strategy instead of recreating the workspace
377+
// TODO: nodes.
378+
let mut dom_nodes = websys_bridge::el_from_ws_element(&self.cfg.mount_point);
379+
dom_nodes.strip_ws_nodes();
380+
381+
// Replace the root dom with a placeholder tag and move the children from the root element
382+
// to the newly created root. Uses `Placeholder` to mimic update logic.
383+
let mut new = El::empty(dom_types::Tag::Placeholder);
384+
new.children = dom_nodes.children;
385+
new
386+
};
386387

388+
// Setup listeners
387389
self.setup_window_listeners();
388390
patch::setup_input_listeners(&mut new);
389391
patch::attach_listeners(&mut new, &self.mailbox());
390392

393+
// Recreate the needed nodes.
394+
// TODO: Refer the TODO at the beginning of the function.
391395
let mut new_node = Node::Element(new);
392-
393396
websys_bridge::assign_ws_nodes(&util::document(), &mut new_node);
394-
395-
if let Node::Element(mut new) = new_node {
396-
// Attach all top-level elements to the mount point: This is where our initial render occurs.
397-
for child in &mut new.children {
398-
match child {
399-
Node::Element(child_el) => {
400-
websys_bridge::attach_el_and_children(child_el, &self.cfg.mount_point);
401-
patch::attach_listeners(child_el, &self.mailbox());
402-
}
403-
Node::Text(top_child_text) => {
404-
websys_bridge::attach_text_node(top_child_text, &self.cfg.mount_point);
405-
}
406-
Node::Empty => (),
397+
let mut new = new_node
398+
.el()
399+
.expect("`El` placed into `Node::Element` `new_node` is no longer an `El`.");
400+
401+
// Remove all old elements, and replace them with out newly created elements - we have
402+
// effectively taken over the original DOM and now have free reign over the mount_point.
403+
// Attach all top-level elements to the mount point: This is where our initial render
404+
// occurs.
405+
while let Some(child) = self.cfg.mount_point.first_child() {
406+
self.cfg
407+
.mount_point
408+
.remove_child(&child)
409+
.expect("No problem removing node from parent.");
410+
}
411+
for child in &mut new.children {
412+
match child {
413+
Node::Element(child_el) => {
414+
websys_bridge::attach_el_and_children(child_el, &self.cfg.mount_point);
415+
patch::attach_listeners(child_el, &self.mailbox());
407416
}
417+
Node::Text(top_child_text) => {
418+
websys_bridge::attach_text_node(top_child_text, &self.cfg.mount_point);
419+
}
420+
Node::Empty => (),
408421
}
409-
self.data.main_el_vdom.replace(Some(new));
410422
}
411423

412-
// Update the state on page load, based
424+
new
425+
}
426+
/// App initialization: Collect its fundamental components, setup, and perform
427+
/// an initial render.
428+
pub fn run(self) -> Self {
429+
// Our initial render. Can't initialize in new due to mailbox() requiring self.
430+
// "new" name is for consistency with `update` function.
431+
432+
// Bootstrap the vdom with the dom's state. No need to do a full render, sinc executing
433+
// initial_orders should do this.
434+
self.data.main_el_vdom.replace(Some(self.bootstrap_vdom()));
435+
436+
// Setup routes handling. Update the state on page load, based
413437
// on the starting URL. Must be set up on the server as well.
414438
if let Some(routes) = *self.data.routes.borrow() {
415439
routing::setup_popstate_listener(
@@ -421,6 +445,19 @@ impl<Ms: Clone, Mdl, ElC: View<Ms> + 'static, GMs: 'static> App<Ms, Mdl, ElC, GM
421445
);
422446
routing::setup_link_listener(enclose!((self => s) move |msg| s.update(msg)), routes);
423447
}
448+
449+
// Since everything's been set up, run the initial update. This will trigger the initial
450+
// render as per normal for seed behavior. -- Executed here to ensure that all state has
451+
// been initialized with a bootstrap version. The bootstrap will be replaced after first
452+
// render.
453+
self.process_cmd_and_msg_queue(
454+
self.cfg
455+
.initial_orders
456+
.replace(None)
457+
.expect("initial_orders should be set in AppBuilder::finish")
458+
.effects,
459+
);
460+
424461
self
425462
}
426463

0 commit comments

Comments
 (0)