From 777d9c013beb0bec8ea5cd58b4a1c2de889afb98 Mon Sep 17 00:00:00 2001 From: stavroskasidis Date: Sat, 23 Sep 2017 00:56:39 +0300 Subject: [PATCH 1/2] implemented "FromAttribute" --- .../CounterDisplay.cshtml | 14 ++--- .../_ViewImports.cshtml | 1 + .../Components/FromAttributeAttribute.cs | 25 ++++++++ .../Components/RazorComponent.cs | 58 +++++++++++++++++-- 4 files changed, 85 insertions(+), 13 deletions(-) create mode 100644 src/Blazor.Runtime/Components/FromAttributeAttribute.cs diff --git a/samples/ClientServerApp/ClientServerApp.Client/CounterDisplay.cshtml b/samples/ClientServerApp/ClientServerApp.Client/CounterDisplay.cshtml index 74a28e4e..b7a4b042 100644 --- a/samples/ClientServerApp/ClientServerApp.Client/CounterDisplay.cshtml +++ b/samples/ClientServerApp/ClientServerApp.Client/CounterDisplay.cshtml @@ -6,14 +6,10 @@ @functions { - // It would be cleaner if we could mark properties with [FromAttribute] or similar, - // instead of having to extract them from a dictionary manually. - private int currentCount; - private Action resetCounterCallback; - protected override void ReceiveParameters(IDictionary parameters) - { - currentCount = (int)parameters["current-count"]; - resetCounterCallback = (Action)parameters["on-reset"]; - } + [FromAttribute("current-count")] + private int currentCount { get; set; } + + [FromAttribute("on-reset")] + private Action resetCounterCallback { get; set; } } \ No newline at end of file diff --git a/samples/ClientServerApp/ClientServerApp.Client/_ViewImports.cshtml b/samples/ClientServerApp/ClientServerApp.Client/_ViewImports.cshtml index 4cbafd5f..7e7d0147 100644 --- a/samples/ClientServerApp/ClientServerApp.Client/_ViewImports.cshtml +++ b/samples/ClientServerApp/ClientServerApp.Client/_ViewImports.cshtml @@ -4,5 +4,6 @@ @using Task = Blazor.Runtime.FakeBcl.Task; // Temporary until Mono supports Task @using HttpClient = Blazor.Runtime.FakeBcl.HttpClient; // Temporary until Mono supports HttpClient @using ClientServerApp.Client +@using Blazor.Runtime.Components @using ClientServerApp.Shared @using System.Net.Http diff --git a/src/Blazor.Runtime/Components/FromAttributeAttribute.cs b/src/Blazor.Runtime/Components/FromAttributeAttribute.cs new file mode 100644 index 00000000..21fd54d0 --- /dev/null +++ b/src/Blazor.Runtime/Components/FromAttributeAttribute.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Blazor.Runtime.Components +{ + //TODO add field support -> needs corlib FieldInfo implementation + [AttributeUsage(/*AttributeTargets.Field | */ AttributeTargets.Property, AllowMultiple = false, Inherited = true)] + public class FromAttributeAttribute : Attribute + { + public string AttributeName {get; set;} + public bool IsOptional { get; set; } + + public FromAttributeAttribute(string attributeName) + { + AttributeName = attributeName; + } + + public FromAttributeAttribute(string attributeName, bool isOptional) + { + AttributeName = attributeName; + IsOptional = isOptional; + } + } +} diff --git a/src/Blazor.Runtime/Components/RazorComponent.cs b/src/Blazor.Runtime/Components/RazorComponent.cs index f22f32e4..b09658c8 100644 --- a/src/Blazor.Runtime/Components/RazorComponent.cs +++ b/src/Blazor.Runtime/Components/RazorComponent.cs @@ -5,6 +5,7 @@ using System.Linq; using Blazor.Routing; using Blazor.Runtime.Components; +using System.Reflection; using Blazor.Runtime.Interop; namespace Blazor.Components @@ -56,9 +57,58 @@ protected override void RenderVirtualDom() // don't have to be marked abstract. } + private static Dictionary> ParameterMembersCache = new Dictionary>(); + private IEnumerable GetComponentParameterMembers() + { + if (ParameterMembersCache.ContainsKey(this.GetType())) + { + return ParameterMembersCache[this.GetType()]; + } + + var decoratedMembers = new List(); + + //TODO: Add field support -> needs corlib FieldInfo implementation + // Also, the following Linq will not work for some reason (missing GetEnumerator) + + //decoratedMembers.AddRange(this.GetType().GetTypeInfo().DeclaredFields.Where(x => x.GetCustomAttribute(typeof(FromAttributeAttribute)) != null)); + //decoratedMembers.AddRange(this.GetType().GetTypeInfo().DeclaredProperties.Where(x => x.GetCustomAttributes(typeof(FromAttributeAttribute),false).Count() > 0)); + + + //foreach works normally + foreach (var property in this.GetType().GetTypeInfo().DeclaredProperties) + { + if (property.GetCustomAttributes(typeof(FromAttributeAttribute), true).Count() > 0) + { + decoratedMembers.Add(property); + } + } + + ParameterMembersCache.Add(this.GetType(), decoratedMembers); + return decoratedMembers; + } + protected override void ReceiveParameters(IDictionary parameters) { - // Subclasses may optionally override this + var decoratedMembers = GetComponentParameterMembers(); + + foreach (var member in decoratedMembers) + { + + var attribute = (FromAttributeAttribute)member.GetCustomAttributes().First(x => x.GetType() == typeof(FromAttributeAttribute)); + if (!parameters.ContainsKey(attribute.AttributeName) && !attribute.IsOptional) + { + throw new Exception($"Parameter '{attribute.AttributeName}' is required"); + } + + var parameterValue = parameters[attribute.AttributeName]; + + //TODO add field support -> needs corlib FieldInfo implementation + + //if(member is FieldInfo) + // ((FieldInfo)member).SetValue(this, parameterValue); + //else + ((PropertyInfo)member).SetValue(this, parameterValue); + } } private static Type GetTypeForCompiledRazorFile(string cshtmlFilename) @@ -102,7 +152,7 @@ protected VDomAttribute onclick(Action callback) }; } - protected VDomAttribute onclick(Action callback) + protected VDomAttribute onclick(Action callback) { return new VDomAttribute { @@ -120,7 +170,7 @@ protected VDomAttribute onclickAsync(Func callback) }; } - protected VDomAttribute onclickAsync(Func callback) + protected VDomAttribute onclickAsync(Func callback) { return new VDomAttribute { @@ -129,7 +179,7 @@ protected VDomAttribute onclickAsync(Func callback) }; } - protected VDomAttribute onchange(Action callback) + protected VDomAttribute onchange(Action callback) { return new VDomAttribute { From 220cbc6487729169430700ccc81c1511eb5f905e Mon Sep 17 00:00:00 2001 From: stavroskasidis Date: Mon, 6 Nov 2017 23:10:31 +0200 Subject: [PATCH 2/2] Taking advantage of the mono runtime: Added field support/ Improved code --- .../CounterDisplay.cshtml | 4 +-- .../Components/FromAttributeAttribute.cs | 3 +- .../Components/RazorComponent.cs | 29 +++++-------------- 3 files changed, 11 insertions(+), 25 deletions(-) diff --git a/samples/ClientServerApp/ClientServerApp.Client/CounterDisplay.cshtml b/samples/ClientServerApp/ClientServerApp.Client/CounterDisplay.cshtml index b7a4b042..05da120f 100644 --- a/samples/ClientServerApp/ClientServerApp.Client/CounterDisplay.cshtml +++ b/samples/ClientServerApp/ClientServerApp.Client/CounterDisplay.cshtml @@ -8,8 +8,8 @@ @functions { [FromAttribute("current-count")] - private int currentCount { get; set; } + private int currentCount { get; set; } //works with properties [FromAttribute("on-reset")] - private Action resetCounterCallback { get; set; } + private Action resetCounterCallback; //and fields } \ No newline at end of file diff --git a/src/Blazor.Runtime/Components/FromAttributeAttribute.cs b/src/Blazor.Runtime/Components/FromAttributeAttribute.cs index 21fd54d0..e8ecc3f5 100644 --- a/src/Blazor.Runtime/Components/FromAttributeAttribute.cs +++ b/src/Blazor.Runtime/Components/FromAttributeAttribute.cs @@ -4,8 +4,7 @@ namespace Blazor.Runtime.Components { - //TODO add field support -> needs corlib FieldInfo implementation - [AttributeUsage(/*AttributeTargets.Field | */ AttributeTargets.Property, AllowMultiple = false, Inherited = true)] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)] public class FromAttributeAttribute : Attribute { public string AttributeName {get; set;} diff --git a/src/Blazor.Runtime/Components/RazorComponent.cs b/src/Blazor.Runtime/Components/RazorComponent.cs index b09658c8..c86f5d78 100644 --- a/src/Blazor.Runtime/Components/RazorComponent.cs +++ b/src/Blazor.Runtime/Components/RazorComponent.cs @@ -66,22 +66,10 @@ private IEnumerable GetComponentParameterMembers() } var decoratedMembers = new List(); + - //TODO: Add field support -> needs corlib FieldInfo implementation - // Also, the following Linq will not work for some reason (missing GetEnumerator) - - //decoratedMembers.AddRange(this.GetType().GetTypeInfo().DeclaredFields.Where(x => x.GetCustomAttribute(typeof(FromAttributeAttribute)) != null)); - //decoratedMembers.AddRange(this.GetType().GetTypeInfo().DeclaredProperties.Where(x => x.GetCustomAttributes(typeof(FromAttributeAttribute),false).Count() > 0)); - - - //foreach works normally - foreach (var property in this.GetType().GetTypeInfo().DeclaredProperties) - { - if (property.GetCustomAttributes(typeof(FromAttributeAttribute), true).Count() > 0) - { - decoratedMembers.Add(property); - } - } + decoratedMembers.AddRange(this.GetType().GetTypeInfo().DeclaredFields.Where(x => x.GetCustomAttribute(typeof(FromAttributeAttribute), true) != null)); + decoratedMembers.AddRange(this.GetType().GetTypeInfo().DeclaredProperties.Where(x => x.GetCustomAttribute(typeof(FromAttributeAttribute),true) != null)); ParameterMembersCache.Add(this.GetType(), decoratedMembers); return decoratedMembers; @@ -101,13 +89,12 @@ protected override void ReceiveParameters(IDictionary parameters } var parameterValue = parameters[attribute.AttributeName]; + - //TODO add field support -> needs corlib FieldInfo implementation - - //if(member is FieldInfo) - // ((FieldInfo)member).SetValue(this, parameterValue); - //else - ((PropertyInfo)member).SetValue(this, parameterValue); + if(member is FieldInfo) + ((FieldInfo)member).SetValue(this, parameterValue); + else + ((PropertyInfo)member).SetValue(this, parameterValue); } }