Skip to content

Commit 404fcfc

Browse files
committed
Allow the virtual DOM to take over the original contents of its mount point.
1 parent 356f23c commit 404fcfc

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
@@ -365,49 +365,73 @@ impl<Ms, Mdl, ElC: View<Ms> + 'static, GMs: 'static> App<Ms, Mdl, ElC, GMs> {
365365
}
366366
}
367367

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

386+
// Setup listeners
385387
self.setup_window_listeners();
386388
patch::setup_input_listeners(&mut new);
387389
patch::attach_listeners(&mut new, &self.mailbox());
388390

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

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

0 commit comments

Comments
 (0)