Insert new Blazor View or Component with JSRuntime (jQuery.append) - c#

This seems so simple in theory, but I can't find a way to make it work:
in _Layout.cshtml
window.insertComponent = function (selector, component) {
$(selector).append(component);
}
In my main component:
private object Model {get;set;}
private string elementId {get;set;}
//...
private async void OnChange(EventArgs e)
{
await JSRuntime.InvokeVoidAsync("insertComponent", new[] { "#" + elementId, new SomeChildComponentOrView() { Model = this.Model }});
}
What, if anything, can I do to make this work? Obviously, new SomeChildComponentOrView() won't compile. Where do I go from here?
Using ASP.NET Core 3.1 with Blazor.

I suppose you may check your Setup.cs file, it should be configured to add a service and use it.
app.UseStaticFile()
causing if you don't use it, it will be no function on JS or JQ.

Related

How do I generate a url inside a c# service?

Really simple I hope. I just want to do the equivalent of
Url.Action("SomeAction", "SomeController", new { id = 3 });
But inside a service class. Not inside a controller class or IActionResult method
In a plain old service class. Because of the service call having all the data I don't want to pass in other information so my service call is nice and clean.
I've come close but nothing seems to work, either that or it cant be done.
I tried to dependency inject this
services.AddScoped<IUrlHelper>(x => x
.GetRequiredService<IUrlHelperFactory>()
.GetUrlHelper(x.GetRequiredService<IActionContextAccessor>().ActionContext));
In my service call I used (DI) this
public AdminService(..., IUrlHelper urlHelper)
so in my service method I could to this
string editUrl = _urlHelper.Action("EditRole", "Admin", new { id = 0 });
which got rid of all the red squiglies but at run time this bit caused me a problem
.GetUrlHelper(x.GetRequiredService<IActionContextAccessor>().ActionContext));
You can inject IUrlHelper interface inside a service class.
public class ServiceClass
{
private readonly IActionContextAccessor _actionContextAccessor;
private readonly IUrlHelperFactory _urlHelperFactory;
public ServiceClass(IActionContextAccessor actionContextAccessor,
IUrlHelperFactory urlHelperFactory,)
{
_actionContextAccessor = actionContextAccessor;
_urlHelperFactory = urlHelperFactory;
}
public string CreateUrl()
{
var urlHelper = _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext);
string url = urlHelper.Action("SomeAction", "SomeController");
return url;
}
}
#SMM I had to add this to my startup but otherwise, works, so thank you
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
services.AddSingleton<IUrlHelper, UrlHelper>();
Url.Action generates only an url.
#Url.Action("actionName", "controllerName", new { id = id })
Html.ActionLink generates an tag automatically.

How to register api controller from a library with configuration

