mirror of
https://github.com/DioxusLabs/dioxus
synced 2025-02-18 06:38:26 +00:00
Fix inserting attributes after expanding dynamic nodes (#3094)
This commit is contained in:
parent
3f8c32d1ff
commit
ef436e4ed0
4 changed files with 101 additions and 83 deletions
|
@ -592,6 +592,12 @@ impl VNode {
|
||||||
|
|
||||||
// If this is an element, load in all of the placeholder or dynamic content under this root element too
|
// If this is an element, load in all of the placeholder or dynamic content under this root element too
|
||||||
if matches!(root, TemplateNode::Element { .. }) {
|
if matches!(root, TemplateNode::Element { .. }) {
|
||||||
|
// !!VERY IMPORTANT!!
|
||||||
|
// Write out all attributes before we load the children. Loading the children will change paths we rely on
|
||||||
|
// to assign ids to elements with dynamic attributes
|
||||||
|
if let Some(to) = to.as_deref_mut() {
|
||||||
|
self.write_attrs(mount, &mut attrs, root_idx as u8, dom, to);
|
||||||
|
}
|
||||||
// This operation relies on the fact that the root node is the top node on the stack so we need to do it here
|
// This operation relies on the fact that the root node is the top node on the stack so we need to do it here
|
||||||
self.load_placeholders(
|
self.load_placeholders(
|
||||||
mount,
|
mount,
|
||||||
|
@ -600,10 +606,6 @@ impl VNode {
|
||||||
dom,
|
dom,
|
||||||
to.as_deref_mut(),
|
to.as_deref_mut(),
|
||||||
);
|
);
|
||||||
// Now write out any attributes we need
|
|
||||||
if let Some(to) = to.as_deref_mut() {
|
|
||||||
self.write_attrs(mount, &mut attrs, root_idx as u8, dom, to);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This creates one node on the stack
|
// This creates one node on the stack
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use dioxus::dioxus_core::{ElementId, Mutation};
|
use dioxus::dioxus_core::{ElementId, Mutation};
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
fn basic_syntax_is_a_template() -> Element {
|
fn basic_syntax_is_a_template() -> Element {
|
||||||
let asd = 123;
|
let asd = 123;
|
||||||
|
@ -39,8 +40,6 @@ fn dual_stream() {
|
||||||
assert_eq!(edits.edits, {
|
assert_eq!(edits.edits, {
|
||||||
[
|
[
|
||||||
LoadTemplate { index: 0, id: ElementId(1) },
|
LoadTemplate { index: 0, id: ElementId(1) },
|
||||||
CreateTextNode { value: "123".to_string(), id: ElementId(2) },
|
|
||||||
ReplacePlaceholder { path: &[0, 0], m: 1 },
|
|
||||||
SetAttribute {
|
SetAttribute {
|
||||||
name: "class",
|
name: "class",
|
||||||
value: "asd 123 123 ".into_value(),
|
value: "asd 123 123 ".into_value(),
|
||||||
|
@ -48,6 +47,8 @@ fn dual_stream() {
|
||||||
ns: None,
|
ns: None,
|
||||||
},
|
},
|
||||||
NewEventListener { name: "click".to_string(), id: ElementId(1) },
|
NewEventListener { name: "click".to_string(), id: ElementId(1) },
|
||||||
|
CreateTextNode { value: "123".to_string(), id: ElementId(2) },
|
||||||
|
ReplacePlaceholder { path: &[0, 0], m: 1 },
|
||||||
AppendChildren { id: ElementId(0), m: 1 },
|
AppendChildren { id: ElementId(0), m: 1 },
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,16 +2,24 @@
|
||||||
|
|
||||||
use dioxus::dioxus_core::Mutation::*;
|
use dioxus::dioxus_core::Mutation::*;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_core::ElementId;
|
use dioxus_core::{AttributeValue, ElementId};
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
/// Should push the text node onto the stack and modify it
|
/// Should push the text node onto the stack and modify it
|
||||||
|
/// Regression test for https://github.com/DioxusLabs/dioxus/issues/2809 and https://github.com/DioxusLabs/dioxus/issues/3055
|
||||||
#[test]
|
#[test]
|
||||||
fn many_roots() {
|
fn many_roots() {
|
||||||
fn app() -> Element {
|
fn app() -> Element {
|
||||||
|
let width = "100%";
|
||||||
rsx! {
|
rsx! {
|
||||||
div {
|
div {
|
||||||
MyNav {}
|
MyNav {}
|
||||||
MyOutlet {}
|
MyOutlet {}
|
||||||
|
div {
|
||||||
|
// We need to make sure that dynamic attributes are set before the nodes before them are expanded
|
||||||
|
// If they are set after, then the paths are incorrect
|
||||||
|
width,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,13 +46,21 @@ fn many_roots() {
|
||||||
[
|
[
|
||||||
// load the div {} container
|
// load the div {} container
|
||||||
LoadTemplate { index: 0, id: ElementId(1) },
|
LoadTemplate { index: 0, id: ElementId(1) },
|
||||||
// Load myoutlet first
|
// Set the width attribute first
|
||||||
LoadTemplate { index: 0, id: ElementId(2) },
|
AssignId { path: &[2], id: ElementId(2,) },
|
||||||
ReplacePlaceholder { path: &[1], m: 1 },
|
SetAttribute {
|
||||||
// Then the MyNav
|
name: "width",
|
||||||
|
ns: Some("style",),
|
||||||
|
value: AttributeValue::Text("100%".to_string()),
|
||||||
|
id: ElementId(2,),
|
||||||
|
},
|
||||||
|
// Load MyOutlet next
|
||||||
LoadTemplate { index: 0, id: ElementId(3) },
|
LoadTemplate { index: 0, id: ElementId(3) },
|
||||||
LoadTemplate { index: 1, id: ElementId(4) },
|
ReplacePlaceholder { path: &[1], m: 1 },
|
||||||
LoadTemplate { index: 2, id: ElementId(5) },
|
// Then MyNav
|
||||||
|
LoadTemplate { index: 0, id: ElementId(4) },
|
||||||
|
LoadTemplate { index: 1, id: ElementId(5) },
|
||||||
|
LoadTemplate { index: 2, id: ElementId(6) },
|
||||||
ReplacePlaceholder { path: &[0], m: 3 },
|
ReplacePlaceholder { path: &[0], m: 3 },
|
||||||
// Then mount the div to the dom
|
// Then mount the div to the dom
|
||||||
AppendChildren { m: 1, id: ElementId(0) },
|
AppendChildren { m: 1, id: ElementId(0) },
|
||||||
|
|
|
@ -623,37 +623,37 @@ fn nested_suspense_resolves_client() {
|
||||||
|
|
||||||
// Load the title
|
// Load the title
|
||||||
LoadTemplate { index: 0, id: ElementId(2,) },
|
LoadTemplate { index: 0, id: ElementId(2,) },
|
||||||
CreateTextNode { value: "The robot says hello world".to_string(), id: ElementId(4,) },
|
|
||||||
ReplacePlaceholder { path: &[0,], m: 1 },
|
|
||||||
SetAttribute {
|
SetAttribute {
|
||||||
name: "id",
|
name: "id",
|
||||||
ns: None,
|
ns: None,
|
||||||
value: AttributeValue::Text("title-0".to_string()),
|
value: AttributeValue::Text("title-0".to_string()),
|
||||||
id: ElementId(2,),
|
id: ElementId(2,),
|
||||||
},
|
},
|
||||||
|
CreateTextNode { value: "The robot says hello world".to_string(), id: ElementId(4,) },
|
||||||
|
ReplacePlaceholder { path: &[0,], m: 1 },
|
||||||
|
|
||||||
// Then load the body
|
// Then load the body
|
||||||
LoadTemplate { index: 1, id: ElementId(5,) },
|
LoadTemplate { index: 1, id: ElementId(5,) },
|
||||||
CreateTextNode { value: "The robot becomes sentient and says hello world".to_string(), id: ElementId(6,) },
|
|
||||||
ReplacePlaceholder { path: &[0,], m: 1 },
|
|
||||||
SetAttribute {
|
SetAttribute {
|
||||||
name: "id",
|
name: "id",
|
||||||
ns: None,
|
ns: None,
|
||||||
value: AttributeValue::Text("body-0".to_string()),
|
value: AttributeValue::Text("body-0".to_string()),
|
||||||
id: ElementId(5,),
|
id: ElementId(5,),
|
||||||
},
|
},
|
||||||
|
CreateTextNode { value: "The robot becomes sentient and says hello world".to_string(), id: ElementId(6,) },
|
||||||
|
ReplacePlaceholder { path: &[0,], m: 1 },
|
||||||
|
|
||||||
// Then load the suspended children
|
// Then load the suspended children
|
||||||
LoadTemplate { index: 2, id: ElementId(7,) },
|
LoadTemplate { index: 2, id: ElementId(7,) },
|
||||||
CreateTextNode { value: "Loading 1...".to_string(), id: ElementId(8,) },
|
|
||||||
CreateTextNode { value: "Loading 2...".to_string(), id: ElementId(9,) },
|
|
||||||
ReplacePlaceholder { path: &[0,], m: 2 },
|
|
||||||
SetAttribute {
|
SetAttribute {
|
||||||
name: "id",
|
name: "id",
|
||||||
ns: None,
|
ns: None,
|
||||||
value: AttributeValue::Text("children-0".to_string()),
|
value: AttributeValue::Text("children-0".to_string()),
|
||||||
id: ElementId(7,),
|
id: ElementId(7,),
|
||||||
},
|
},
|
||||||
|
CreateTextNode { value: "Loading 1...".to_string(), id: ElementId(8,) },
|
||||||
|
CreateTextNode { value: "Loading 2...".to_string(), id: ElementId(9,) },
|
||||||
|
ReplacePlaceholder { path: &[0,], m: 2 },
|
||||||
|
|
||||||
// Finally replace the loading placeholder in the body with the resolved children
|
// Finally replace the loading placeholder in the body with the resolved children
|
||||||
ReplaceWith { id: ElementId(3,), m: 3 },
|
ReplaceWith { id: ElementId(3,), m: 3 },
|
||||||
|
@ -693,13 +693,6 @@ fn nested_suspense_resolves_client() {
|
||||||
8,
|
8,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
CreateTextNode { value: "The world says hello back".to_string(), id: ElementId(10,) },
|
|
||||||
ReplacePlaceholder {
|
|
||||||
path: &[
|
|
||||||
0,
|
|
||||||
],
|
|
||||||
m: 1,
|
|
||||||
},
|
|
||||||
SetAttribute {
|
SetAttribute {
|
||||||
name: "id",
|
name: "id",
|
||||||
ns: None,
|
ns: None,
|
||||||
|
@ -708,13 +701,27 @@ fn nested_suspense_resolves_client() {
|
||||||
8,
|
8,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
CreateTextNode { value: "The world says hello back".to_string(), id: ElementId(10,) },
|
||||||
|
ReplacePlaceholder {
|
||||||
|
path: &[
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
m: 1,
|
||||||
|
},
|
||||||
LoadTemplate {
|
LoadTemplate {
|
||||||
|
|
||||||
index: 1,
|
index: 1,
|
||||||
id: ElementId(
|
id: ElementId(
|
||||||
11,
|
11,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
SetAttribute {
|
||||||
|
name: "id",
|
||||||
|
ns: None,
|
||||||
|
value: AttributeValue::Text("body-1".to_string()),
|
||||||
|
id: ElementId(
|
||||||
|
11,
|
||||||
|
),
|
||||||
|
},
|
||||||
CreateTextNode { value: "In a stunning turn of events, the world collectively unites and says hello back".to_string(), id: ElementId(12,) },
|
CreateTextNode { value: "In a stunning turn of events, the world collectively unites and says hello back".to_string(), id: ElementId(12,) },
|
||||||
ReplacePlaceholder {
|
ReplacePlaceholder {
|
||||||
path: &[
|
path: &[
|
||||||
|
@ -722,16 +729,16 @@ fn nested_suspense_resolves_client() {
|
||||||
],
|
],
|
||||||
m: 1,
|
m: 1,
|
||||||
},
|
},
|
||||||
|
LoadTemplate {
|
||||||
|
index: 2,
|
||||||
|
id: ElementId(
|
||||||
|
13,
|
||||||
|
),
|
||||||
|
},
|
||||||
SetAttribute {
|
SetAttribute {
|
||||||
name: "id",
|
name: "id",
|
||||||
ns: None,
|
ns: None,
|
||||||
value: AttributeValue::Text("body-1".to_string()),
|
value: AttributeValue::Text("children-1".to_string()),
|
||||||
id: ElementId(
|
|
||||||
11,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
LoadTemplate {
|
|
||||||
index: 2,
|
|
||||||
id: ElementId(
|
id: ElementId(
|
||||||
13,
|
13,
|
||||||
),
|
),
|
||||||
|
@ -743,14 +750,6 @@ fn nested_suspense_resolves_client() {
|
||||||
],
|
],
|
||||||
m: 1,
|
m: 1,
|
||||||
},
|
},
|
||||||
SetAttribute {
|
|
||||||
name: "id",
|
|
||||||
ns: None,
|
|
||||||
value: AttributeValue::Text("children-1".to_string()),
|
|
||||||
id: ElementId(
|
|
||||||
13,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
ReplaceWith {
|
ReplaceWith {
|
||||||
id: ElementId(
|
id: ElementId(
|
||||||
3,
|
3,
|
||||||
|
@ -776,13 +775,6 @@ fn nested_suspense_resolves_client() {
|
||||||
9,
|
9,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
CreateTextNode { value: "Goodbye Robot".to_string(), id: ElementId(15,) },
|
|
||||||
ReplacePlaceholder {
|
|
||||||
path: &[
|
|
||||||
0,
|
|
||||||
],
|
|
||||||
m: 1,
|
|
||||||
},
|
|
||||||
SetAttribute {
|
SetAttribute {
|
||||||
name: "id",
|
name: "id",
|
||||||
ns: None,
|
ns: None,
|
||||||
|
@ -791,12 +783,27 @@ fn nested_suspense_resolves_client() {
|
||||||
9,
|
9,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
CreateTextNode { value: "Goodbye Robot".to_string(), id: ElementId(15,) },
|
||||||
|
ReplacePlaceholder {
|
||||||
|
path: &[
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
m: 1,
|
||||||
|
},
|
||||||
LoadTemplate {
|
LoadTemplate {
|
||||||
index: 1,
|
index: 1,
|
||||||
id: ElementId(
|
id: ElementId(
|
||||||
16,
|
16,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
SetAttribute {
|
||||||
|
name: "id",
|
||||||
|
ns: None,
|
||||||
|
value: AttributeValue::Text("body-2".to_string()),
|
||||||
|
id: ElementId(
|
||||||
|
16,
|
||||||
|
),
|
||||||
|
},
|
||||||
CreateTextNode { value: "The robot says goodbye".to_string(), id: ElementId(17,) },
|
CreateTextNode { value: "The robot says goodbye".to_string(), id: ElementId(17,) },
|
||||||
ReplacePlaceholder {
|
ReplacePlaceholder {
|
||||||
path: &[
|
path: &[
|
||||||
|
@ -804,14 +811,6 @@ fn nested_suspense_resolves_client() {
|
||||||
],
|
],
|
||||||
m: 1,
|
m: 1,
|
||||||
},
|
},
|
||||||
SetAttribute {
|
|
||||||
name: "id",
|
|
||||||
ns: None,
|
|
||||||
value: AttributeValue::Text("body-2".to_string()),
|
|
||||||
id: ElementId(
|
|
||||||
16,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
LoadTemplate {
|
LoadTemplate {
|
||||||
|
|
||||||
index: 2,
|
index: 2,
|
||||||
|
@ -819,9 +818,6 @@ fn nested_suspense_resolves_client() {
|
||||||
18,
|
18,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
// Create a placeholder for the resolved children
|
|
||||||
CreateTextNode { value: "Loading 3...".to_string(), id: ElementId(19,) },
|
|
||||||
ReplacePlaceholder { path: &[0,], m: 1 },
|
|
||||||
SetAttribute {
|
SetAttribute {
|
||||||
name: "id",
|
name: "id",
|
||||||
ns: None,
|
ns: None,
|
||||||
|
@ -830,6 +826,9 @@ fn nested_suspense_resolves_client() {
|
||||||
18,
|
18,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
// Create a placeholder for the resolved children
|
||||||
|
CreateTextNode { value: "Loading 3...".to_string(), id: ElementId(19,) },
|
||||||
|
ReplacePlaceholder { path: &[0,], m: 1 },
|
||||||
|
|
||||||
// Replace the loading placeholder with the resolved children
|
// Replace the loading placeholder with the resolved children
|
||||||
ReplaceWith {
|
ReplaceWith {
|
||||||
|
@ -864,13 +863,6 @@ fn nested_suspense_resolves_client() {
|
||||||
19,
|
19,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
CreateTextNode { value: "Goodbye Robot again".to_string(), id: ElementId(20,) },
|
|
||||||
ReplacePlaceholder {
|
|
||||||
path: &[
|
|
||||||
0,
|
|
||||||
],
|
|
||||||
m: 1,
|
|
||||||
},
|
|
||||||
SetAttribute {
|
SetAttribute {
|
||||||
name: "id",
|
name: "id",
|
||||||
ns: None,
|
ns: None,
|
||||||
|
@ -879,12 +871,27 @@ fn nested_suspense_resolves_client() {
|
||||||
19,
|
19,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
CreateTextNode { value: "Goodbye Robot again".to_string(), id: ElementId(20,) },
|
||||||
|
ReplacePlaceholder {
|
||||||
|
path: &[
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
m: 1,
|
||||||
|
},
|
||||||
LoadTemplate {
|
LoadTemplate {
|
||||||
index: 1,
|
index: 1,
|
||||||
id: ElementId(
|
id: ElementId(
|
||||||
21,
|
21,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
SetAttribute {
|
||||||
|
name: "id",
|
||||||
|
ns: None,
|
||||||
|
value: AttributeValue::Text("body-3".to_string()),
|
||||||
|
id: ElementId(
|
||||||
|
21,
|
||||||
|
),
|
||||||
|
},
|
||||||
CreateTextNode { value: "The robot says goodbye again".to_string(), id: ElementId(22,) },
|
CreateTextNode { value: "The robot says goodbye again".to_string(), id: ElementId(22,) },
|
||||||
ReplacePlaceholder {
|
ReplacePlaceholder {
|
||||||
path: &[
|
path: &[
|
||||||
|
@ -892,16 +899,16 @@ fn nested_suspense_resolves_client() {
|
||||||
],
|
],
|
||||||
m: 1,
|
m: 1,
|
||||||
},
|
},
|
||||||
|
LoadTemplate {
|
||||||
|
index: 2,
|
||||||
|
id: ElementId(
|
||||||
|
23,
|
||||||
|
),
|
||||||
|
},
|
||||||
SetAttribute {
|
SetAttribute {
|
||||||
name: "id",
|
name: "id",
|
||||||
ns: None,
|
ns: None,
|
||||||
value: AttributeValue::Text("body-3".to_string()),
|
value: AttributeValue::Text("children-3".to_string()),
|
||||||
id: ElementId(
|
|
||||||
21,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
LoadTemplate {
|
|
||||||
index: 2,
|
|
||||||
id: ElementId(
|
id: ElementId(
|
||||||
23,
|
23,
|
||||||
),
|
),
|
||||||
|
@ -913,14 +920,6 @@ fn nested_suspense_resolves_client() {
|
||||||
],
|
],
|
||||||
m: 1,
|
m: 1,
|
||||||
},
|
},
|
||||||
SetAttribute {
|
|
||||||
name: "id",
|
|
||||||
ns: None,
|
|
||||||
value: AttributeValue::Text("children-3".to_string()),
|
|
||||||
id: ElementId(
|
|
||||||
23,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
ReplaceWith {
|
ReplaceWith {
|
||||||
id: ElementId(
|
id: ElementId(
|
||||||
3,
|
3,
|
||||||
|
|
Loading…
Add table
Reference in a new issue