From 1ab04f97becbcef478331046bd89449b31a215c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Standa=20Luke=C5=A1?= Date: Tue, 31 Oct 2023 12:02:16 +0100 Subject: [PATCH] Fix compilation of html attributes with prefixes Resolves #1726 --- .../ControlTree/ControlTreeResolverBase.cs | 13 ++++++------- .../Dothtml/Parser/DothtmlAttributeNode.cs | 11 +++-------- .../DefaultControlTreeResolverTests.cs | 16 ++++++++++++++++ 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/Framework/Framework/Compilation/ControlTree/ControlTreeResolverBase.cs b/src/Framework/Framework/Compilation/ControlTree/ControlTreeResolverBase.cs index 0ff543d36f..812a16ba84 100644 --- a/src/Framework/Framework/Compilation/ControlTree/ControlTreeResolverBase.cs +++ b/src/Framework/Framework/Compilation/ControlTree/ControlTreeResolverBase.cs @@ -263,7 +263,7 @@ private IAbstractControl ProcessObjectElement(DothtmlElementNode element, IDataC control.ConstructorParameters = constructorParameters; // resolve data context - var dataContextAttribute = element.Attributes.FirstOrDefault(a => a.AttributeName == "DataContext"); + var dataContextAttribute = element.Attributes.FirstOrDefault(a => a.AttributeFullName == "DataContext"); if (dataContextAttribute != null) { ProcessAttribute(DotvvmBindableObject.DataContextProperty, dataContextAttribute, control, dataContext); @@ -287,7 +287,7 @@ private IAbstractControl ProcessObjectElement(DothtmlElementNode element, IDataC .AddError($"The control '{controlMetadata.Type.CSharpName}' requires a DataContext of type '{controlMetadata.DataContextConstraint.CSharpFullName}'!"); } - ProcessAttributeProperties(control, element.Attributes.Where(a => a.AttributeName != "DataContext").ToArray(), dataContext!); + ProcessAttributeProperties(control, element.Attributes.Where(a => a.AttributeFullName != "DataContext").ToArray(), dataContext!); // process control contents ProcessControlContent(control, element.Content); @@ -324,21 +324,20 @@ public IAbstractBinding ProcessBinding(DothtmlBindingNode node, IDataContextStac private void ProcessAttributeProperties(IAbstractControl control, DothtmlAttributeNode[] nodes, IDataContextStack dataContext) { var doneAttributes = new HashSet(); - string getName(DothtmlAttributeNode n) => n.AttributePrefix == null ? n.AttributeName : n.AttributePrefix + ":" + n.AttributeName; void resolveAttribute(DothtmlAttributeNode attribute) { - var name = getName(attribute); + var name = attribute.AttributeFullName; if (!doneAttributes.Add(attribute)) return; var property = controlResolver.FindProperty(control.Metadata, name, MappingMode.Attribute); if (property == null) { - attribute.AddError($"The control '{control.Metadata.Type}' does not have a property '{attribute.AttributeName}' and does not allow HTML attributes!"); + attribute.AddError($"The control '{control.Metadata.Type}' does not have a property '{attribute.AttributeFullName}' and does not allow HTML attributes!"); } else { var dependsOn = property.DataContextChangeAttributes.SelectMany(c => c.PropertyDependsOn); - foreach (var p in dependsOn.SelectMany(t => nodes.Where(n => t == getName(n)))) + foreach (var p in dependsOn.SelectMany(t => nodes.Where(n => t == n.AttributeFullName))) resolveAttribute(p); ProcessAttribute(property, attribute, control, dataContext); } @@ -454,7 +453,7 @@ attribute.ValueNode is DothtmlValueBindingNode binding && { var pGroup = groupedProperty.PropertyGroup; var name = groupedProperty.GroupMemberName; - var prefix = attribute.AttributeName.Substring(0, attribute.AttributeName.Length - name.Length); + var prefix = attribute.AttributeFullName.Substring(0, attribute.AttributeFullName.Length - name.Length); // If the HTML attribute is used with a prefix such as `Item`, it might be clearer if the first character is uppercased // e.g. ItemClass reads better than Itemclass // we supress the warning in such case diff --git a/src/Framework/Framework/Compilation/Parser/Dothtml/Parser/DothtmlAttributeNode.cs b/src/Framework/Framework/Compilation/Parser/Dothtml/Parser/DothtmlAttributeNode.cs index 90569f2401..5a3ef2d6e5 100644 --- a/src/Framework/Framework/Compilation/Parser/Dothtml/Parser/DothtmlAttributeNode.cs +++ b/src/Framework/Framework/Compilation/Parser/Dothtml/Parser/DothtmlAttributeNode.cs @@ -10,18 +10,13 @@ public sealed class DothtmlAttributeNode : DothtmlNode { #region debugger display [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private string debuggerDisplay - { - get - { - return (string.IsNullOrWhiteSpace(AttributePrefix) ? "" : AttributePrefix + ":") + AttributeName - + (ValueNode == null ? "" : "="); - } - } + private string debuggerDisplay => + AttributeFullName + (ValueNode == null ? "" : "="); #endregion public string? AttributePrefix => AttributePrefixNode?.Text; public string AttributeName => AttributeNameNode.Text; + public string AttributeFullName => AttributePrefix == null ? AttributeName : AttributePrefix + ":" + AttributeName; public DothtmlValueNode? ValueNode { get; set; } diff --git a/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/DefaultControlTreeResolverTests.cs b/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/DefaultControlTreeResolverTests.cs index 95b960469f..7ebdd3bf3c 100644 --- a/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/DefaultControlTreeResolverTests.cs +++ b/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/DefaultControlTreeResolverTests.cs @@ -828,6 +828,22 @@ public void DefaultViewCompiler_DifferentControlAlternativeNames() Assert.AreEqual(typeof(ControlWithAlternativeNames), nonLiterals[2].Metadata.Type); Assert.AreEqual(2, nonLiterals[0].Metadata.AlternativeNames!.Count); } + + [TestMethod] + public void DefaultViewCompiler_XmlnsAttribute() + { + var root = ParseSource(""" + @viewModel System.String + + """); + var element = root.Content.Single(c => c.Metadata.Type == typeof(HtmlGenericControl)); + Assert.AreEqual("svg", element.ConstructorParameters[0]); + Assert.AreEqual("32", element.GetHtmlAttribute("width").CastTo().Value); + Assert.AreEqual("32", element.GetHtmlAttribute("height").CastTo().Value); + Assert.AreEqual("http://www.w3.org/2000/svg", element.GetHtmlAttribute("xmlns").CastTo().Value); + Assert.AreEqual("http://www.w3.org/1999/xlink", element.GetHtmlAttribute("xmlns:xlink").CastTo().Value); + Assert.AreEqual("123", element.GetHtmlAttribute("another:attribute").CastTo().Value); + } } }