Using DI in components works great with inject (#inject or in Codebehind with [inject]).
Now how does it work if you want to use DI in a normal class? I found exactly the same question here:
Blazor - Call JavaScript from C# class
Unfortunately, although the question is marked as answered, it is not clear how this is actually supposed to work. I'll summarize it here:
you create a class which should be used as DI, here for example for JSInterop (e.g. to use JavaScript outside from components):
public class JsInteropForClasses
{
private readonly IJSRuntime jSRuntime;
public JsInteropForClasses(IJSRuntime jSRuntime)
{
this.jSRuntime = jSRuntime;
}
public async void MessageBox()
{
await jSRuntime.InvokeVoidAsync("MessageBox", "DI question!!!");
}
}
you register service in Program.cs
builder.Services.AddTransient<JsInteropForClasses>();
and now comes the crucial question, how do you use this in a other class that is not a component (so where you can't use inject)?
I applied the suggested solution, but I get argument problem because constructor of DI-class expects an object type IJSRuntime and I don't pass anything during creation:
JsInteropForClasses js = new JsInteropForClasses();
js.MessageBox();
Therefore, I can't use DI (because program can't start).
The following variant does not work either:
JsInteropForClasses js;
js.MessageBox();
I searched for a long time, but only found this one entry (see link). All others refer to DI and components.
Does anyone know about how to use DI in own class (not components)?
Thanks
When you register a service in the DI container (in Program.cs) initialization of the service will be handled by the DI. That means you should NOT call the constructor on your own. The only thing you need to do is to register the service (with the interface if necessary) properly. Like this:
builder.Services.AddTransient<JsInteropForClasses>();
or
builder.Services.AddTransient<IJsInteropForClasses, JsInteropForClasses>();
Then you can inject the service (or interface of the service) in the class constructor you want to use.
Like:
public class SomeClass {
private readonly JsInteropForClasses jsInteropForClasses;
public SomeClass(JsInteropForClasses jsInteropForClasses) {
this.jsInteropForClasses = jsInteropForClasses;
}
}
or in Razor as shown by Henk here
#inject JsInteropForClasses ij
Related
Here is what I want to achieve:
I have a (normal) class in a Blazor WASM project. I want to invoke a JavaScript function from my class.
If I want to do this from a Razor component it is working fine, I inject IJSruntime and invokevoidasync to my JavaScript.
But it goes wrong when I try to do it from a class.
First I tried to inject it like this:
[Inject]
IJSRuntime JSRuntime { get; set; }
But ended up with error message: Value cannot be null. (Parameter 'jsRuntime')
I learned from this post that I have to "Inject it in the traditonal way", so I did this:
public class InvokeJavaScript
{
private readonly IJSRuntime jSRuntime;
public InvokeJavaScript(IJSRuntime jSRuntime)
{
this.jSRuntime = jSRuntime;
}
public async void InvokeMyJs()
{
await jSRuntime.InvokeVoidAsync("giveMeAMessage");
}
}
But from there on I am stuck, I know that this must be some key .NET knowledge but I am missing a piece here.. I want to call the "InvokeMyJs" methode like:
InvokeJavaScript ij = new InvokeJavaScript();
ij.InvokeMyJs();
But know I am facing an error: There is no argument given that corresponds to the required formal parameter 'jSRuntime' of 'InvokeJavaScript.InvokeJavaScript(IJSRuntime)'
That I get the error makes sense to me but I dont know how to fix it, what parameter must I send to InvokeJavaScript(IJSRuntime jSRuntime) and how do I do it correctly? Can anyone give an example?
When you use DI you have to follow it through.
In general that means avoiding new, as in:
InvokeJavaScript ij = new InvokeJavaScript(); // no parameter
register the InvokeJavaScript as a Service in Startup.
inject it where you need it.
Program.cs
builder.Services.AddTransient<InvokeJavaScript>();
SomeComponent.razor
#inject InvokeJavaScript ij
I want to create a Javascript class that I can instantiate from a Vue component and call functions on it like in C#.
public class MyClass()
{
public MyClass(string someParameter){
....initialization code using "someParameter"
}
public String SomeProperty {get; set;}
public void MyClassMethod(){
....my code goes here
}
}
In Vue, I'd import JavaScript "functions" using the "Import" keyword but here I'd like to import the entire object and call the functions on it. I'm thinking that an IFFE might be the way but not sure. My question is 1) is it possible and 2) if so, how should I structure it AND how would I call/or instantiate the object from Vue code?
If you are using Vue.js, I think the better way will be to stick with vue component based structure and use mixins.
(https://v2.vuejs.org/v2/guide/mixins.html)
What is the difference between concrete factories and abstract factories?
PS: I originally asked a question "what is a concrete factory". Asked my doubt here in a separated question to keep the two discussions separate.
The thing is abstract factories are just abstract class the concrete factories give the real implementation of the abstract classes
The difference will be easier to see from the perspective of a client class which makes use of the factory class.
If the client class uses a concrete factory class, then it will be difficult to reuse the client class in another context. We cannot replace the current in-used concrete factory class by another concrete factory class without modifying source code of the client class.
In contrast, if the client uses an abstract factory class (or interface), we can easily reuse the client class with another (concrete) factory class because the source code of the client class does not mention any concrete factory class. For example, consider the code below:
interface AbstractFactory { Product create(); }
class Client {
private AbstractFactory factory;
public Client(AbstractFactory factory) { this.factory = factory; }
public void foo() { /* use this.factory */ }
}
// Now we can reuse the Client class with any concrete factory class
class ConcreteFactory1 implements AbstractFactory { ... }
class ConcreteFactory2 implements AbstractFactory { ... }
Client client1 = new Client(new ConcreteFactory1());
client1.foo();
Client client2 = new Client(new ConcreteFactory2());
client2.foo();
As you see, in any case, source code of the Client class does not need to be modified, but it still works with different concrete factory class.
I am setting up a new project with latest Angular.I am using Angular Material for this.I am using BreakpointObserver from #angular/cdk/layout.
I am able to add that succesfully to one of my component.But I want to add it globally to my project so that all the components/modules can use web/tablet/mobile breakpoints for different DOM manipulation.
I am able to add that to the app.component.ts , but I am expecting to write a directive or something.Not service because BreakpointObserver is already a service.
What would be the best approach to add BreakPointObserver observables globally in the project.Do not want to add isHandset$ observables everytime in each component's ts file
I think your idea of using some kind of custom directive would be a good solution. For example, in the case you want to add/remove components of the DOM, you could define only one structural directive that handle adding/removing its host, depending on the viewport dimension using the BreakpointObserver service.
To accomplish that you could define something like this:
#Directive({
selector: '[contentForSmallScreen]'
})
export class ContentForSmallScreenDirective implements OnDestroy {
constructor(
private templateReference: TemplateRef<any>,
private viewContainerRef: ViewContainerRef,
private breakpointObserver: BreakpointObserver
) {
this.breakpointObserver
.observe([tablet,mobile])
.pipe(pluck('matches'), distinctUntilChanged())
.subscribe(this.showHideHost);
}
private showHideHost = (matches: boolean) => {
matches
? this.viewContainerRef.createEmbeddedView(this.templateReference)
: this.viewContainerRef.clear();
}
ngOnDestroy(): void {
this.breakpointObserver.ngOnDestroy();
}
}
And then after declare it at your NgModule, it can be used with any component/html element you wanna add/remove from the DOM depending on if the viewport's dimension meet the tablet and mobile viewport specs.
<app-any-component *contentForSmallScreen></app-any-component>
👨💻 StackBlitz example
I'm getting used to view components in MVC 6, and I asked a similar question a few years ago about partial views. If I build a view component encapsulating a common use-case that requires its own Javascript, where do I put that Javascript? I know that it is dangerous at best to have Javascript in partial views, but it would be a lot simpler to include it in the view component, rather than in the containing view or a separate file that has to be referenced by the containing view.
For example, say I have a view component that has two drop-downs. The selection in the first drop-down determines what items appear in the second drop-down. This is easily handled in Javascript, of course, but where do I put it?
From my experience with ASP.NET 5 View Components, I would say that the best thing to do with them is to keep them isolated and in one place, so they will be easily to manage in long-term projects.
In one of my ASP.NET projects, I've developed View Components structure like this one:
View, Backend code and Model are all in one place, so when you move around the folder, you are sure that you move whole component. Moreover, when you are modyfying them, you have quick access to all of their parts.
It will be convinient to put JavaScript which is highly coupled with a component also in such structure. You can do this by simply creating the file under the component's folder, and then writing a GULP TASK that will copy JS file to wwwroot. From that point, you will be able to link that JavaScript code on component's .cshtml using standard syntax:
<script src="~/Components/yourcomponent.js"></script>
To obtain such a structure in my project, I've extended Razor, to be able to search for my component's CSHTML's in proper place. To do this, I've added this code in Startup.cs:
public partial class Startup
{
public void ConfigureServices(IServiceCollection services)
{
//non relevant code skipped
services.AddMvc().AddRazorOptions(ConfigureRazor);
}
public void ConfigureRazor(RazorViewEngineOptions razor)
{
razor.ViewLocationExpanders.Add(new ViewLocationExpander());
}
}
and the ViewLocationExpander class is:
public class ViewLocationExpander : IViewLocationExpander
{
protected static IEnumerable<string> ExtendedLocations = new[]
{
"/{0}.cshtml"
};
public void PopulateValues(ViewLocationExpanderContext context)
{
//nothing here
}
public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
{
//extend current view locations
return viewLocations.Concat(ExtendedLocations);
}
}
Then, you invoke component like this (from any .cshtml view):
#await Component.InvokeAsync("NavigationComponent",new NavigationComponentModel())