What I have done is created a small API in a class library. This API would be used by other sites. Think of it as a standard endpoint that all of our websites will contain.
[Route("api/[controller]")]
[ApiController]
public class CustomController : ControllerBase
{
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}
}
The above is in a class library. Now what i would like to do is be able to add this to the projects in a simple manner.
app.UseCustomAPI("/api/crap");
I am not exactly sure how i should handle routing to the api controllers in the library. I created a CustomAPIMiddleware which is able to catch that i called "/api/crap" however i am not sure how i should forward the request over to CustomController in the library
public async Task Invoke(HttpContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
PathString matched;
PathString remaining;
if (context.Request.Path.StartsWithSegments(_options.PathMatch, out matched, out remaining))
{
PathString path = context.Request.Path;
PathString pathBase = context.Request.PathBase;
context.Request.PathBase = pathBase.Add(matched);
context.Request.Path = remaining;
try
{
await this._options.Branch(context);
}
finally
{
context.Request.PathBase = pathBase;
context.Request.Path = path;
}
path = new PathString();
pathBase = new PathString();
}
else
await this._next(context);
}
After having done that i am starting to think i may have approached this in the wrong manner and should actually be trying to add it directly to the routing tables somehow. That being said i would like it if they could customize the endpoint that the custom controller reads from.
Update
The following does work. Loading and registering API Controllers From Class Library in ASP.NET core
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddApplicationPart(Assembly.Load(new AssemblyName("WebAPI")));
However i am really looking for a middlewere type solution so that users can simply add it and i can configure the default settings or they can change some of the settings. The above example would not allow for altering the settings.
app.UseCustomAPI("/api/crap");
Update from comment without Assembly
If i dont add the .AddApplicationPart(Assembly.Load(new AssemblyName("WebAPI")));
This localhost page can’t be found No webpage was found for the web address:
https://localhost:44368/api/Custom
To customise the routing for a controller at runtime, you can use an Application Model Convention. This can be achieved with a custom implementation of IControllerModelConvention:
public class CustomControllerConvention : IControllerModelConvention
{
private readonly string newEndpoint;
public CustomControllerConvention(string newEndpoint)
{
this.newEndpoint = newEndpoint;
}
public void Apply(ControllerModel controllerModel)
{
if (controllerModel.ControllerType.AsType() != typeof(CustomController))
return;
foreach (var selectorModel in controllerModel.Selectors)
selectorModel.AttributeRouteModel.Template = newEndpoint;
}
}
This example just replaces the existing template (api/[controller]) with whatever is provided in the CustomControllerConvention constructor. The next step is to register this new convention, which can be done via the call to AddMvc. Here's an example of how that works:
services.AddMvc(o =>
{
o.Conventions.Add(new CustomControllerConvention("api/whatever"));
});
That's all that's needed to make things work here, but as you're offering this up from another assembly, I'd suggest an extension method based approach. Here's an example of that:
public static class MvcBuilderExtensions
{
public static IMvcBuilder SetCustomControllerRoute(
this IMvcBuilder mvcBuilder, string newEndpoint)
{
return mvcBuilder.AddMvcOptions(o =>
{
o.Conventions.Add(new CustomControllerConvention(newEndpoint));
});
}
}
Here's how that would be called:
services.AddMvc()
.SetCustomControllerRoute("api/whatever");
This whole approach means that without a call to SetCustomControllerRoute, api/Custom will still be used as a default.

.NET Core 2.0 View Component Not Populating #Model Property

I'm learning how to use .NET Core 2.0, and one of the parts of it that I need to be able to use is a View Component.
I've almost figured it out completely. I'm using Dependency Injection to pass a service into my ViewComponent constructor which then passes off a List of TestItemto the view.
The #model System.Collections.Generic.List<Common.TestItem> property is always null, however; ViewContext.ViewData.Model populates correctly.
Any ideas why? Code is posted below.
Service
TestItemService class
public class TestItemService
{
public List<TestItem> GetItems()
{
return new List<TestItem>()
{
new TestItem(){ id=0, date=DateTime.Today.AddDays(-2), Name="TestItem1" },
new TestItem(){ id=1, date=DateTime.Today.AddDays(-4), Name="TestItem2" },
new TestItem(){ id=2, date=DateTime.Today.AddDays(3), Name="TestItem3" },
};
}
}
Dependency Injection
In the Startup class in the ConfigureServices method:
services.AddScoped<TestItemService.TestItemService, TestItemService.TestItemService>();
ViewComponent
public class TestViewComponent : ViewComponent
{
private readonly TestItemService.TestItemService service;
public TestViewComponent(TestItemService.TestItemService context)
{
service = context;
}
public async Task<IViewComponentResult> InvokeAsync()
{
List<TestItem> items = await GetItemsAsync();
return View("Default",items);
}
public Task<List<TestItem>> GetItemsAsync()
{
return service.GetItems().Where(i => i != null).ToAsyncEnumerable().ToList();
}
}
View
#page
#model System.Collections.Generic.List<Common.TestItem>
<h3>Priority Items</h3>
<ul>
#foreach (var todo in (List<Common.TestItem>)ViewContext.ViewData.Model)
{
<li>#todo.Name</li>
}
</ul>
I found the problem. In my view component cshtml file, I had the #page tag at the top which for some reason broke this. I'll change the accepted answer to someone else's if they will explain why that broke it. What does the #page tag do>

UTF8 behavior in asp.mvc6 with dependency injection

