How to use Javascript in .NET MAUI Blazor App Hybrid - c#

when ever i try to put javascript link in .NET MAUI Blazor App Hybrid it says "Script tags should not be placed inside components because they cannot be updated dynamically."
Please help
i want to use javascript in .NET MUAI Blazor App Hybrid

You should try to use JavaScript using the modular approach.
Let's assume you have file hello.js which contain functions like below:
export function helloWorld (name) {
console.log("Hello "+ name);
}
Try creating an interop for your JS file like:
public class Interop : IDisposable
{
private readonly string _filePath;
protected readonly IJSRuntime _jsRuntime;
private Task<IJSObjectReference> _module;
public Interop(IJSRuntime jsRuntime)
{
_filePath = filePath;
_jsRuntime = jsRuntime;
}
public Task<IJSObjectReference> Module => _module ??= _jsRuntime.InvokeAsync<IJSObjectReference>("import", "./hello.js").AsTask();
//create a method to call function from the JS file
public async void SayHelloWorld(string name)
{
var module = await Module;
await module.InvokeVoidAsync("helloWorld", name);
}
public void Dispose()
{
if (_module is not null)
{
((IDisposable)_module).Dispose();
}
}
}
then use the js function like:
new Interop(JSRuntime).SayHelloWorld("Adam");

Related

Application_Start event on ASP.NET Core 6.0

How would I go about setting global variables in ASP.NET Core 6.0(razor pages)?
I have some information in the database, for example, ServiceName, ContactEmail and so on, and want to save it to my static class.
I don't want to access the database every time I need to display the information.
In addition, there aren't Global.asax in ASP.NET Core .
In ASP.NET MVC 5 (based on .net framework), I could do it like
// global.asax
protected void Application_Start() {
var context = new DefaultConnection();
MyConfig.ServiceName = context.GlobalSettings.SingleOrDefault().ServiceName;
// MyConfig is my static class
}
But I don't know where I should do it in ASP.NET Core project.
How can I do that? Please help me.
So lazy-loading is probably a very good choice for you.
Step 1: Create a data service that provides your data.
public interface IStaticDbData // Think of a better name!
{
public Task<string> GetContactEmailAsync();
public Task<string> GetServiceNameAsync();
// Etc.
}
public class StaticDbData : IStaticDbData
{
// Since we want a singleton, we'll have to synchronize the data fetching.
private object _lock = new object();
private string _contactEmail;
private string _serviceName;
// Etc.
// Try to create a single function that loads all of the data in one round trip to the DB.
// This will run in its own thread, so the calling thread can be awaited.
private Task LoadAllDataAsync()
=> Task.Run(() =>
{
lock (_lock)
{
//Re-check after locking.
if (_contactEmail != null)
{
return;
}
// Database code here to extract your data.
// Save to the individual fields.
}
});
public async Task<string> GetContactEmailAsync()
{
// See if data is there.
if (_contactEmail != null)
{
return _contactEmail;
}
// Data was not there. Load data.
await LoadAllDataAsync();
return _contactEmail;
}
public async Task<string> GetServiceNameAsync()
{
if (_serviceName != null)
{
return _serviceName;
}
await LoadAllDataAsync();
return _serviceName;
}
}
Step 2: Now that you have your service interface and service implementation, register the m in the IoC container. In program.cs:
builder.Services.AddSingleton<IStaticDbData, StaticDbData>();
Step 3: Consume the service as you would any other service.
public class SomeOtherServiceOrControllerOrWhatever
{
private IStaticDbData StaticDbDataSvc { get; }
// Constructor-injected.
public SomeOtherServiceOrControllerOrWhatever(IStaticDbData staticDbDataSvc)
{
StaticDbDataSvc = staticDbDataSvc;
}
}
NOTE: Make sure that your consuming services are also registered and resolved using the IoC container.
This is sudo code
You can create a static class with static properties:
public static class MyConfig
{
public static string Setting1 {set; get;}
...
}
then write a method to fetch data from your database and fill MyConfig and in the Program.cs file just call that method:
app.MapControllers();
app.Run();
CallYourMethodHere(); <-----
another is you can do this:
first create a static class:
public static class MyConfig
{
private static Dictionary<string, string> MyConfigs {set; get;}
private static Dictionary<string, string> GetConfigFromDatabase(bool forceToFill)
{
if(MyConfigs == null || MyConfigs.Any() == false || forceToFill == true)
{
//Fetch Data From Database and Fill MyConfig
}
}
public static string GetConfig(string configName)
{
return GetConfigFromDatabase(false)[configName];
}
}
In solution 2 you have to consider some thread-safe and race condition concepts.

