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
Related
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
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)
I have two panels in Wicket.
ParentPanel and
ChildPanel
Scenario
ChildPanel is a component of ParentPanel.
Every time a certain event is fired in the Parent panel, I want to prepend a certain javascript in the ChildPanel.
Working Solution
ParentPanel.java
public class ParentPanel extends Panel
{
private static final long serialVersionUID = 4306746527837380863L;
private final ChildPanel childPanel;
public ParentPanel(String aId, IModel<AnnotatorState> aModel)
{
super(aId);
model = aModel;
// initially the Child panel is empty. passing empty model
childPanel = new ChildPanel("child-container", Model.of());
childPanel.setOutputMarkupId(true);
add(childPanel);
}
#OnEvent
public void onClickText(RenderEvent aEvent)
{
LOG.trace("Event Fired");
IPartialPageRequestHandler iPartialPageRequestHandler = aEvent.getRequestHandler();
Integer someData = getSomeData();
childPanel.setDefaultModel(Model.of(someData));
//I am trying to add the child panel to the ajax target to ensure rerendering of child panel.
iPartialPageRequestHandler.add(childPanel);
}
}
ChildPanel.java
public class ChildPanel extends Panel
{
private static final long serialVersionUID = -3849226240909011148L;
private static final Logger LOG = LoggerFactory.getLogger(ChildPanel.class);
private IModel<Integer> model;
public ChildPanel(String aId, IModel<Integer> aModel)
{
super(aId);
model = (aModel);
}
#Override
public void renderHead(IHeaderResponse aResponse)
{
super.renderHead(aResponse);
model = (IModel<Integer>) getDefaultModel();
if (model == null)
return;
Integer someData = model.getObject();
String someJavascript = createSomeJavascript(someData);
log.debug("Rendering Updated UI with the JS: ", javascript);
aResponse.render(OnDomReadyHeaderItem.forScript(javascript));
}
}
Apparently renderHead() is called every time the childPanel is added to the iPartialPageRequestHandler and it does my work that is to update the UI on every event fired in the ParentPanel.
Problem
However I have been told that this is not a good practice. Since in this case, the JS is added to renderHead(), then it ends up in the page source code - that gets cumbersome when sending (large) JSON data. A good place to attach the JS to the AJAX target needs to be found (maybe render() or probable better onRender() or onAfterRender()) only if its possible/doable in Wicket. Due to my limited knowledge, I am not able to guess how else I can implement the scenario. Any other (better or possible) solution is welcomes and appreciable.
Thanks.
I assume the problem is that the createSomeJavascript creates a big chuck of code?
I would then refactor it so that createSomeJavascript just calls a function that you add to the Component using a JavaScriptResourceReference. This way, only a small bit of Javascript ends up in the source.
For example, put your javascript in an external file like this:
private static final ResourceReference JS_QM = new JavaScriptResourceReference( ClientPanel.class, "question.mark.js" );
#Override
public void renderHead( Component component, IHeaderResponse response )
{
super.renderHead( component, response );
response.render( JavaScriptReferenceHeaderItem.forReference( JS_QM, "question-mark" ) );
...
}
c# code:
public class Person{
public string Name { get; set; }
public age int { get; set; }
}
cshtml code which generates an id of #Person_Name
#Html.DropDownListFor(m => m.Person.Name)
javascript code:
$('#Person_Name').on("change", function () {
//Do something
}
If I change the Person class property name from Name to FullName. The next step is to modify the cshtml code to read as:
#Html.DropDownListFor(m => m.Person.FullName)
I understand you can manually go in and change the Jquery code, but if this change is made and the person making the change is unaware of the jquery, it is going to cause an error. Is there a way to prevent this from happening through some form of notification or logging? Rather than just remembering that the jquery needs to be changed.
You do have the option to use:
#Html.IdFor(x => x.Person.FullName)
Or
#Html.NameFor(x => x.Person.FullName)
That does rely on having small bits of script in pages, but for what id do this is mostly calling functions in .js resources which from my point of view I find better for reuse anyway.
You may still not get alerted to all issues unless you choose to compile your MVC views, you'll probably get a warning if you have the view open. How you set up your project to compile your MVC views will depend on the type of project but a quick Google should help with that one.
I am building a SPA (Single Page Application) using Breezejs and Knockoutjs.
I am running into an issue when trying to set a navigation property inside a knockout subscription. On the final line of the ko.subscription the console.log function shows me the entity, however, the WebPresences navigation property is null.
Not sure if the fact its in a ko.subscription really matters but I've been able to set the navigation prop just in a js function I call right before save, so I think it has some significance.
So here is my Entity Model
public partial class Entity
{
public int Id { get; set; }
public string Name { get; set; }
public Nullable<int> WebId { get; set; }
public virtual WebPresence WebPresence { get; set; }
}
And here is my ko.subscription and relevant variables:
var vm = {
newEntity: ko.observable(datacontext.createBreezeEntity('Entity')),
newWebPresence: ko.observable(datacontext.newBreezeEntity('WebPresence')),
}
vm.newEntity().WebPresence.subscribe(
function (data) {
var self = this;
if (data === null)
self.target(vm.newWebPresence());
console.log(vm.newEntity());
}
);
And last but not least my datacontext
createBreezeEntity: function (entityName) {
return manager.createEntity(entityName);
},
newBreezeEntity: function (entityName) {
return manager.metadataStore.getEntityType(entityName).createEntity();
}
I do not understand what you're striving to accomplish.
One thing I'm pretty confident about ... is that your the datacontext.newBreezeEntity creates an object that is just hanging in thin air and isn't part of any navigation property.
Let's look at your datacontext.newBreezeEntity method:
return manager.metadataStore.getEntityType(entityName).createEntity();
This indeed does create a new entity of the type named entityName. But this is a 'proto-entity'. It does not belong to an EntityManager ... certainly not to the manager instance. It isn't related to any particular other entity (e.g., your mysteriously-named Entity entity). It's just a detached entity.
I'm betting you thought that it would belong to manager because you started the expression there. Well it doesn't. You lost the connection with manager the moment you asked for its metadataStore.
But there is so much else that makes no sense to me at all. I can't tell why you're subscribing to vm.newBreezeEntity nor why it's called "newBreezeEntity" nor what it's relationship is supposed to be to vm.newEntity nor what you think this is within the subscription function.
Perhaps you should step back and describe what you WANT this code to do.