Use a #typeparam as component in Blazor - asp.net-core

Is it possible to use a #typeparam as Component?
More explicitly, do something like the following MyComponent.razor:
#typeparam TComponent
<TComponent />
Of course, there would also be a MyComponent.razor.cs file whose content would be:
public partial MyComponent<TComponent> : ComponentBase where TComponent : ComponentBase
so that the compiler would know that <TComponent /> is meaningful.
I cannot find any documentation about this in Microsoft docs.
When I try it seems to compile, but display the following warning:
warning RZ10012: Found markup element with unexpected name 'TComponent'. If this is intended to be a component, add a #using directive for its namespace
However it is only a warning and not an error. It does't show anything in the browser though.
I am using ASP.NET 5.
Thanks

Not quite sure what you are trying to do but you can write what I believe you are trying to achieve as a component class like this:
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
namespace BlazorTest.Pages
{
public class ComponentRenderer<TComponent> : ComponentBase where TComponent : IComponent
{
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenComponent<TComponent>(1);
builder.CloseComponent();
}
}
}
So in Index.razor you can do something like the following to render a component of type FetchData:
....
<ComponentRenderer TComponent="FetchData"></ComponentRenderer>
....
If I'm wide of the mark post a little more information.

Related

Blazor #page route url define with variable

I have a question for Blazor Server Side.
I want to #page route url define with variable or property.
I can use now with below default method
#page "/route-url"
<h1>Page Test</h1>
#code {
}
But i want use like as below method
#page MenuItem.Role
<h1>Page Test</h1>
#code {
}
I'm tried above method then throwed exception. Like as below exception.
C:\Projects\TestBlazorProject\Pages\TestPage.razor(1,7): error RZ1016: The 'page' directive expects a string surrounded by double quotes. [C:\Projects\TestBlazorProject\TestBlazorProject.csproj]
How to define #page route url with any different variable or any class property?
Can this be done?
Yes
How?
Page file
#attribute [Route(PageRoute.TaskList)]
<div>PAGE HTML HERE</div>
#code{ ... }
PageRoute.cs:
public static class PageRoute
{
public const string TaskList = "/route-url";
}
Explanation
The page directive gets compiled down to an attribute and has the same restrictions as C# attributes.
You can use the #attribute with the [Route] attribute and use string concatenation instead of string interpolation to define a constant for the route, since that's what the C# compiler supports.
Why would anybody do this?
This is a good coding practice, because you are not hardcoding the page/component name in multiple places, but only in one place.
So one fine day when you manager asks to change page name "Earth" to "Planet3",
you just change it in 1 place, and be 98% sure that your app wont crash because of it.
#page isn't C#, it's Razor talk. Razor files are pre-compiled into c# files during compilation.
As an example, this is the important section of the C# pre-compiled file for Index.razor (Index.razor.g.cs):
[Microsoft.AspNetCore.Components.RouteAttribute("/")]
public partial class Index : Microsoft.AspNetCore.Components.ComponentBase
{
#pragma warning disable 1998
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
{
__builder.AddMarkupContent(0, "<h1>Hello, world!</h1>\r\n\r\nWelcome to your new app.\r\n\r\n");
__builder.OpenComponent<Blazor.Starter.Shared.SurveyPrompt>(1);
__builder.AddAttribute(2, "Title", "How is Blazor working for you?");
__builder.CloseComponent();
}
#pragma warning restore 1998
}
Note that #page has become a compile time attribute [Microsoft.AspNetCore.Components.RouteAttribute("/")]. It's fixed at compiletime, you can't change it at runtime.
Routes are set this way because the router builds a routemap - essentially a dictionary of route url/component class pairs - when the application loads by trawling the application assembly for any component classes with a Route attribute. On a routing event it reads the new url, finds the component class and loads it into the layout. Any variables - stuff in curly brackets - get passed into the component as Parameters.
You haven't made it clear what the line below is supposed to do:
#page MenuItem.Role
Do you want to capture a variable supplied in the route into MenuItem.Role?
Do you want to set this page's route to the value in MenuItem.Role?
If 1, either the other answers will work for you. If 2, you'll need to consider writing your own router. A subject beyond a simple answer here.
I think you can achieve that by following.
#page "/{Role}"
#code{
[Parameter]
public string Role { get; set; }
}
Building off of the above you can I was able to get this to work with the code isolation approach.
Client/Pages/Foo/
----Index.razor
----Index.cs
namespace Client.Pages.Foo;
[Microsoft.AspNetCore.Components.RouteAttribute(Path)]
public partial class Index
{
public const string Path = "/Foo";
}

How to override the login page provided by abp.io?

