Difference between Wrapper and Native razor components [closed] - asp.net-core

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 3 years ago.
Improve this question
I have some clarifications on blazor-server-side. what is the difference between blazor native and wrapper components? Can any one please help me?

Disclaimer: I am not using blazor, but following it a little bit since its first presentation, but I never heard of native and wrapper components nor does the documentation include this wording. Therefore I might be wrong, but from the wording I get the idea, that by "wrapper" the interoperability is meant.
I am going to quote some text from this excelent blog post
Probably native, because the component is created only by .NET bl(r)azor without javascript:
Since Razor Components runs server side as a .NET Standard app, logic is written using .NET technologies. This is possible due to the Blazor framework which employs the RenderTree, a DOM abstraction similar to virtual DOMs used in popular JavaScript frameworks like Angular and React. Let's look at UI side of the framework to understand how components are written.
<p>Current count: #currentCount</p>
<button class="btn btn-primary" onclick="#IncrementCount">
Click me
</button>
#functions {
int currentCount = 0;
[Parameter] protected int CountBy { get; set; } = 1;
void IncrementCount()
{
currentCount += CountBy;
}
}
Probably wrapper, because we use in our component the interoperability layer a JS Function.
Additionally a Razor Components app can use dependencies the JavaScript ecosystems and through an interoperability layer the app can communicate bidirectionally with both .NET and JavaScript dependencies. This is helpful for situations where the Razor Components does not support a necessary browser/DOM API or an existing JavaScript library is useful.
GeoLocation.cs (.NET)
public class Geolocation
{
// ...
public async Task GetCurrentPosition(
Action<Position> onSuccess,
Action<PositionError> onError,
PositionOptions options = null)
{
OnGetPosition = onSuccess;
OnGetPositionError = onError;
await JSRuntime.Current.InvokeAsync<bool>(
"interopGeolocation.getCurrentPosition",
new DotNetObjectRef(this),
options);
}
// ...
}
interopGeolocation.js (Browser)
window.interopGeolocation = {
getCurrentPosition: function (geolocationRef, options) {
const success = (result) => {
geolocationRef.invokeMethodAsync(
'RaiseOnGetPosition',
interopGeolocation.toSerializeable(result));
};
const error = (er) =>
geolocationRef.invokeMethodAsync(
'RaiseOnGetPositionError',
er.code);
navigator.geolocation.getCurrentPosition(
success,
error,
options);
},
// ...

Related

TestCafe: Page Models for Single Page Applications

I have been struggling to figure out the best way to represent a single page application within TestCafe, and was wondering if anyone out there could help me?
Currently I am structuring it like the following (fake page names of course). I have greatly simplified it here for the sake of discussion, but the problem you should start to see is that as the app grows larger, the main page starts importing more and more. And each of those imports have imports, which might have more imports. So the cascading affect is causing TestCafe to drastically slow down when launching tests.
Does it make more sense to force the tests themselves to import all of the 'sections' they work with? What about for longer workflow tests that hit a bunch of sections? Does it still make sense then?
Any advice would be greatly appreciated!
import {Selector, t} from 'testcafe';
import {
ConsumerSection,
ManufacturerSection,
SupplierSection,
<AndSoOn>
} from './CarPageSections';
export class CarPage extends BasePage {
// BasePage contains all of the Header, Footer, NavBar, SideBar, Action Flyouts
CarSelectionTimer: Selector;
ModelSelectionModal: ModelSelectionModal;
SomeOtherModal: SomeOtherModal;
// Section Selectors
sectionPanels = {
ConsumerSection: null as ConsumerSection,
ManufacturerSection: null as ManufacturerSection,
SupplierSection: null as SupplierSection,
<AndSoOn>: null as <AndSoOn>
};
sections = {
ConsumerSection: null as SectionControl,
ManufacturerSection: null as SectionControl,
SupplierSection: null as SectionControl,
<AndSoOn>: null as SectionControl
};
constructor() {
this.CarSelectionTimer = Selector('#car-selection-timer');
// Sections
this.sections = {
ConsumerSection: new SectionControl('Consumer'),
ManufacturerSection: new SectionControl('Manufacturer'),
SupplierSection: new SectionControl('Supplier'),
<AndSoOn>: new SectionControl('<AndSoOn>')
};
this.sectionPanels = {
ConsumerSection: new ConsumerSection(this.sections.ConsumerSection.control),
ManufacturerSection: new ManufacturerSection(this.sections.ManufacturerSection.control),
SupplierSection: new SupplierSection(this.sections.SupplierSection.control),
<AndSoOn>: new <AndSoOn>(this.sections.<AndSoOn>.control)
};
this.ModelSelectionModal = new ModelSelectionModal();
this.SomeOtherModal = new SomeOtherModal();
}
async SomeActionToPerformOnThePage(params) {
// DO STUFF
}
async SomeOtherActionToPerformOnThePage(params) {
// DO STUFF
}
}
Considerations to handle:
Constructors with parameters like ConsumerSection(control) above.
Using files to export multiple objects / classes to simplify importing in tests (or other models).
Questions to consider:
Should every model be decoupled from every other model?
Without coupling models, how do you make it as easy as possible to work with? In other test frameworks, you can hand back a new page type upon a given method/action: i.e. LoginPage.Submit() returns HomePage().
It's difficult to determine the cause of the issue without your full page model. Your issue looks similar to this one: https://github.com/DevExpress/testcafe/issues/4054. Please check that Github thread and apply the recommendations from it.
If this does not help, please share your full page model. If you cannot share it here, you can send it at support#devexpress.com

Blazor concurrency problem using Entity Framework Core

My goal
I want to create a new IdentityUser and show all the users already created through the same Blazor page. This page has:
a form through you will create an IdentityUser
a third-party's grid component (DevExpress Blazor DxDataGrid) that shows all users using UserManager.Users property. This component accepts an IQueryable as a data source.
Problem
When I create a new user through the form (1) I will get the following concurrency error:
InvalidOperationException: A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread-safe.
I think the problem is related to the fact that CreateAsync(IdentityUser user) and UserManager.Users are referring the same DbContext
The problem isn't related to the third-party's component because I reproduce the same problem replacing it with a simple list.
Step to reproduce the problem
create a new Blazor server-side project with authentication
change Index.razor with the following code:
#page "/"
<h1>Hello, world!</h1>
number of users: #Users.Count()
<button #onclick="#(async () => await Add())">click me</button>
<ul>
#foreach(var user in Users)
{
<li>#user.UserName</li>
}
</ul>
#code {
[Inject] UserManager<IdentityUser> UserManager { get; set; }
IQueryable<IdentityUser> Users;
protected override void OnInitialized()
{
Users = UserManager.Users;
}
public async Task Add()
{
await UserManager.CreateAsync(new IdentityUser { UserName = $"test_{Guid.NewGuid().ToString()}" });
}
}
What I noticed
If I change Entity Framework provider from SqlServer to Sqlite then the error will never show.
System info
ASP.NET Core 3.1.0 Blazor Server-side
Entity Framework Core 3.1.0 based on SqlServer provider
What I have already seen
Blazor A second operation started on this context before a previous operation completed: the solution proposed doesn't work for me because even if I change my DbContext scope from Scoped to Transient I still using the same instance of UserManager and its contains the same instance of DbContext
other guys on StackOverflow suggests creating a new instance of DbContext per request. I don't like this solution because it is against Dependency Injection principles. Anyway, I can't apply this solution because DbContext is wrapped inside UserManager
Create a generator of DbContext: this solution is pretty like the previous one.
Using Entity Framework Core with Blazor
Why I want to use IQueryable
I want to pass an IQueryable as a data source for my third-party's component because its can apply pagination and filtering directly to the Query. Furthermore IQueryable is sensitive to CUD
operations.
UPDATE (08/19/2020)
Here you can find the documentation about how to use Blazor and EFCore together
UPDATE (07/22/2020)
EFCore team introduces DbContextFactory inside Entity Framework Core .NET 5 Preview 7
[...] This decoupling is very useful for Blazor applications, where using IDbContextFactory is recommended, but may also be useful in other scenarios.
If you are interested you can read more at Announcing Entity Framework Core EF Core 5.0 Preview 7
UPDATE (07/06/2020)
Microsoft released a new interesting video about Blazor (both models) and Entity Framework Core. Please take a look at 19:20, they are talking about how to manage concurrency problem with EFCore
General solution
I asked Daniel Roth BlazorDeskShow - 2:24:20 about this problem and it seems to be a Blazor Server-Side problem by design.
DbContext default lifetime is set to Scoped. So if you have at least two components in the same page which are trying to execute an async query then we will encounter the exception:
InvalidOperationException: A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread-safe.
There are two workaround about this problem:
(A) set DbContext's lifetime to Transient
services.AddDbContext<ApplicationDbContext>(opt =>
opt.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")), ServiceLifetime.Transient);
(B) as Carl Franklin suggested (after my question): create a singleton service with a static method which returns a new instance of DbContext.
anyway, each solution works because they create a new instance of DbContext.
About my problem
My problem wasn't strictly related to DbContext but with UserManager<TUser> which has a Scoped lifetime. Set DbContext's lifetime to Transient didn't solve my problem because ASP.NET Core creates a new instance of UserManager<TUser> when I open the session for the first time and it lives until I don't close it. This UserManager<TUser> is inside two components on the same page. Then we have the same problem described before:
two components that own the same UserManager<TUser> instance which contains a transient DbContext.
Currently, I solved this problem with another workaround:
I don't use UserManager<TUser> directly instead, I create a new instance of it through IServiceProvider and then it works. I am still looking for a method to change the UserManager's lifetime instead of using IServiceProvider.
tips: pay attention to services' lifetime
This is what I learned. I don't know if it is all correct or not.
I downloaded your sample and was able to reproduce your problem. The problem is caused because Blazor will re-render the component as soon as you await in code called from EventCallback (i.e. your Add method).
public async Task Add()
{
await UserManager.CreateAsync(new IdentityUser { UserName = $"test_{Guid.NewGuid().ToString()}" });
}
If you add a System.Diagnostics.WriteLine to the start of Add and to the end of Add, and then also add one at the top of your Razor page and one at the bottom, you will see the following output when you click your button.
//First render
Start: BuildRenderTree
End: BuildRenderTree
//Button clicked
Start: Add
(This is where the `await` occurs`)
Start: BuildRenderTree
Exception thrown
You can prevent this mid-method rerender like so....
protected override bool ShouldRender() => MayRender;
public async Task Add()
{
MayRender = false;
try
{
await UserManager.CreateAsync(new IdentityUser { UserName = $"test_{Guid.NewGuid().ToString()}" });
}
finally
{
MayRender = true;
}
}
This will prevent re-rendering whilst your method is running. Note that if you define Users as IdentityUser[] Users you will not see this problem because the array is not set until after the await has completed and is not lazy evaluated, so you don't get this reentrancy problem.
I believe you want to use IQueryable<T> because you need to pass it to 3rd party components. The problem is, different components can be rendered on different threads, so if you pass IQueryable<T> to other components then
They might render on different threads and cause the same problem.
They most likely will have an await in the code that consumes the IQueryable<T> and you'll have the same problem again.
Ideally, what you need is for the 3rd party component to have an event that asks you for data, giving you some kind of query definition (page number etc). I know Telerik Grid does this, as do others.
That way you can do the following
Acquire a lock
Run the query with the filter applied
Release the lock
Pass the results to the component
You cannot use lock() in async code, so you'd need to use something like SpinLock to lock a resource.
private SpinLock Lock = new SpinLock();
private async Task<WhatTelerikNeeds> ReadData(SomeFilterFromTelerik filter)
{
bool gotLock = false;
while (!gotLock) Lock.Enter(ref gotLock);
try
{
IUserIdentity result = await ApplyFilter(MyDbContext.Users, filter).ToArrayAsync().ConfigureAwait(false);
return new WhatTelerikNeeds(result);
}
finally
{
Lock.Exit();
}
}
Perhaps not the best approach but rewriting async method as non-async fixes the problem:
public void Add()
{
Task.Run(async () =>
await UserManager.CreateAsync(new IdentityUser { UserName = $"test_{Guid.NewGuid().ToString()}" }))
.Wait();
}
It ensures that UI is updated only after the new user is created.
The whole code for Index.razor
#page "/"
#inherits OwningComponentBase<UserManager<IdentityUser>>
<h1>Hello, world!</h1>
number of users: #Users.Count()
<button #onclick="#Add">click me. I work if you use Sqlite</button>
<ul>
#foreach(var user in Users.ToList())
{
<li>#user.UserName</li>
}
</ul>
#code {
IQueryable<IdentityUser> Users;
protected override void OnInitialized()
{
Users = Service.Users;
}
public void Add()
{
Task.Run(async () => await Service.CreateAsync(new IdentityUser { UserName = $"test_{Guid.NewGuid().ToString()}" })).Wait();
}
}
I found your question looking for answers about the same error message you had.
My concurrency issue appears to have been due to a change that triggered a re-rendering of the visual tree to occur at the same time as (or due to the fact that) I was trying to call DbContext.SaveChangesAsync().
I solved this by overriding my component's ShouldRender() method like this:
protected override bool ShouldRender()
{
if (_updatingDb)
{
return false;
}
else
{
return base.ShouldRender();
}
}
I then wrapped my SaveChangesAsync() call in code that set a private bool field _updatingDb appropriately:
try
{
_updatingDb = true;
await DbContext.SaveChangesAsync();
}
finally
{
_updatingDb = false;
StateHasChanged();
}
The call to StateHasChanged() may or may not be necessary, but I've included it just in case.
This fixed my issue, which was related to selectively rendering a bound input tag or just text depending on if the data field was being edited. Other readers may find that their concurrency issue is also related to something triggering a re-render. If so, this technique may be helpful.
Well, I have a quite similar scenario with this, and I 'solve' mine is to move everything from OnInitializedAsync() to
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if(firstRender)
{
//Your code in OnInitializedAsync()
StateHasChanged();
}
{
It seems solved, but I had no idea to find out the proves. I guess just skip from the initialization to let the component success build, then we can go further.
/******************************Update********************************/
I'm still facing the problem, seems I'm giving a wrong solution to go. When I checked with this Blazor A second operation started on this context before a previous operation completed I got my problem clear. Cause I'm actually dealing with a lot of components initialization with dbContext operations. According to #dani_herrera mention that if you have more than 1 component execute Init at a time, probably the problem appears.
As I took his advise to change my dbContext Service to Transient, and I get away from the problem.
#Leonardo Lurci Had covered conceptually. If you guys are not yet wanting to move to .NET 5.0 preview, i would recommend looking at Nuget package 'EFCore.DbContextFactory', documentation is pretty neat. Essential it emulates AddDbContextFactory. Ofcourse, it creates a context per component.
So far, this is working fine for me so far without any problems...
I ensure single-threaded access by only interacting with my DbContext via a new DbContext.InvokeAsync method, which uses a SemaphoreSlim to ensure only a single operation is performed at a time.
I chose SemaphoreSlim because you can await it.
Instead of this
return Db.Users.FirstOrDefaultAsync(x => x.EmailAddress == emailAddress);
do this
return Db.InvokeAsync(() => ...the query above...);
// Add the following methods to your DbContext
private SemaphoreSlim Semaphore { get; } = new SemaphoreSlim(1);
public TResult Invoke<TResult>(Func<TResult> action)
{
Semaphore.Wait();
try
{
return action();
}
finally
{
Semaphore.Release();
}
}
public async Task<TResult> InvokeAsync<TResult>(Func<Task<TResult>> action)
{
await Semaphore.WaitAsync();
try
{
return await action();
}
finally
{
Semaphore.Release();
}
}
public Task InvokeAsync(Func<Task> action) =>
InvokeAsync<object>(async () =>
{
await action();
return null;
});
public void InvokeAsync(Action action) =>
InvokeAsync(() =>
{
action();
return Task.CompletedTask;
});
#Leonardo Lurci has a great answer with multiple solutions to the problem. I will give my opinion about every solution and which I think it is the best one.
Making DBContext transient - it is a solution but it is not optimized for this cases..
Carl Franklin suggestion - the singleton service will not be able to control the lifetime of the context and will depend on the service requester to dispose the context after use.
Microsoft documentation they talk about injecting DBContext Factory into a component with the IDisposable interface to Dispose the context when the component is destroied. This is not a very good solution, because a lot of problems happen with it, like: performing a context operation and leaving the component before it finishes that operation, will dispose the context and throw exception..
Finally. The best solution so far is to inject the DBContext Factory in the component yes, but whenever you need it, you create a new instance with using statement like bellow:
public async Task GetSomething()
{
using var context = DBFactory.CreateDBContext();
return await context.Something.ToListAsync();
}
Since DbFactory is optimazed when creating new context instances, there is no significante overhead, making it a better choice and better performing than Transient context, it also disposes the context at the end of the method because of "using" statement.
Hope it was useful.

Plagiarism Checker C# based API [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 7 years ago.
Improve this question
I am looking for a plagiarism checker API that is based on C# code. I need to use it on my web service. I need to easily query plagiarism checking engine and get result for the originality of the text.
If you know any service that is similar to what I am asking it would be great!
I’m using an online plagiarism checker service called Copyleaks which offers an interface to integrate with their API (HTTP REST). It also provides interface which is fully compatible with C#.
Steps to integrate with Copyleaks API:
Register on Copyleaks website.
Create a new C# Console application project and install Copyleaks’ Nuget Package.
Use the following code which performs a webpage scan.
This code was taken from its SDK (GitHub):
public void Scan(string username, string apiKey, string url)
{
// Login to Copyleaks server.
Console.Write("User login... ");
LoginToken token = UsersAuthentication.Login(username, apiKey);
Console.WriteLine("\t\t\tSuccess!");
// Create a new process on server.
Console.Write("Submiting new request... ");
Detector detector = new Detector(token);
ScannerProcess process = detector.CreateProcess(url);
Console.WriteLine("\tSuccess!");
// Waiting to process to be finished.
Console.Write("Waiting for completion... ");
while (!process.IsCompleted())
Thread.Sleep(1000);
Console.WriteLine("\tSuccess!");
// Getting results.
Console.Write("Getting results... ");
var results = process.GetResults();
if (results.Length == 0)
{
Console.WriteLine("\tNo results.");
}
else
{
for (int i = 0; i < results.Length; ++i)
{
Console.WriteLine();
Console.WriteLine("Result {0}:", i + 1);
Console.WriteLine("Domain: {0}", results[i].Domain);
Console.WriteLine("Url: {0}", results[i].URL);
Console.WriteLine("Precents: {0}", results[i].Precents);
Console.WriteLine("CopiedWords: {0}", results[i].NumberOfCopiedWords);
}
}
}
Call the function with your Username, API-Key and the URL of the content you wish to scan for plagiarism.
You can read more about its server in "How To" tutorial.

Aftersubmit event in Trirand jqgrid Commercial

Can someone tell me how to use AfterSubmit Event in Commercial jqgrid for ASP.Net MVC? Im not able to find enough documentation for commercial JQGrid.
I solved it by defining the ClientSideEvents - on the server side.
chargesJQGrid.ClientSideEvents.AfterAddDialogRowInserted = "AfterInsert";
chargesJQGrid.ClientSideEvents.AfterDeleteDialogRowDeleted = "AfterDelete";
Then implemented the javascript method
function AfterInsert(response,postdata)
{
}
function AfterDelete(response,postdata)
{
}

How to minify JavaScript inside script block on view pages

How to minify JavaScript inside a view page's script block with minimal effort?
I have some page specific scripts that would like to put on specific view pages. But the ASP.NET MVC4 bundling and minification only works with script files, not script code inside a view page.
UPDATE
I took Sohnee's advice to extract the scripts into files. But I need to use them on specific pages so what I end up doing is:
on layout page, i created an optional section for page specific javascript block:
#RenderSection("js", required: false)
</body>
then in the view page, let's say Index.cshtml, i render the script section like such:
#section js{
#Scripts.Render("~/bundles/js/" + Path.GetFileNameWithoutExtension(this.VirtualPath))
}
as you can see, it assumes the javascript filename (index.js) is the same as the view page name (index.cshtml). then in the bundle config, i have:
var jsFiles = Directory.GetFiles(HttpContext.Current.Server.MapPath("Scripts/Pages"), "*.js");
foreach (var jsFile in jsFiles)
{
var bundleName = Path.GetFileNameWithoutExtension(jsFile);
bundles.Add(new ScriptBundle("~/bundles/js/" + bundleName).Include(
"~/Scripts/pages/" + Path.GetFileName(jsFile)));
}
then, if you are on index page, the HTML output will be:
<script src="/bundles/js/Index?v=ydlmxiUb9gTRm508o0SaIcc8LJwGpVk-V9iUQwxZGCg1"></script>
</body>
and if you are on products page, the HTML output will be:
<script src="/bundles/js/Products?v=ydlmxiUb9gTRm508o0SaIcc8LJwGpVk-V9iUQwxZGCg1"></script>
</body>
You can minify inline scripts using this HTML helper
using Microsoft.Ajax.Utilities;
using System;
namespace System.Web.Mvc
{
public class HtmlHelperExtensions
{
public static MvcHtmlString JsMinify(
this HtmlHelper helper, Func<object, object> markup)
{
string notMinifiedJs =
markup.Invoke(helper.ViewContext)?.ToString() ?? "";
var minifier = new Minifier();
var minifiedJs = minifier.MinifyJavaScript(notMinifiedJs, new CodeSettings
{
EvalTreatment = EvalTreatment.MakeImmediateSafe,
PreserveImportantComments = false
});
return new MvcHtmlString(minifiedJs);
}
}
}
And inside your Razor View use it like this
<script type="text/javascript">
#Html.JsMinify(#<text>
window.Yk = window.Yk || {};
Yk.__load = [];
window.$ = function (f) {
Yk.__load.push(f);
}
</text>)
</script>
If you use System.Web.Optimization than all necessary dlls are already referenced otherwise you can install WebGrease NuGet package.
Some additional details available here: http://www.cleansoft.lv/minify-inline-javascript-in-asp-net-mvc-with-webgrease/
EDIT:
Replaced DynamicInvoke() with Invoke(). No need for runtime checks here, Invoke is much faster than DynamicInvoke. Added .? to check for possible null.
The way to do this with minimal effort is to extract it into a script file. Then you can use bundling and minification just as you want.
If you want to minify it inline, it will be a much greater effort than simply moving the script off-page.
Based on #samfromlv's answer, I created an extension to handle CSS as well. It also takes BundleTable.EnableOptimizations into consideration.
OptimizationExtensions.cs
Adding in an answer for ASP.NET MVC Core. The solution I used to minify inline JS and razor generated html was WebMarkupMin.
It ultimately boiled down to adding these two minuscule changes to my project:
public void Configure(IApplicationBuilder app)
{
app.UseStaticFiles();
//added
app.UseWebMarkupMin();
app.UseMvc(.....
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
//added
services.AddWebMarkupMin(
options =>
{
//i comment these two lines out after testing locally
options.AllowMinificationInDevelopmentEnvironment = true;
options.AllowCompressionInDevelopmentEnvironment = true;
})
.AddHttpCompression();
}
There's a great blog post by Andrew Lock (author of ASP.NET Core in Action) about using WebMarkupMin https://andrewlock.net/html-minification-using-webmarkupmin-in-asp-net-core/ WebMarkupMin is highly configurable and Andrew's post goes way more indepth, highly recommended reading it intently before just copying and pasting.
A little late for the party, but for .NET Core you could use a TagHelper to minify the content of a script tag like this:
[HtmlTargetElement("script", Attributes = MinifyAttributeName)]
public class ScriptTagHelper : TagHelper
{
private const string MinifyAttributeName = "minify";
[HtmlAttributeName(MinifyAttributeName)]
public bool ShouldMinify { get; set; }
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
if (!ShouldMinify)
{
await base.ProcessAsync(context, output);
return;
}
var textChildContent = await output.GetChildContentAsync();
var scriptContent = textChildContent.GetContent();
// or use any other minifier here
var minifiedContent = NUglify.Uglify.Js(scriptContent).Code;
output.Content.SetHtmlContent(minifiedContent);
}
}
and then use it in your views:
<script minify="true">
...
</script>
Fenton had a great answer about this: "rather than minify inline JavaScript code, externalize the inline JavaScript code and then you can minify with any standard JavaScript minifiers / bundlers."
Here is how you externalize the JavaScript: https://webdesign.tutsplus.com/tutorials/how-to-externalize-and-minify-javascript--cms-30718
Here is my direct answer to minify the inline JavaScript code (require a bit of manual work).
Copy the inline JavaScript code snippet and paste them into a separate JavaScript file and save it, e.g. inline.js
Use esbuild to minify the inline code snippet in inline.js, see more details about minification here
esbuild --minify < inline.js > inline-minified.js
Copy the minified JavaScript code snippet in inline-minified.js and paste it back into the original HTML to replace the original code inside of the tag.
Done.