Skip to content
This repository was archived by the owner on Feb 23, 2021. It is now read-only.

Commit a3fd998

Browse files
committed
Fully support ViewComponentTagHelpers in design time
1 parent 24a9c54 commit a3fd998

1 file changed

Lines changed: 114 additions & 1 deletion

File tree

src/Microsoft.AspNetCore.Razor.Design/Internal/AssemblyTagHelperDescriptorResolver.cs

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
using System.Globalization;
77
using System.Linq;
88
using System.Reflection;
9+
using System.Threading.Tasks;
910
using Microsoft.AspNetCore.Razor.Compilation.TagHelpers;
1011
using Microsoft.AspNetCore.Razor.Runtime.TagHelpers;
12+
using Microsoft.AspNetCore.Razor.TagHelpers;
1113
using Microsoft.Extensions.DependencyInjection;
1214

1315
namespace Microsoft.AspNetCore.Razor.Design.Internal
@@ -17,6 +19,7 @@ public class AssemblyTagHelperDescriptorResolver
1719
private const string TypeFullName = "Microsoft.AspNetCore.Mvc.DesignTimeMvcServiceCollectionProvider";
1820
private const string MvcAssemblyName = "Microsoft.AspNetCore.Mvc";
1921
private const string MethodName = "PopulateServiceCollection";
22+
private const string ViewComponentNameKey = "ViewComponentName";
2023

2124
private readonly TagHelperDescriptorFactory _tagHelperDescriptorFactory;
2225
private readonly TagHelperTypeResolver _tagHelperTypeResolver;
@@ -87,7 +90,14 @@ public IEnumerable<TagHelperDescriptor> Resolve(string assemblyName, ErrorSink e
8790
var context = new TagHelperDescriptorResolutionContext(directiveDescriptors, errorSink);
8891
var descriptors = resolver.Resolve(context);
8992

90-
return descriptors;
93+
// Temporary workaround to make design time for ViewComponent tag helpers work without needing a VS update.
94+
// This will be removed in a future version.
95+
var vcthDescriptors = descriptors.Where(d => d.PropertyBag.ContainsKey(ViewComponentNameKey));
96+
var fakeVcthDescriptors = GetFakeDescriptors(vcthDescriptors);
97+
98+
var finalDescriptors = descriptors.Except(vcthDescriptors).Union(fakeVcthDescriptors);
99+
100+
return finalDescriptors;
91101
}
92102