Call js generated from typescript in blazor using IJSObjectReference

Hi I have a question about using Blazor and typescript.
My typescript:
namespace JSInteropWithTypeScript {
export class Generator {
public showAlert(): void {
alert("INTEROP");
}
}
export function Load(): void {
window['generator'] = new Generator();
}
}
JSInteropWithTypeScript.Load();
And here is generated JS:
var JSInteropWithTypeScript;
(function (JSInteropWithTypeScript) {
var Generator = /** #class */ (function () {
function Generator() {
}
Generator.prototype.showAlert = function () {
alert("INTEROP");
};
return Generator;
}());
JSInteropWithTypeScript.Generator = Generator;
function Load() {
window['generator'] = new Generator();
}
JSInteropWithTypeScript.Load = Load;
})(JSInteropWithTypeScript || (JSInteropWithTypeScript = {}));
JSInteropWithTypeScript.Load();
//# sourceMappingURL=generator.js.map
Next I want to call compiled js script so I have a simple component with button:
#namespace Components.Reports.Components.Generator
#inherits ControlsBase
<button class="btn btn-success" #onclick="ShowAlert"> BUTON </button>
In base class for this component I want to lazy load (so i do not need to add every script in _Host just use them when they are needed) this script file and call this function, so I made this contraption:
public class ControlsBase : ComponentBase, IAsyncDisposable
{
private readonly Lazy<Task<IJSObjectReference>> moduleTask;
[Inject]
protected IJSRuntime JsRuntime { get; set; }
public ControlsBase()
{
Task<IJSObjectReference> jsInvokes() => this.JsRuntime.InvokeAsync<IJSObjectReference>("import", "./_content/Components/js/generator.js").AsTask();
this. moduleTask = new Lazy<Task<IJSObjectReference>>(jsInvokes);
}
public async Task ShowAlert()
{
try
{
var module = await moduleTask.Value;
await module.InvokeVoidAsync("showAlert");
}
catch (JSException jsException)
{
logger.Error($"Problem with js interop {jsException}");
}
}
public async ValueTask DisposeAsync()
{
if (moduleTask.IsValueCreated)
{
var module = await moduleTask.Value;
await module.DisposeAsync();
}
}
}
So now I want to call this, my problem/question is when I call compiled js script I am getting
Could not find 'showAlert' ('showAlert' was undefined).
When i use vanilla code like:
export function showAlert() {
alert("INTEROP");
}
it works.
So what am I doing wrong with typescript?
EDIT
Using #Haytam guide I remade my ts to simple:
namespace JSInteropWithTypeScript {
export class Generator {
public ShowAlert(): void {
alert("INTEROP");
}
}
}
function showAlert(): void {
let generator = new JSInteropWithTypeScript.Generator();
generator.ShowAlert();
}
And it compiles to js:
var JSInteropWithTypeScript;
(function (JSInteropWithTypeScript) {
var Generator = /** #class */ (function () {
function Generator() {
}
Generator.prototype.ShowAlert = function () {
alert("INTEROP");
};
return Generator;
}());
JSInteropWithTypeScript.Generator = Generator;
})(JSInteropWithTypeScript || (JSInteropWithTypeScript = {}));
function showAlert() {
var generator = new JSInteropWithTypeScript.Generator();
generator.ShowAlert();
}
But to work properly I need to manually add export before function in js.I know that the cause for this error is that TypeScript, by default, produces JavaScript code that is designed for use through various package managers that cater to the exports and modules. Such tools are usully Node.js or WebPack. I can turn it off by using tsconfig:
{
"compilerOptions": {
"module": "none"
}
}
But still js interop works only when function is marked as export in js. Any ideas how to handle that?
EDIT2:
I think problem lays in lazy loding, added generated js script as static resource:
<script src="_content/Components/js/generator.js"></script>
and used code:
public async Task OnClick()
{
try
{
await this.JsRuntime.InvokeVoidAsync("showAlert");
}
catch (JSException jsException)
{
logger.Error($"Problem with js interop {jsException}");
}
}
In my component base and it is alive, but still just for curiosity I want to make n this lazy loading. Any suggestions would be appreciated.
I see two problems here:
Blazor JS Isolation works with JavaScript Modules. Putting functions in window is not required anymore, that's why they added this isolation in the first place.
Since Blazor JS Isolation works with JS modules, it expects exported functions directly. I believe no matter what you try, the generated JS from using TS classes will not work.
I think you should try to export the functions directly.
EDIT
Based on your latest edit, doesn't this work?
export function showAlert(): void {
let generator = new JSInteropWithTypeScript.Generator();
generator.ShowAlert();
}

