Esempio n. 1
0
            public override void VisitTagHelperBody(TagHelperBodyIntermediateNode node)
            {
                // Wrap the component's children in a ChildContent node if we have some significant
                // content.
                if (node.Children.Count == 0)
                {
                    return;
                }

                // If we get a single HTML content node containing only whitespace,
                // then this is probably a tag that looks like '<MyComponent>  </MyComponent>
                //
                // We don't want to create a child content for this case, because it can conflict
                // with a child content that's set via an attribute. We don't want the formatting
                // of insignificant whitespace to be annoying when setting attributes directly.
                if (node.Children.Count == 1 && IsIgnorableWhitespace(node.Children[0]))
                {
                    return;
                }

                // From here we fork and behave differently based on whether the component's child content is
                // implicit or explicit.
                //
                // Explicit child content will look like: <MyComponent><ChildContent><div>...</div></ChildContent></MyComponent>
                // compared with implicit: <MyComponent><div></div></MyComponent>
                //
                // Using implicit child content:
                // 1. All content is grouped into a single child content lambda, and assigned to the property 'ChildContent'
                //
                // Using explicit child content:
                // 1. All content must be contained within 'child content' elements that are direct children
                // 2. Whitespace outside of 'child content' elements will be ignored (not an error)
                // 3. Non-whitespace outside of 'child content' elements will cause an error
                // 4. All 'child content' elements must match parameters on the component (exception for ChildContent,
                //    which is always allowed.
                // 5. Each 'child content' element will generate its own lambda, and be assigned to the property
                //    that matches the element name.
                if (!node.Children.OfType <TagHelperIntermediateNode>().Any(t => t.TagHelpers.Any(th => th.IsChildContentTagHelper())))
                {
                    // This node has implicit child content. It may or may not have an attribute that matches.
                    var attribute = _component.Component.BoundAttributes
                                    .Where(a => string.Equals(a.Name, ComponentsApi.RenderTreeBuilder.ChildContent, StringComparison.Ordinal))
                                    .FirstOrDefault();
                    _children.Add(RewriteChildContent(attribute, node.Source, node.Children));
                    return;
                }

                // OK this node has explicit child content, we can rewrite it by visiting each node
                // in sequence, since we:
                // a) need to rewrite each child content element
                // b) any significant content outside of a child content is an error
                for (var i = 0; i < node.Children.Count; i++)
                {
                    var child = node.Children[i];
                    if (IsIgnorableWhitespace(child))
                    {
                        continue;
                    }

                    if (child is TagHelperIntermediateNode tagHelperNode &&
                        tagHelperNode.TagHelpers.Any(th => th.IsChildContentTagHelper()))
                    {
                        // This is a child content element
                        var attribute = _component.Component.BoundAttributes
                                        .Where(a => string.Equals(a.Name, tagHelperNode.TagName, StringComparison.Ordinal))
                                        .FirstOrDefault();
                        _children.Add(RewriteChildContent(attribute, child.Source, child.Children));
                        continue;
                    }

                    // If we get here then this is significant content inside a component with explicit child content.
                    child.Diagnostics.Add(ComponentDiagnosticFactory.Create_ChildContentMixedWithExplicitChildContent(child.Source, _component));
                    _children.Add(child);
                }

                bool IsIgnorableWhitespace(IntermediateNode n)
                {
                    if (n is HtmlContentIntermediateNode html &&
                        html.Children.Count == 1 &&
                        html.Children[0] is IntermediateToken token &&
                        string.IsNullOrWhiteSpace(token.Content))
                    {
                        return(true);
                    }

                    return(false);
                }
            }