93103
private void EnsureMvcInitialized()
@@ -118,5 +128,108 @@ private void EnsureMvcInitialized()
118128
_populateMethodDelegate = (Action<IServiceCollection, string>) populateMethod
119129
?.CreateDelegate(typeof(Action<IServiceCollection, string>));
120130
}
131+
132+
private IEnumerable<TagHelperDescriptor> GetFakeDescriptors(IEnumerable<TagHelperDescriptor> vcthDescriptors)
133+
{
134+
/*
135+
* Since there are no corresponding tag helper types represented by vcthDescriptors,
136+
* We create fake descriptors with the same attributes and their types using the types
137+
* ViewComponentTagHelperDesignTimeType and ViewComponentTagHelperDesignTimeHelperType which mimic the
138+
* same design time experience as the original tag helper descriptors.
139+
*/
140+
var fakeDescriptors = new List<TagHelperDescriptor>();
141+
foreach (var descriptor in vcthDescriptors)
142+
{
143+
var fakeType = typeof(ViewComponentTagHelperDesignTimeType).FullName;
144+
var fakeDescriptor = new TagHelperDescriptor(descriptor);
145+
fakeDescriptor.TypeName = fakeType;
146+
147+
var fakeAttributes = new List<TagHelperAttributeDescriptor>();
148+
fakeDescriptor.Attributes = fakeAttributes;
149+
150+
var fakeHelperType = typeof(ViewComponentTagHelperDesignTimeHelperType).FullName;
151+
var fakeHelperDescriptor = new TagHelperDescriptor(descriptor);
152+
fakeHelperDescriptor.TypeName = fakeHelperType;
153+
154+
var fakeHelperAttributes = new List<TagHelperAttributeDescriptor>();
155+
fakeHelperDescriptor.Attributes = fakeHelperAttributes;
156+
157+
foreach (var attribute in descriptor.Attributes)
158+
{
159+
/*
160+
* We use TagHelperAttributeDescriptor.PropertyName to inject code into the generated code for the cshtml.
161+
* For example consider FooProperty of type string in ExampleTagHelper and ExampleTagHelper2,
162+
*
163+
* __SomeNamespace_ExampleTagHelper.FooProperty = "bar";
164+
* __SomeNamespace_ExampleTagHelper2.FooProperty = __SomeNamespace_ExampleTagHelper.FooProperty;
165+
*
166+
* "FooProperty" in the above code will be replaced with the specified value as below,
167+
*
168+
* __Microsoft_AspNetCore_Razor_Design_Internal_ViewComponentTagHelperDesignTimeType.PlaceholderProperty = null;
169+
* Microsoft.AspNetCore.Razor.Design.Internal.ViewComponentTagHelperDesignTimeType.ActionProperty = () => {
170+
* System.String __obj = default(System.String);
171+
* Microsoft.AspNetCore.Razor.Design.Internal.ViewComponentTagHelperDesignTimeType.PlaceholderMethod(__obj); ## This handles "__obj is assigned but never used" warning.
172+
* __obj = "bar";
173+
* __Microsoft_AspNetCore_Razor_Design_Internal_ViewComponentTagHelperDesignTimeHelperType.PlaceholderProperty = null;
174+
* };// ## The comment(//) here is needed to ignore the code that replaces "ExampleTagHelper.FooProperty".
175+
*/
176+
const string fakeVariableName = "__obj";
177+
var summary = $"{attribute.TypeName}: {descriptor.TypeName}.{attribute.PropertyName}";
178+
var fakeAttributePropertyName = $"{nameof(ViewComponentTagHelperDesignTimeType.PlaceholderProperty)} = null; " +
179+
$"{fakeType}.{nameof(ViewComponentTagHelperDesignTimeType.ActionProperty)} = () => {{ " +
180+
$"{attribute.TypeName} {fakeVariableName} = default({attribute.TypeName}); " +
181+
$"{fakeType}.{nameof(ViewComponentTagHelperDesignTimeType.PlaceholderMethod)}({fakeVariableName}); {fakeVariableName} ";
182+
183+
var fakeAttribute = new TagHelperAttributeDescriptor()
184+
{
185+
IsIndexer = attribute.IsIndexer,
186+
IsEnum = attribute.IsEnum,
187+
IsStringProperty = attribute.IsStringProperty,
188+
Name = attribute.Name,
189+
PropertyName = fakeAttributePropertyName,
190+
TypeName = attribute.TypeName,
191+
DesignTimeDescriptor = new TagHelperAttributeDesignTimeDescriptor
192+
{
193+
Summary = attribute.DesignTimeDescriptor?.Summary ?? summary
194+
},
195+
};
196+
fakeAttributes.Add(fakeAttribute);
197+
198+
var fakeHelperAttributePropertyName =
199+
$"{nameof(ViewComponentTagHelperDesignTimeType.PlaceholderProperty)} = null; }};//";
200+
201+
var fakeHelperAttribute = new TagHelperAttributeDescriptor()
202+
{
203+
IsIndexer = attribute.IsIndexer,
204+
IsEnum = attribute.IsEnum,
205+
IsStringProperty = attribute.IsStringProperty,
206+
Name = attribute.Name,
207+
PropertyName = fakeHelperAttributePropertyName,
208+
TypeName = attribute.TypeName,
209+
};
210+
fakeHelperAttributes.Add(fakeHelperAttribute);
211+
}
212+
213+
fakeDescriptors.Add(fakeDescriptor);
214+
fakeDescriptors.Add(fakeHelperDescriptor);
215+
}
216+
217+
return fakeDescriptors;
218+
}
219+
}
220+
221+
public abstract class ViewComponentTagHelperDesignTimeType : TagHelper
222+
{
223+
public object PlaceholderProperty { get; set; }
224+
225+
public static Action ActionProperty { get; set; }
226+
227+
public static void PlaceholderMethod(object obj)
228+
{
229+
}
230+
}
231+
232+
public abstract class ViewComponentTagHelperDesignTimeHelperType : ViewComponentTagHelperDesignTimeType
233+
{
121234
}
122235
}

0 commit comments

Comments
 (0)