I have a single page app based on Aurelia and I'm trying to get it to work with an existing SignalR backend. I've downloaded the SignalR javascript client and integrated it with the Aurelia app manually (i.e. I'm not using a proxy file). I'm able to connect to the SignalR hub and see the arrvive messages in the console.... so far so good. Now, I'm trying to use the Aurelia Event Aggregator so that when a new hub message arrives an event is fired and any components of the app subscribed to that particular event will do some work. The issue is that the SignalR event callback doesn't seem to be able to access the Event Aggregator object. Here's the code to illustrate the issue:
//Import statements omitted for brevity
#inject (EventAggregator)
export class MyService{
constructor(eventAggregator) {
this.ea = eventAggregator;
this.connection = $.hubConnection("http://localhost:8080/signalr", { useDefaultPath: false });
this.hub = this.connection.createHubProxy("myHub");
//Register a callback function to fire when a new hub message arrives
this.hub.on("sendMessage", this.processHubMessage);
//No issues so far - all this constructor code works fine
}
processHubMessage(message) {
// This doesn't work - this.ea is undefined in the scope of this function
this.ea.publish('deviceStatusUpdate', message);
}
}
The event aggregator object referenced within the callback function is not defined - I assume because it's not being called within the scope of the class. Is there a way to resolve this? How do I give the callback function access to the class properties (this.ea in my example).
Try using
this.hub.on("sendMessage", (message) => this.processHubMessage(message));
It's failing on you due to how this isn't what you're expecting it to be. By using a fat arrow function, this is what you expect it to be. This is a really frustrating part of JavaScript, but fat arrows provide a simple workaround for it.
I think you are missing the 'start' for your Proxy, also you may need to alias your view model to pass to the HubProxy.
This works for me:
constructor(eventAggregator){
this.eventAggregator = eventAggregator;
var signalrAddress = 'https://pathToYouServer';
var hubName = 'yourHubsName';
var connection = $.hubConnection(signalrAddress);
var eventHubProxy = connection.createHubProxy(hubName);
var vm = this;
eventHubProxy.on('yourBroadcastMessage', function(data) {
vm.eventAggregator.publish(data);
});
connection.start();
}
Related
Im building a new app and since i want it to be smooth as everyone, I want to use a background thread that would be responsible for all the data downloading using restsharp. Im also following the MVVM pattern.
I've been reading a lot about task.run and how to use it properly and the whole async-await topic. But since Im new to all this, Im not sure how I should procceed to do things right. I have a lot of code so I will breifly try to explain what Im doing and then put a snippet.
So I started with creating a service class that contains all the functions that are using restsharp to get the data. And inside my ViewModel Im calling those functions in the very begining. Im trying to use tasks and run those functions on the background thread but the app get blocked on the splash screen. And abviously thats because Im doing things wrong ... so I decided to ask you guys.
I have this function for exemple :
public string GetResPor()
{
var restClient = new RestClient { BaseUrl = new Uri("http://xxx.xxx.xxx.xxx:xxxx") };
var request = new RestRequest
{
Resource = "getCliPor",
Method = Method.GET
};
request.AddParameter(new Parameter { Name = "idt", Value = GetImAsync().GetAwaiter().GetResult(), Type = ParameterType.GetOrPost });
var result = restClient.Execute(request);
Port = result.Content;
return Port;
}
When I convert this on a Task :
public async Task<string> GetResPor()
{
var restClient = new RestClient { BaseUrl = new Uri("http://xxx.xxx.xxx.xxx:xxxx") };
var request = new RestRequest
{
Resource = "getCliPor",
Method = Method.GET
};
request.AddParameter(new Parameter { Name = "idt", Value = GetImAsync().GetAwaiter().GetResult(), Type = ParameterType.GetOrPost });
var result = await restClient.ExecuteTaskAsync(request);
Port = result.Content;
return Port;
}
on the ViewModel I start by creating a new instance of my service class and then:
Port = RD.GetRestauPort().GetAwaiter().GetResult();
And this is where the app get blocked, no exceptions no nothing.
To keep things simple, let's start with the basics. The easiest thing to do, in order to run something in a background thread, is to call it inside a Task.Run(). What this does is:
Queues the specified work to run on the ThreadPool and returns a task or Task<TResult> handle for that work.
Basically, you are delegating your work to the TreadPool and it handles everything for you - looks for a worker, waits for the worker to finish its job (on a new thread) and then notifies you of the result.
So, basically, whatever you want to be in a background thread, the simples solution will be to wrap it inside a Task.Run() and await its result, in case you need it.
Also, avoid using GetAwaiter().GetResult(). The simple rule in asynchronous programming is - if you can await, await all the way up.
You can read more about the topics in
this SO post
Advanced Tips for Using Task.Run With Async/Await
Using Task.Run in Conjunction with Async/Await
In my project I am trying to get a user based on it's email adress every second with the UserManager but when I do this I get the following error Cannot access a disposed object Object name: 'UserManager1, but this is when I do it inside of a Timer(). If I just do it once there is no problem, how can I fix this? This timer is inside a class that is being called by a SignalR Hub.
Code:
Timer = new System.Threading.Timer(async (e) =>
{
IEnumerable<Conversation> conversations = await _conversationsRepo.GetAllConversationsForUserEmailAsync(userMail);
List<TwilioConversation> twilioConversations = new List<TwilioConversation>();
foreach (Conversation conversation in conversations)
{
TwilioConversation twilioConversation = await _twilioService.GetConversation(conversation.TwilioConversationID);
twilioConversation.Messages = await _twilioService.GetMessagesForConversationAsync(conversation.TwilioConversationID);
twilioConversation.ParticipantNames = new List<string>();
List<TwilioParticipant> participants = await _twilioService.GetParticipantsForConversationAsync(conversation.TwilioConversationID);
foreach (TwilioParticipant participant in participants)
{
User user = await _userManager.FindByEmailAsync(participant.Email);
twilioConversation.ParticipantNames.Add(user.Name);
}
twilioConversations.Add(twilioConversation);
}
}, null, startTimeSpan, periodTimeSpan);
UserManager along with quite a few other types is a service that has a scoped lifetime. This means that they are only valid within the lifetime of a single request.
That also means that holding on to an instance for longer is not a safe thing to do. In this particular example, UserManager depends on the UserStore which has a dependency on a database connection – and those will definitely be closed when the request has been completed.
If you need to run something outside of the context of a request, for example in a background thread, or in your case in some timed execution, then you should create a service scope yourself and retrieve a fresh instance of the dependency you rely on.
To do that, inject a IServiceScopeFactory and then use that to create the scope within your timer code. This also applies to all other scoped dependencies, e.g. your repository which likely requires a database connection as well:
Timer = new System.Threading.Timer(async (e) =>
{
using (var scope = serviceScopeFactory.CreateScope())
{
var conversationsRepo = scope.ServiceProvider.GetService<ConversionsRepository>();
var userManager = scope.ServiceProvider.GetService<UserManager<User>>();
// do stuff
}
}, null, startTimeSpan, periodTimeSpan);
I'm trying to write an example app in wit.ai. I followed the quickstart app using node.js client that is shown at https://wit.ai/docs/quickstart.
The example shown there has only one custom action. But when I try to add a new story and a new action, I see that the context is being shared between the stories. This is causing wrong behaviour(a custom action from another story is being executed).
I cannot find any example with multiple custom actions and stories. Are there any node.js or python examples other than the ones from wit.ai websites?
You need to create a context for each session, and this is a quick example (from https://github.com/wit-ai/node-wit/blob/master/examples/messenger.js):
const findOrCreateSession = (fbid) => {
let sessionId;
// Let's see if we already have a session for the user fbid
Object.keys(sessions).forEach(k => {
if (sessions[k].fbid === fbid) {
// Yep, got it!
sessionId = k;
}
});
if (!sessionId) {
// No session found for user fbid, let's create a new one
sessionId = new Date().toISOString();
sessions[sessionId] = {
fbid: fbid,
context: { // New context per session id.
_fbid_: fbid
}
}; // set context, _fid_
}
return sessionId;
};
You can find a working example at https://github.com/hunkim/Wit-Facebook.
I suppose wit engine don't store context on their side.
You 'merge' function must merge entities in different ways, depending on your app logic.
But if you story is completed, you need to clear context for next stories.
I added a built-in function clear-context and call this function from wit as action.
Check out my example.
It's not an official api, but you can understand how wit http api works.
A parameter governs what data is to be displayed. The parameter is retrieved from activationData in the activate method of the view model and used in a call to a Web Api method. Data is returned, and added to the view model like this
define(['durandal/app', 'knockout', 'moment'],
function (app, config, ko, moment) {
var vm = {
app: app
};
vm.activate = function (activationData) {
vm.ChecklistInstanceId = activationData.ChecklistInstanceId;
$.ajax({
url: "api/ChecklistInstance/" + vm.ChecklistInstanceId,
headers: { Authorization: "Session " + app.SessionToken() }
}).done(function (data) {
$.extend(vm, ko.mapping.fromJS(data));
});
};
return vm;
});
Inspecting the viewmodel immediately after it is extended reveals that it is decorated with observables exactly as expected. For example, vm.Caption() exists and returns the string I expect, and vm.Section() is an appropriately populated observable array, and so on down a fairly elaborate object graph.
The problem is the binding phase has already occurred, and at that time the view model lacks all the observables to which I'm trying to bind.
Two possible strategies suggest themselves:
obtain the parameter earlier
re-bind
I don't know how to do either of those things. Can anyone tell me how to re-organise my code to allow binding to parametrically fetched data?
A third possibility occurred to me:
define(['durandal/app', 'knockout', 'moment'],
function (app, config, ko, moment) {
var vm = {
app: app,
Caption: ko.observable(),
Section: ko.observableArray()
};
vm.activate = function (activationData) {
vm.ChecklistInstanceId = activationData.ChecklistInstanceId;
$.ajax({
url: "api/ChecklistInstance/" + vm.ChecklistInstanceId,
headers: { Authorization: "Session " + app.SessionToken() }
}).done(function (data) {
var foo = ko.mapping.fromJS(data);
vm.Caption(foo.Caption());
vm.Section(foo.Section());
});
};
return vm;
});
This works because all the observables exist in the binding phase. This might seem surprising given that I describe only the root of a potentially deep object graph, but the fact that the observable array is empty causes the binding phase to exit without a hitch.
Later in the activate handler, values are added to the observable array after ko.mapping has its way with the data, and binding succeeds.
I have a sense of dèja vu from this: it is eerily reminiscent of problems solved using forward declarations in TurboPascal back in the eighties. La plus ça change...
In order to work on a fully-constructed view, you will need to move your logic to either the attached handler or the compositionComplete handler. As you said, at the activate stage, the DOM isn't yet fully constructed. You can read about these lifecycle callbacks here.
Typically, what we do is pass the activationData in through the activate handler, store the activationData locally (if your viewModel is instance-based, then on a property in the constructor), and then reference that activationData in the attached or the compositionComplete handler.
You can fetch the data in the activate handler, and then store the data locally. But that's all you should do there. Reserve view-related logic for later in the cycle. In this case, you may need to return a promise from activate, and then resolve upon receiving your data. You can read about it here.
UPDATE
Take a look at this post, and the conversation there.
What is the correct method for setting a client to auto answer with the vLine API for WebRTC calls?
Looking at your comment, it looks like you have figured this out. But for completeness and for future reference I will go ahead and answer.
To auto answer a call, all you have to do is call MediaSession.start() when an incoming call comes in, instead of throwing a prompt to the user.
Here is an example snippet:
client.on('add:mediaSession', onAddMediaSession, self);
// Handle new media sessions
onAddMediaSession(event){
var mediaSession = event.target;
mediaSession.on('enterState:incoming', onIncoming, self);
},
// Handle new incoming calls and autoAccept
onIncoming(event){
var mediaSession = event.target;
// Auto Accept call instead of a prompt
mediaSession.start();
}
Note that you can do this in your code even if you are using the UI Widgets.