I have got a strange Problem with strings in an asp.net 5 application, using asp.mvc 6 and the build in dependency injection (this is my conclusion). In a controller, I return a custom class created from a class library injected into the controller. To demonstrate the problem, I created a Test Class.
public class TestContainer
{
public string Caption { get; set; }
}
Then, in a controller, I return a list of these containers; one is created in the controller, and one is created in a class library which is injected in the controller.
In the controller:
[Route("api/testmethod")]
public List<TestContainer> TestMethod()
{
var testlocal = new TestContainer()
{
Caption = "aouäöü some name"
};
var testdpi = _businessLogic.ReturnTestContainer();
return new List<TestContainer>() {testlocal, testdpi};
}
In the injected library I use the following method to create the Testcontainer:
public TestContainer ReturnTestContainer()
{
var test = new TestContainer()
{
Caption = "aouäöü some name",
};
return test;
}
Now the strange thing happens. If I debug the application, it looks as follows:
Images can be found here and here.
For the local generate class, it shows:
aouäöü some name
For the Caption property, but for the class generated by the injected class it shows:
aou��� some name
For the caption property and the output in the browser, it looks as follows:
[{"Caption":"aouäöü some name"},{"Caption":"aou��� some name"}]
I can´t explain this behavior to myself. Any ideas?

Using the subdomain as a parameter

I’ve got an ASP.net MVC (5.2) site that runs using several subdomains, where the name of the subdomain is the name of a client in my database. Basically what I want to do is use the subdomain as a variable within my action methods to allow me to get the correct data from my database.
I did something similar a few years back, but it’s messy and not intuitive, so was wondering if there’s a better way to do it than I was using before. Here’s what I did before:
protected override void OnActionExecuting(ActionExecutingContext filterContext) {
Session["subdomain"] = GetSubDomain(Request.Url);
}
private static string GetSubDomain(Uri url) {
string host = url.Host;
if (host.Split('.').Length > 1) {
int index = host.IndexOf(".");
string subdomain = host.Substring(0, index);
if (subdomain != "www") {
return subdomain;
}
}
return null;
}
Which basically assigned a key to the session variable if the subdomain was anything other than "www", but I’m really not happy with this way of doing it as it relies on me knowing that the session might contain this magic value!
Ideally I’d like to be able to create an attribute that I can decorate my classes/methods with that would extract the subdomain and then allow me to include a "subdomain" parameter in my action method that would contain the value extracted by the attribute. Is that even possible?
If that can’t be done, is there a better way of doing what I’m doing now without having to rely on the session?
Thanks,
Dylan
Your right this doesn't need to be stored in Session and IMHO shouldn't be, I would refactor this out into its own class and use HttpContext.Current.
public interface ISubDomainProvider
{
string SubDomain { get; set; }
}
public class SubDomainProvider : ISubDomainProvider
{
public SubDomainProvider()
{
string host = HttpContext.Current.Request.Url.Host; // not checked (off the top of my head
if (host.Split('.').Length > 1)
{
int index = host.IndexOf(".");
string subdomain = host.Substring(0, index);
if (subdomain != "www")
{
SubDomain = subdomain;
}
}
}
public string SubDomain { get; set; }
}
You choose how to use it, if your using an IoC container it would just be a case of injecting this class into your controller via the constructor, I like this because it is easier to Mock and Unit Test. Of course you can still do this:
public class SomeController : Controller
{
private readonly ISubDomainProvider _subDomainProvider;
public SomeController()
{
_subDomainProvider = new SubDomainProvider();
}
}
You could even create you own abstract Controller Class:
public abstract class MyAbstractController : Controller
{
public MyAbstractController()
{
SubDomain = new SubDomainProvider();
}
protected string SubDomain {get; set; }
}
public class SomeController : MyAbstractController
{
public ActionResult SomeAction()
{
// access the subdomain by calling the base base.SubDomain
}
}
You could set the name in the Session on the Session_Start event in the global.asax, this means it would only happen one time and would persist for the duration of the users' session
public void Session_Start(object sender, EventArgs e)
{
Session["subdomain"] = GetSubDomain(Request.Url);
}
Looks like there’s a good way of doing what I’m after at:
ASP.NET MVC Pass object from Custom Action Filter to Action
It essentially uses the route data to pass a custom parameter to the action, and can also pass objects other than simple strings etc.
On the plus side it avoids using the session and relying on magic values, but on the downside it means processing the URL for every request, which probably isn’t a good idea if a database is involved.

Categories

Resources