I tried to override my custom login with LoginModel and Login.cshtml as described here.
After replacing Login.cshtml and myCustomeLoginModel.cs, this error is shown :
'LoginModel' does not contain a constructor that takes 2 arguments
and this is the code of myCustomLoginModel.cs after replacing :
public class myCustomLoginModel: LoginModel
{
public myCustomLoginModel(IAuthenticationSchemeProvider schemeProvider,
IOptions<Volo.Abp.Account.Web.AbpAccountOptions> accountOptions)
: base(schemeProvider, accountOptions)
{
}
}
and then all properties of myCustomLoginModel is not recognized in Login.cshtml like this :
The properties of myCustomLoginModel is not recognized
What did I miss ?
I tried to send any text for #hikalkan, the contributor of this concept at abp.io, but I did not found any email or contact info that I can use.
I found that I had a fault!
I had created a class named 'LoginModel.cs' under the Login.cshtml.
I don't know how this happened.
But this causes errors.
If I followed exactly what was in the document, it would work nicely!

Xamarin XAML x:Static reference to property in a nested class

I'm writing a mobile app using Xamarin and I have a static class called Strings that wraps my RESX resources. I want to use x:Static to bind to these in my XAML files. This is working if I have a single static class with static properties to bind to.
I'm cutting out some comments and other non-essential bits, but it basically looks like this:
namespace MyCompany.Resources
{
public static partial class Strings
{
public static string LabelUsername { get { return Resources.LabelUsername; } }
}
}
Then in my XAML, I bind to it like this:
<Entry Placeholder="{x:Static resources:Strings.LabelUsername}"
where resources is defined as
xmlns:resources="clr-namespace:MyCompany.Resources;assembly=MyCompany"
That all works fine. It breaks down when I add a nested class to Strings. The class looks like this:
namespace MyCompany.Resources
{
public static partial class Strings
{
public static partial class Label
{
public static string Username { get { return Resources.Label_Username; } }
}
}
}
If I do that, then I would bind to it in my XAML like this:
<Entry Placeholder="{x:Static resources:Strings.Label.Username}"
Notice how after "resources:" we now have three levels (Strings.Label.Username). This seems to be what fails. When I do this, I get the compile error:
Type Strings.Label not found in xmlns clr-namespace:MyCompany.Resources;assembly=MyCompany
Also, I can access the nested class and its properties just fine from my ViewModels. Is there any way to make this work from the XAML? I know I could bind to a variable in the VM and then have that reference Strings.Label.Username, but I don't want to do that for every resource binding.
Your static property's name in the binding should be
Strings+LabelUsername.Username
Not only did you have a typo, but you tried to use the dot notation to reference the nested class, which won't work.
Bindings use standard .net reflection notation for referencing properties and classes by name (they either use a parser on the string or use reflection directly, can't be arsed to check the codebase). Nested class names use a + to separate the containing class name and the inner class name. You can read more about that here:
C# : having a "+" in the class name?

Disambiguating namespaces in generated XAML code

I have a Page that imports controls from a library like this:
<Page
x:Class="Foo.Bar.SomePage"
xmlns:myNamespace="using:Bar.Controls">
<myNamespace:SomeControl x:Name="someControl">
<!-- snip -->
</myNamespace:SomeControl>
</Page>
As you can see here, the page is declared in the ::Foo::Bar namespace, while SomeControl is declared in the ::Bar namespace. The problem I face is that Visual Studio generates this code:
namespace Bar {
namespace Controls {
ref class SomeControl;
}
}
namespace Foo
{
namespace Bar
{
partial ref class SomePage : /* ... */
{
/* ... */
private: Bar::SomeControl^ someControl;
};
}
}
The field definition Bar::SomeControl^ someControl tries to select ::Foo::Bar::SomeControl instead of ::Bar::SomeControl because Visual Studio doesn't fully-qualify it.
Is this by design (is there a way to phrase the using: URI in such a way that it will fully-qualify the name), or is this a bug? How can I work around that?
I think that I could convince people to make an exception to the namespace structure for this specific class, but it would be much simpler if there was an in-code solution for this.
For posterity, right now I'm using this kludge before #including the g.h file, but it's not super pretty:
namespace Foo
{
namespace Bar
{
typedef ::Bar::SomeControl SomeControl;
}
}
It introduces the control into the (incorrect) namespace so that it works even though the XAML code generator gets it wrong.

Referencing a custom WinRT component breaks javascript class?

I have a javascript Windows Store application that I'm working on, and I needed to create a WinRT component for some processing. As soon as I add the reference to that component, I get a javascript error:
0x800a01bd - Javascript runtime error: Object doesn't support this action.
This occurs on a line w/ the following:
engine = new MyApp.Engine();
Which is defined:
WinJS.Namespace.define("MyApp", {
Engine: WinJS.Class.define(function() {
//constructor stuff
//other stuff snipped for brevity
}
});
I'm not even accessing any code in my custom component, simply adding the reference causes it to break. Anyone run into this? Googling/Binging has been no help.
I found the answer.
So in my Javascript code, I had the declaration for a namespace.
In my WinRT C# component, I was using the same namespace. That namespace apparently stomps out my JS namespace declartion. I changed my WinRT component from this:
namespace MyApp
{
public sealed class SomeClass
{
}
}
to:
namespace MyAppUtils
{
public sealed class SomeClass
{
}
}
And now everything is good..so, Lesson: If you're using JS and a custom WinRT component, you (apparently) can't use the same namespace in both.