/// <summary> /// Instantiates a new <see cref="TagHelperContext"/>. /// </summary> /// <param name="allAttributes">Every attribute associated with the current HTML element.</param> /// <param name="items">Collection of items used to communicate with other <see cref="ITagHelper"/>s.</param> /// <param name="uniqueId">The unique identifier for the source element this <see cref="TagHelperContext" /> /// applies to.</param> public TagHelperContext( IEnumerable<IReadOnlyTagHelperAttribute> allAttributes, IDictionary<object, object> items, string uniqueId) { if (allAttributes == null) { throw new ArgumentNullException(nameof(allAttributes)); } if (items == null) { throw new ArgumentNullException(nameof(items)); } if (uniqueId == null) { throw new ArgumentNullException(nameof(uniqueId)); } AllAttributes = new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>( allAttributes.Select(attribute => new TagHelperAttribute(attribute.Name, attribute.Value))); Items = items; UniqueId = uniqueId; }
/// <summary> /// Checks all attributes on a tag-helper element if any are asp-all-route-data or asp-route-* /// Returns null if no route data was found. /// </summary> /// <param name="attributes"></param> /// <returns></returns> public static Dictionary <string, string> GetRouteValues(ReadOnlyTagHelperAttributeList attributes) { bool hasAllRouteData = attributes.TryGetAttribute("asp-all-route-data", out TagHelperAttribute allRouteValues); if (hasAllRouteData) { return(allRouteValues.Value as Dictionary <string, string>); } else { // You can write the attributes as ASP-ROUTE- and the name will be ASP-ROUTE so need to ignore case IEnumerable <TagHelperAttribute> routeAttributes = attributes.Where(x => x.Name.StartsWith("asp-route-", StringComparison.OrdinalIgnoreCase)); if (routeAttributes.Any()) { Dictionary <string, string> routeValues = new Dictionary <string, string>(); foreach (TagHelperAttribute attribute in routeAttributes) { // asp-route- is 10 characters long string parameter = attribute.Name.Substring(10).ToLower(); routeValues.Add(parameter, attribute.Value as string); } return(routeValues); } } return(null); }
public static TagHelperAttributeList ToList(this ReadOnlyTagHelperAttributeList readOnlyTagHelperAttributeList) { TagHelperAttribute[] attrs = new TagHelperAttribute[readOnlyTagHelperAttributeList.Count]; readOnlyTagHelperAttributeList.CopyTo(attrs, 0); return(new TagHelperAttributeList(attrs)); }
public static TagHelperAttribute GetTagHelperAttribute(this ReadOnlyTagHelperAttributeList tagHelperAttribute, string name) { if (tagHelperAttribute.TryGetAttribute(name, out var attribute)) { return(attribute); } return(null); }
/// <summary> /// Instantiates a new <see cref="TagHelperContext"/>. /// </summary> /// <param name="allAttributes">Every attribute associated with the current HTML element.</param> /// <param name="items">Collection of items used to communicate with other <see cref="ITagHelper"/>s.</param> /// <param name="uniqueId">The unique identifier for the source element this <see cref="TagHelperContext" /> /// applies to.</param> /// <param name="getChildContentAsync">A delegate used to execute and retrieve the rendered child content /// asynchronously.</param> public TagHelperContext( [NotNull] IEnumerable<IReadOnlyTagHelperAttribute> allAttributes, [NotNull] IDictionary<object, object> items, [NotNull] string uniqueId, [NotNull] Func<bool, Task<TagHelperContent>> getChildContentAsync) { AllAttributes = new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>( allAttributes.Select(attribute => new TagHelperAttribute(attribute.Name, attribute.Value))); Items = items; UniqueId = uniqueId; _getChildContentAsync = getChildContentAsync; }
private static bool HasMissingAttributes(ReadOnlyTagHelperAttributeList allAttributes, string[] requiredAttributes) { // Check for all attribute values // Perf: Avoid allocating enumerator for (var i = 0; i < requiredAttributes.Length; i++) { if (!allAttributes.TryGetAttribute(requiredAttributes[i], out var attribute)) { // Missing attribute. return(true); } if (attribute.Value is string valueAsString && string.IsNullOrEmpty(valueAsString)) { // Treat attributes with empty values as missing. return(true); } } return(false); }
public static TagHelperContext CreateTagHelperContext(this IWebPackObject webPack, ReadOnlyTagHelperAttributeList allAttributes, IDictionary <object, object> items, string uniqueId) { var customAttributeList = new TagHelperAttributeList(); var attributes = webPack.Resolve(); foreach (KeyValuePair <string, object> entry in attributes) { customAttributeList.Add(new TagHelperAttribute(entry.Key, entry.Value)); } foreach (var item in allAttributes) { if (!customAttributeList.ContainsName(item.Name)) { customAttributeList.Add(new TagHelperAttribute(item.Name, item.Value)); } } return(new TagHelperContext(customAttributeList, new Dictionary <object, object>(), uniqueId)); }
/// <summary> /// Given an HTML tag (with attributes), read the resource that should be pushed to the client. /// </summary> private PushPromise ReadPushedResourceFromTag(string tagName, ReadOnlyTagHelperAttributeList attributes) { switch (tagName) { case "script": // create Link: </asset/to/push.js>; rel=preload; as=script return(new PushPromise(attributes["src"].Value.ToString(), "script")); case "link": // create Link: </asset/to/push.css>; rel=preload; as=stylesheet return(new PushPromise(attributes["href"].Value.ToString(), attributes["rel"].Value?.ToString())); case "img": // create Link: </asset/to/push.jpg>; rel=preload; as=image return(new PushPromise(attributes["src"].Value.ToString(), "image")); case "a": // create Link: </asset/to/push.html>; rel=preload; as=document return(new PushPromise(attributes["href"].Value.ToString(), "document")); default: throw new NotImplementedException($"{nameof(Handler.PushPromiseHandler)} does not know how to handle '{tagName}' tags"); } }
/// <summary> /// Process /// </summary> /// <param name="context">Context</param> /// <param name="output">Output</param> public override void Process(TagHelperContext context, TagHelperOutput output) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (output == null) { throw new ArgumentNullException(nameof(output)); } //clear the output output.SuppressOutput(); //required asterisk bool.TryParse(IsRequired, out bool required); if (required) { output.PreElement.SetHtmlContent("<div class='input-group input-group-required'>"); output.PostElement.SetHtmlContent("<div class=\"input-group-btn\"><span class=\"required\">*</span></div></div>"); } //contextualize IHtmlHelper IViewContextAware viewContextAware = _htmlHelper as IViewContextAware; viewContextAware?.Contextualize(ViewContext); //get htmlAttributes object Dictionary <string, object> htmlAttributes = new Dictionary <string, object>(); ReadOnlyTagHelperAttributeList attributes = context.AllAttributes; foreach (TagHelperAttribute attribute in attributes) { if (!attribute.Name.Equals(ForAttributeName) && !attribute.Name.Equals(NameAttributeName) && !attribute.Name.Equals(ItemsAttributeName) && !attribute.Name.Equals(DisabledAttributeName) && !attribute.Name.Equals(RequiredAttributeName)) { htmlAttributes.Add(attribute.Name, attribute.Value); } } //generate editor string tagName = For != null ? For.Name : Name; bool.TryParse(IsMultiple, out bool multiple); if (!string.IsNullOrEmpty(tagName)) { IHtmlContent selectList; if (multiple) { selectList = _htmlHelper.Editor(tagName, "MultiSelect", new { htmlAttributes, SelectList = Items }); } else { if (htmlAttributes.ContainsKey("class")) { htmlAttributes["class"] += " form-control"; } else { htmlAttributes.Add("class", "form-control"); } selectList = _htmlHelper.DropDownList(tagName, Items, htmlAttributes); } output.Content.SetHtmlContent(selectList.RenderHtmlContent()); } }
public async Task TagHelper_CallsGeneratorWithExpectedValues_RealModelType( Type modelType, object model, bool allowMultiple) { // Arrange var contextAttributes = new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>( Enumerable.Empty<IReadOnlyTagHelperAttribute>()); var originalAttributes = new TagHelperAttributeList(); var propertyName = "Property1"; var tagName = "select"; var tagHelperContext = new TagHelperContext( contextAttributes, items: new Dictionary<object, object>(), uniqueId: "test", getChildContentAsync: useCachedResult => { var tagHelperContent = new DefaultTagHelperContent(); tagHelperContent.SetContent("Something"); return Task.FromResult<TagHelperContent>(tagHelperContent); }); var output = new TagHelperOutput(tagName, originalAttributes); var metadataProvider = new EmptyModelMetadataProvider(); var modelExplorer = metadataProvider.GetModelExplorerForType(modelType, model); var modelExpression = new ModelExpression(propertyName, modelExplorer); var htmlGenerator = new Mock<IHtmlGenerator>(MockBehavior.Strict); var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator.Object, metadataProvider); var currentValues = new string[0]; htmlGenerator .Setup(real => real.GetCurrentValues( viewContext, modelExplorer, propertyName, // expression allowMultiple)) .Returns(currentValues) .Verifiable(); htmlGenerator .Setup(real => real.GenerateSelect( viewContext, modelExplorer, null, // optionLabel propertyName, // expression It.IsAny<IEnumerable<SelectListItem>>(), currentValues, allowMultiple, null)) // htmlAttributes .Returns((TagBuilder)null) .Verifiable(); var tagHelper = new SelectTagHelper(htmlGenerator.Object) { For = modelExpression, ViewContext = viewContext, }; // Act await tagHelper.ProcessAsync(tagHelperContext, output); // Assert htmlGenerator.Verify(); Assert.NotNull(viewContext.FormContext?.FormData); var keyValuePair = Assert.Single( viewContext.FormContext.FormData, entry => entry.Key == SelectTagHelper.SelectedValuesFormDataKey); Assert.Same(currentValues, keyValuePair.Value); }
public async Task PopulateHtmlTagAsync(ITagBuilderOptions options, ReadOnlyTagHelperAttributeList attributeList, Asset asset, IHtmlContentBuilder output) { TagBuilder tagBuilder = null; var outputMode = options.Output; if (!this.Options.BundleStylesheets && !this.Options.MinifyStylesheets) { outputMode = OptimizationTagOutput.Tag; } else if (OptimizationTagOutput.Tag == outputMode && (this.Options.BundleStylesheets || this.Options.MinifyStylesheets)) { outputMode = OptimizationTagOutput.StaticFile; } switch (outputMode) { case OptimizationTagOutput.Default: //same as OptimizationTagOutput.Inline string content = await asset.ReadContentAsStringAsync(); output.AppendHtmlLine($"<style>{content}</style>"); break; case OptimizationTagOutput.Tag: tagBuilder = new TagBuilder("link"); tagBuilder.TagRenderMode = TagRenderMode.SelfClosing; tagBuilder.Attributes.Add("rel", "stylesheet"); tagBuilder.Attributes.Add("type", "text/css"); tagBuilder.Attributes.Add("href", asset.GetWebLocation(this.Options)); output.AppendHtml(tagBuilder); break; case OptimizationTagOutput.StaticFile: case OptimizationTagOutput.Loader: string webPath = null; PathString relativeFilePath = new PathString(this.Options.StaticAssetsPath).Add($"/{asset.Name}"); var assetFileInfo = this.HostingEnvironment.WebRootFileProvider.GetFileInfo(relativeFilePath); if (!assetFileInfo.Exists) { content = await asset.ReadContentAsStringAsync(); await File.WriteAllTextAsync(assetFileInfo.PhysicalPath, content, Encoding.UTF8); } if (string.IsNullOrWhiteSpace(this.Options.AssetRootUrl)) { webPath = this.HttpContextAccessor.HttpContext.Request.PathBase.Add(relativeFilePath); } else { if (!Uri.TryCreate(this.Options.AssetRootUrl, UriKind.Absolute, out Uri cdnRootUri)) { throw new UriFormatException($"'{this.Options.AssetRootUrl}' is an invalid Uri. An absolute Uri is expected."); } Uri.TryCreate(cdnRootUri, $"{asset.Name}", out Uri webPathUri); webPath = webPathUri.ToString(); } tagBuilder = new TagBuilder("link"); tagBuilder.TagRenderMode = TagRenderMode.SelfClosing; tagBuilder.Attributes.Add("rel", "stylesheet"); tagBuilder.Attributes.Add("type", "text/css"); tagBuilder.Attributes.Add("href", webPath); output.AppendHtml(tagBuilder); break; } }
public async Task TagHelper_CallsGeneratorWithExpectedValues_RealModelType( Type modelType, object model, bool allowMultiple) { // Arrange var contextAttributes = new ReadOnlyTagHelperAttributeList <IReadOnlyTagHelperAttribute>( Enumerable.Empty <IReadOnlyTagHelperAttribute>()); var originalAttributes = new TagHelperAttributeList(); var propertyName = "Property1"; var tagName = "select"; var tagHelperContext = new TagHelperContext( contextAttributes, items: new Dictionary <object, object>(), uniqueId: "test"); var output = new TagHelperOutput( tagName, originalAttributes, getChildContentAsync: useCachedResult => { var tagHelperContent = new DefaultTagHelperContent(); tagHelperContent.SetContent("Something"); return(Task.FromResult <TagHelperContent>(tagHelperContent)); }); var metadataProvider = new EmptyModelMetadataProvider(); var modelExplorer = metadataProvider.GetModelExplorerForType(modelType, model); var modelExpression = new ModelExpression(propertyName, modelExplorer); var htmlGenerator = new Mock <IHtmlGenerator>(MockBehavior.Strict); var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator.Object, metadataProvider); var currentValues = new string[0]; htmlGenerator .Setup(real => real.GetCurrentValues( viewContext, modelExplorer, propertyName, // expression allowMultiple)) .Returns(currentValues) .Verifiable(); htmlGenerator .Setup(real => real.GenerateSelect( viewContext, modelExplorer, null, // optionLabel propertyName, // expression It.IsAny <IEnumerable <SelectListItem> >(), currentValues, allowMultiple, null)) // htmlAttributes .Returns((TagBuilder)null) .Verifiable(); var tagHelper = new SelectTagHelper(htmlGenerator.Object) { For = modelExpression, ViewContext = viewContext, }; // Act tagHelper.Init(tagHelperContext); await tagHelper.ProcessAsync(tagHelperContext, output); // Assert htmlGenerator.Verify(); var keyValuePair = Assert.Single( tagHelperContext.Items, entry => (Type)entry.Key == typeof(SelectTagHelper)); Assert.Same(currentValues, keyValuePair.Value); }
public async Task PopulateHtmlTagAsync(ITagBuilderOptions options, ReadOnlyTagHelperAttributeList attributeList, Asset asset, IHtmlContentBuilder output) { TagBuilder tagBuilder = new TagBuilder("script"); tagBuilder.TagRenderMode = TagRenderMode.Normal; tagBuilder.Attributes.Add("type", "text/javascript"); if (attributeList.ContainsName("defer")) { tagBuilder.Attributes.Add("defer", null); } else if (attributeList.ContainsName("async")) { tagBuilder.Attributes.Add("async", null); } var outputMode = options.Output; if (!this.Options.BundleJavascripts && !this.Options.MinifyJavascripts && OptimizationTagOutput.Loader != outputMode) { outputMode = OptimizationTagOutput.Tag; } else if (OptimizationTagOutput.Tag == outputMode && (this.Options.BundleJavascripts || this.Options.MinifyJavascripts)) { outputMode = OptimizationTagOutput.StaticFile; } switch (outputMode) { case OptimizationTagOutput.Default: //same as OptimizationTagOutput.Inline string content = await asset.ReadContentAsStringAsync(); tagBuilder.InnerHtml.SetHtmlContent(content); break; case OptimizationTagOutput.Tag: tagBuilder.Attributes.Add("src", asset.GetWebLocation(this.Options)); if (null != asset?.Content) { await asset.Content.DisposeAsync(); } break; case OptimizationTagOutput.StaticFile: case OptimizationTagOutput.Loader: string webPath = null; PathString relativeFilePath = new PathString(this.Options.StaticAssetsPath).Add($"/{asset.Name}"); var assetFileInfo = this.HostingEnvironment.WebRootFileProvider.GetFileInfo(relativeFilePath); if (!assetFileInfo.Exists) { content = await asset.ReadContentAsStringAsync(); await File.WriteAllTextAsync(assetFileInfo.PhysicalPath, content, Encoding.UTF8); } if (string.IsNullOrWhiteSpace(this.Options.AssetRootUrl)) { webPath = this.HttpContextAccessor.HttpContext.Request.PathBase.Add(relativeFilePath); } else { if (!Uri.TryCreate(this.Options.AssetRootUrl, UriKind.Absolute, out Uri cdnRootUri)) { throw new UriFormatException($"'{this.Options.AssetRootUrl}' is an invalid Uri. An absolute Uri is expected."); } Uri.TryCreate(cdnRootUri, $"{asset.Name}", out Uri webPathUri); webPath = webPathUri.ToString(); } if (OptimizationTagOutput.StaticFile == outputMode) { tagBuilder.Attributes.Add("src", webPath); } else { tagBuilder.InnerHtml.SetHtmlContent($"adriva.optimization.loader.push('{webPath}', 1)"); } if (null != asset?.Content) { await asset.Content.DisposeAsync(); } break; } output.AppendHtml(tagBuilder); }