Blazor service injection not working properly

I have this code in Blazor 3.0.0-preview4-19216-03 targeting a client app:
namespace BlazorShared.Services
{
public interface ILogin
{
Task<string> Login();
}
public class LoginService : ILogin
{
private HttpClient _client;
public LoginService(HttpClient client)
{
_client = client;
}
public async Task<string> Login()
{
var myclient = new HttpClient();
var responseMessage = await myclient.GetAsync("http://www.google.es");
var content = await responseMessage.Content.ReadAsStringAsync();
Debug.WriteLine(content);
return content;
}
}
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ILogin, LoginService>();
}
public void Configure(IComponentsApplicationBuilder app)
{
app.AddComponent<App>("app");
}
}
and this HTML
#functions {
public async Task Submit()
{
var str = await LoginService.Login(null, null, null);
Console.WriteLine(str);
}
}
Full Razor file: https://pastebin.com/3LbQQvk0
I have tested it and the web request never gets done and I'm not able to show the service response in the client. I have tried debugging in chrome following the instructions And I see the service method is being called but the constructor of the service is not, and as I understood Blazor should inject the HttpClient. Any ideas what can be happening? Thanks.
The following is wrong even if it is not the culprit...
In the LoginService you define an HttpClient variable into which you assign an instance of HttpClient provided by DI. On the other hand, you define a new HttpClient object named myclient and use it in the Login method.
You should use the object provided by DI. You shouldn't yourself define HttpClient objects. Why ? Because Blazor configure for you the HttpClient it creates. For instance, setting the Document base URI, which enable your web app to be navigated as an SPA app.
Hope this helps...

Use SignalR 2.0 Owin pipeline with my SignalR lib

