public static BoundAttributeDescriptionInfo From(BoundAttributeDescriptor boundAttribute, bool indexer, string?parentTagHelperTypeName) { if (boundAttribute is null) { throw new ArgumentNullException(nameof(boundAttribute)); } var returnTypeName = indexer ? boundAttribute.IndexerTypeName : boundAttribute.TypeName; var propertyName = boundAttribute.GetPropertyName(); if (parentTagHelperTypeName == null) { // The BoundAttributeDescriptor does not directly have the TagHelperTypeName information available. Because of this we need to resolve it from other parts of it. parentTagHelperTypeName = ResolveTagHelperTypeName(returnTypeName, propertyName, boundAttribute.DisplayName); } var descriptionInfo = new BoundAttributeDescriptionInfo( returnTypeName, parentTagHelperTypeName, propertyName, boundAttribute.Documentation); return(descriptionInfo); }
private List <TagHelperDescriptor> CreateComponentBindTagHelpers(ICollection <TagHelperDescriptor> tagHelpers) { var results = new List <TagHelperDescriptor>(); foreach (var tagHelper in tagHelpers) { if (!tagHelper.IsComponentTagHelper()) { continue; } // We want to create a 'bind' tag helper everywhere we see a pair of properties like `Foo`, `FooChanged` // where `FooChanged` is a delegate and `Foo` is not. // // The easiest way to figure this out without a lot of backtracking is to look for `FooChanged` and then // try to find a matching "Foo". // // We also look for a corresponding FooExpression attribute, though its presence is optional. for (var i = 0; i < tagHelper.BoundAttributes.Count; i++) { var changeAttribute = tagHelper.BoundAttributes[i]; if (!changeAttribute.Name.EndsWith("Changed", StringComparison.Ordinal) || // Allow the ValueChanged attribute to be a delegate or EventCallback<>. // // We assume that the Delegate or EventCallback<> has a matching type, and the C# compiler will help // you figure figure it out if you did it wrongly. (!changeAttribute.IsDelegateProperty() && !changeAttribute.IsEventCallbackProperty())) { continue; } BoundAttributeDescriptor valueAttribute = null; BoundAttributeDescriptor expressionAttribute = null; var valueAttributeName = changeAttribute.Name.Substring(0, changeAttribute.Name.Length - "Changed".Length); var expressionAttributeName = valueAttributeName + "Expression"; for (var j = 0; j < tagHelper.BoundAttributes.Count; j++) { if (tagHelper.BoundAttributes[j].Name == valueAttributeName) { valueAttribute = tagHelper.BoundAttributes[j]; } if (tagHelper.BoundAttributes[j].Name == expressionAttributeName) { expressionAttribute = tagHelper.BoundAttributes[j]; } if (valueAttribute != null && expressionAttribute != null) { // We found both, so we can stop looking now break; } } if (valueAttribute == null) { // No matching attribute found. continue; } var builder = TagHelperDescriptorBuilder.Create(ComponentMetadata.Bind.TagHelperKind, tagHelper.Name, tagHelper.AssemblyName); builder.DisplayName = tagHelper.DisplayName; builder.CaseSensitive = true; builder.Documentation = string.Format( CultureInfo.CurrentCulture, ComponentResources.BindTagHelper_Component_Documentation, valueAttribute.Name, changeAttribute.Name); builder.Metadata.Add(ComponentMetadata.SpecialKindKey, ComponentMetadata.Bind.TagHelperKind); builder.Metadata[TagHelperMetadata.Runtime.Name] = ComponentMetadata.Bind.RuntimeName; builder.Metadata[ComponentMetadata.Bind.ValueAttribute] = valueAttribute.Name; builder.Metadata[ComponentMetadata.Bind.ChangeAttribute] = changeAttribute.Name; if (expressionAttribute != null) { builder.Metadata[ComponentMetadata.Bind.ExpressionAttribute] = expressionAttribute.Name; } // WTE has a bug 15.7p1 where a Tag Helper without a display-name that looks like // a C# property will crash trying to create the toolips. builder.SetTypeName(tagHelper.GetTypeName()); // Match the component and attribute name builder.TagMatchingRule(rule => { rule.TagName = tagHelper.TagMatchingRules.Single().TagName; rule.Attribute(attribute => { attribute.Name = "@bind-" + valueAttribute.Name; attribute.NameComparisonMode = RequiredAttributeDescriptor.NameComparisonMode.FullMatch; attribute.Metadata[ComponentMetadata.Common.DirectiveAttribute] = bool.TrueString; }); }); builder.BindAttribute(attribute => { attribute.Metadata[ComponentMetadata.Common.DirectiveAttribute] = bool.TrueString; attribute.Documentation = string.Format( CultureInfo.CurrentCulture, ComponentResources.BindTagHelper_Component_Documentation, valueAttribute.Name, changeAttribute.Name); attribute.Name = "@bind-" + valueAttribute.Name; attribute.TypeName = changeAttribute.TypeName; attribute.IsEnum = valueAttribute.IsEnum; // WTE has a bug 15.7p1 where a Tag Helper without a display-name that looks like // a C# property will crash trying to create the toolips. attribute.SetPropertyName(valueAttribute.GetPropertyName()); }); if (tagHelper.IsComponentFullyQualifiedNameMatch()) { builder.Metadata[ComponentMetadata.Component.NameMatchKey] = ComponentMetadata.Component.FullyQualifiedNameMatch; } results.Add(builder.Build()); } } return(results); }
private List <TagHelperDescriptor> CreateComponentBindTagHelpers(ICollection <TagHelperDescriptor> tagHelpers) { var results = new List <TagHelperDescriptor>(); foreach (var tagHelper in tagHelpers) { if (!tagHelper.IsComponentTagHelper()) { continue; } // We want to create a 'bind' tag helper everywhere we see a pair of properties like `Foo`, `FooChanged` // where `FooChanged` is a delegate and `Foo` is not. // // The easiest way to figure this out without a lot of backtracking is to look for `FooChanged` and then // try to find a matching "Foo". for (var i = 0; i < tagHelper.BoundAttributes.Count; i++) { var changeAttribute = tagHelper.BoundAttributes[i]; if (!changeAttribute.Name.EndsWith("Changed") || !changeAttribute.IsDelegateProperty()) { continue; } BoundAttributeDescriptor valueAttribute = null; var valueAttributeName = changeAttribute.Name.Substring(0, changeAttribute.Name.Length - "Changed".Length); for (var j = 0; j < tagHelper.BoundAttributes.Count; j++) { if (tagHelper.BoundAttributes[j].Name == valueAttributeName && !tagHelper.BoundAttributes[j].IsDelegateProperty()) { valueAttribute = tagHelper.BoundAttributes[j]; break; } } if (valueAttribute == null) { // No matching attribute found. continue; } var builder = TagHelperDescriptorBuilder.Create(BlazorMetadata.Bind.TagHelperKind, tagHelper.Name, tagHelper.AssemblyName); builder.DisplayName = tagHelper.DisplayName; builder.Documentation = string.Format( Resources.BindTagHelper_Component_Documentation, valueAttribute.Name, changeAttribute.Name); builder.Metadata.Add(BlazorMetadata.SpecialKindKey, BlazorMetadata.Bind.TagHelperKind); builder.Metadata[TagHelperMetadata.Runtime.Name] = BlazorMetadata.Bind.RuntimeName; builder.Metadata[BlazorMetadata.Bind.ValueAttribute] = valueAttribute.Name; builder.Metadata[BlazorMetadata.Bind.ChangeAttribute] = changeAttribute.Name; // WTE has a bug 15.7p1 where a Tag Helper without a display-name that looks like // a C# property will crash trying to create the toolips. builder.SetTypeName(tagHelper.GetTypeName()); // Match the component and attribute name builder.TagMatchingRule(rule => { rule.TagName = tagHelper.TagMatchingRules.Single().TagName; rule.Attribute(attribute => { attribute.Name = "bind-" + valueAttribute.Name; attribute.NameComparisonMode = RequiredAttributeDescriptor.NameComparisonMode.FullMatch; }); }); builder.BindAttribute(attribute => { attribute.Documentation = string.Format( Resources.BindTagHelper_Component_Documentation, valueAttribute.Name, changeAttribute.Name); attribute.Name = "bind-" + valueAttribute.Name; attribute.TypeName = valueAttribute.TypeName; attribute.IsEnum = valueAttribute.IsEnum; // WTE has a bug 15.7p1 where a Tag Helper without a display-name that looks like // a C# property will crash trying to create the toolips. attribute.SetPropertyName(valueAttribute.GetPropertyName()); }); results.Add(builder.Build()); } } return(results); }