I'm looking into upgrading this library to SignalR 2.0
https://github.com/AndersMalmgren/SignalR.EventAggregatorProxy
I want it to support the 2.0 Owin pipeline with the IAppBuilder interface, instead of using RouteCollection like SignalR 1.x did.
Problem is, how can I get the routecollection from the IAppBuilder? I need it to regiser a custom IHttpHandler that handles my custom js script (Like SignalR registers their hub script)
1.x setup of my lib looks like this
public static class SignalRConfig
{
public static void Register(RouteCollection routes)
{
routes.MapHubs();
routes.MapEventProxy<Contracts.Events.Event>();
}
}
My goal for 2.0 config it something like this
public static class SignalRConfig
{
public static void Configuration(IAppBuilder app)
{
app.MapSignalR();
app.MapEventProxy<Contracts.Events.Event>();
}
}
My code that is dependent on RouteCollection looks like this
public static class RouteCollectionExtensions
{
public static void MapEventProxy<TEvent>(this RouteCollection routes)
{
Bootstrapper.Init<TEvent>();
routes.Add(new Route(
"eventAggregation/events",
new RouteValueDictionary(),
new RouteValueDictionary() {{"controller", string.Empty}},
new EventScriptRouteHandler<TEvent>()));
}
}
edit: Looks like its very complex to get Owin to serve a request, can I use helper methods in SignalR 2.0 to register a route and a handler to that route?
update:
Looks like im on the right track with this code
using Owin;
using SignalR.EventAggregatorProxy.Boostrap;
namespace SignalR.EventAggregatorProxy.Owin
{
public static class AppBuilderExtensions
{
public static void MapEventProxy<TEvent>(this IAppBuilder app)
{
Bootstrapper.Init<TEvent>();
app.Map("/eventAggregation/events", subApp => subApp.Use<EventScriptMiddleware<TEvent>>());
}
}
}
Now I just need to implement the EventScriptMiddleware
update: Last piece of the puzzle, now I just need my middleware to actually spit out the javacript, should be easy
namespace SignalR.EventAggregatorProxy.Owin
{
public class EventScriptMiddleware<TEvent> : OwinMiddleware
{
public EventScriptMiddleware(OwinMiddleware next) : base(next)
{
}
public override Task Invoke(IOwinContext context)
{
return context.Response.WriteAsync("Hello world!!");
}
}
}
Final version looks like this, app builder extension
public static class AppBuilderExtensions
{
public static void MapEventProxy<TEvent>(this IAppBuilder app)
{
Bootstrapper.Init<TEvent>();
app.Map("/eventAggregation/events", subApp => subApp.Use<EventScriptMiddleware<TEvent>>());
}
}
Invoke method in Middleware
public override Task Invoke(IOwinContext context)
{
var response = context.Response;
response.ContentType = "application/javascript";
response.StatusCode = 200;
if (ClientCached(context.Request, scriptBuildDate))
{
response.StatusCode = 304;
response.Headers["Content-Length"] = "0";
response.Body.Close();
response.Body = Stream.Null;
return Task.FromResult<Object>(null);
}
response.Headers["Last-Modified"] = scriptBuildDate.ToUniversalTime().ToString("r");
return response.WriteAsync(js);
}
Full source code here
https://github.com/AndersMalmgren/SignalR.EventAggregatorProxy/tree/master/SignalR.EventAggregatorProxy/Owin

How to handle application start event in ASP.NET module

I am writing an asp.net HTTP module which needs to read configuration data once from a local file (say config.xml stored in application root directory) and then based on configuration perform some processing on incoming requests.
Since there is no Application_Start/Application_init hooking available in Asp.NET modules, what would be the best way to handle the scenario. I am trying to avoid reading configuration file each time a request comes. Ideally, I want to read the config file when application starts.
I need to code this in http module only and do not want to use Global.asax
I'd go for a simple property, something like this ...
public MyConfig Config
{
get
{
MyConfig _config = Application["MyConfig"] as MyConfig;
if (_config == null)
{
_config = new MyConfig(...);
Application["MyConfig"] = _config;
}
return _config;
}
}
that way you just access whatever you need from Config via the property ...
int someValue = Config.SomeValue;
and it's loaded into the application object if it hasn't been already
If you need the config on a per-user basis rather than globally, then just use Session["MyConfig"] instead of Application["MyConfig"]
Not sure if this would work, but you might be able to implement this in the module's init method.
In the init method of your httpmodule you can hook up to the event in the context.
For example :
public void Init(HttpApplication context)
{
context.PostRequestHandlerExecute += (sender, e) =>
{
Page p = context.Context.Handler as Page;
if (p != null)
{
///Code here
}
};
}
public SomeHttpModule : IHttpModule
{
private static readonly Configuration Configuration =
ConigurationReader.Read();
}
static variable did the trick. here is the code if someone is interested -
static string test;
public void Init(HttpApplication application)
{
application.BeginRequest +=(new EventHandler(this.Application_BeginRequest));
test = "hi";
application.EndRequest +=(new EventHandler(this.Application_EndRequest));
}
private void Application_BeginRequest(Object source,EventArgs e)
{
{
HttpApplication application = (HttpApplication)source ;
HttpContext context = application.Context;
context.Response.Write(test);
}
}

Categories

Resources