I am working on a site that uses the great jsPlumb library to create a node interface.
jsPlumb has an event 'beforeDrop' that is triggered before a connection between two endpoints are connected, that I want to use to check a condition, and then decide to allow the connection or not.
It the connection is not allowed, I want to use ngToast to show a message to the user.
This is my 'beforeDrop' function
jsPlumb.bind('beforeDrop', function(info){
// Check that they property types match
var outNodeType = $('#'+info.sourceId).data( "ptype" );
var inNodeType = $('#'+info.targetId).data( "ptype" );
if(outNodeType !== inNodeType){
showMessage('warning', '<strong>Error:</strong> unable to connect '+outNodeType+' to '+inNodeType)
return false // false for not establishing new connection
}
return true; // true for establishing new connection
});
And this is the function that shows the ngToast message:
function showMessage(messageType, message){
ngToast.warning({
class: messageType,
content: message
});
}
The problem is that the ngToast message does not appear until I click anywhere on the page. Once I click, the message appears and everything works.
I don't know if this is an issue with jsPlumb and angularjs, or a problem with how I am calling the ngToast function.
I would really appreciate any suggestions as to how to resolve this. TIA!
jsPlumb event will be conaidered as event outside of angular context. Seems like you are calling angular code from outside of angular code. For make sync angular, you need to call $scope.apply() after calling the toaster message method. So that toast will get shown as soon as you clicked on it.
Related
I am using Laravel 5.6.7, Socket.IO and vue.js. I am not using Pusher and redis. Below is my code to send message to user chatting with me one to one.
var url = "http://localhost:6001/apps/My_appId/events?auth_key=My_Key";
var socketId = Echo.socketId();
var request = {
"channel": "private-Send-Message-Channel.2",
"name": "MessengerEvent",
"data": {
"msg": message
},
"socket_id": socketId
};
axios.post(url, JSON.stringify(request)).then((response) => {
//Message Sent
});
I am trying to inform user who is chatting with me that I am still typing. Should I use the same above code which emits xhr on each char type? Is it the only way to inform user that the message typing is still in progress?
Update 1
Is there any better way to post xhr as mentioned above for each key press? I meant if user types 200 chars. will I post xhr 200 times?
or
Do we have an event called whisper and listenForWhisper as shown here https://laravel.com/docs/5.6/broadcasting#client-events ? I am using vue.js and laravel 5.6.7 without pusher and without redis
If you look at the broadcasting documentation you will see two code code snippets which you can use in your Vue.js application.
To broadcast client events, you may use Echo's whisper method:
Echo.private('chat')
.whisper('typing', {
name: this.user.name
});
To listen for client events, you may use the listenForWhisper method:
Echo.private('chat')
.listenForWhisper('typing', (e) => {
console.log(e.name);
});
While the user is typing, you can debounce the whisper method above.
If you don't wish to use another library like lodash, you can implement the debounce by simply wrapping whisper in a timeout. The following method would broadcast the whisper every 300ms:
isTyping() {
let channel = Echo.private('chat');
setTimeout(function() {
channel.whisper('typing', {
name: this.user.name,
typing: true
});
}, 300);
}
The app needs to trigger isTyping() when an onkeydown event occurs in the chat application's input field.
You also need to listen for the whisper once the app is created. The following method will set the typing variable to true for 600ms after the event has been received.
created() {
let _this = this;
Echo.private('chat')
.listenForWhisper('typing', (e) => {
this.user = e.name;
this.typing = e.typing;
// remove is typing indicator after 0.6s
setTimeout(function() {
_this.typing = false
}, 600);
});
},
I am no Laravel expert, but I've faced this problem before.
First, let's define what "typing" means. The simplest way to define it is to say that a user is typing if and only if the input field to send a message is not empty.
This is not perfect, because the user can go away from keyboard in the middle of typing a message then not returning to complete and/or send it, but it is good enough.
More importantly, we now don't need to care about key strokes to know if the user is typing. In fact, "user is typing" now becomes as easy as chat_input_box.length > 0 to represent in code.
This boolean value is what needs to be synced across users/servers, not the act of hitting a key on the keyboard by the user. However, to keep the value up to date, we need to catch input events on chat_input_box and if the boolean value has changed since before this current event has occurred, socket.io should be able send a signal signifying whether the user has stopped or started typing.
On the receiving side, this signal toggles appropriate views to appear or disappear to indicate the state of the app to the user in human terms.
For the shortcoming of a user typing something then leaving, a timeout can be set so that when it is finished the boolean value "is typing" resets to false, while the act of typing something resets the timeout then starts it again automatically.
You don't have to send an xhr request to your app. You can just
broadcast events directly to the chat users without hitting your app.
From the Laravel docs:
Sometimes you may wish to broadcast an event to other connected clients without hitting your Laravel application at all. This can be particularly useful for things like "typing" notifications, where you want to alert users of your application that another user is typing a message on a given screen. To broadcast client events, you may use Echo's whisper method:
Echo.private('chat')
.whisper('typing', {
name: this.user.name
});
To listen for client events, you may use the listenForWhisper method:
Echo.private('chat')
.listenForWhisper('typing', (e) => {
console.log(e.name);
});
Yes you are right, it should not be emitting on every character change instead you could use debouncing to wait for a small time and then fire the function.
I would recommend using lodash library's debounce method. It should be something like this.
Kindly have a look at the documentation: https://lodash.com/docs/4.17.5#debounce
Laravel's Echo also sounds good, as you'll be doing nothing with typing action on the back-end thus just emitting from client to client is better than involving the server.
I am facing a rather strange issue with AngularJs.
I have an AngularJS (AJS henceforth) application which is listening to certain SignalR hubs.
When a SignalR hub receives a message (and the data object is passed in that call too), it fires an event in AJS which in turn shows a Toastr on the UI.
Now I have around 5 different types of SignalR hubs that initiate different Toasts on the UI.
I added another Toast (as per the new requirement) which is initiated by yet another SignalR hub.
Problem:
This new toast that I have implemented does show up when the SignalR hub receives a message, but the data is not rendered. What I mean is that the toast shows up with {{ }} these brackets.
Other toasts are following the same routine and are showing up just fine, except for the new one.
The strangest part is that I have another AJS method running at an interval of around 30 seconds that fetches some count values from the database (using WEB API in the background). And when this action completes the {{ }} disappear and the values show up just fine (I keep the toastr alive for that while by keeping my mouse cursor over it, or else it will disappear).
//SignalR hub initiates the following method
$scope.$on("on_remote_event", function (argEvent, argData) {
var toastrHtml = "<div class='my-toast-style'><div class='myLabel'>Client : {{ argData.ClientName }}</div><div class='myLabel'>Order Date: {{ argData.OrderedOn | date:'MM/dd/yyyy hh:mm a' }} EST</div></div>";
generateAlertToast(toastrHtml, "Order Info", argData);
});
//this method will generate the toast as per the information passed to it
function generateAlertToast(toastrHtml, toastrTitle, argData) {
toastr.options = {
"closeButton": true,
"newestOnTop": true,
"progressBar": true,
"preventDuplicates": true,
"timeOut": 60000,
"positionClass": "toast-bottom-right"
}
var scope = $rootScope.$new();
scope.argData = argData;
//console.info(scope.argData);
var compiledHtml = $compile(toastrHtml)(scope);
toastr.info(compiledHtml, toastrTitle);
};
Now I know that somehow AJS is not evaluating the html for the toastr. But how and where is the problem is what i am banging my head against!!
Please help.
So sorry guys!!
It was my stupid mistake that I made in the SignalR side code due to which the above mentioned code was not working.
Basically when I broadcast the message received from SignalR I forgot to enclose the broadcasting line in the rootscope apply.
$rootScope.$apply(function () {
//broadcast event here
});
And since I did not write the rootscope apply, AJS was not able to run the digest cycle and hence was waiting for me or some other method to initiate the digest cycle and evaluate the values on the UI.
Lesson Learnt.
Thank you :)
I have a question regarding when a new document is added to mongodb
I have an order object that can be added server side after a meteor method call.
I have an admin page called ‘incomingOrders' subscribing to all orders.
What i would like to do , is just play a sound when on this page , when a new order is inserted into the database.
my client side collection updates with the new order, but i need it to show some sort of alert (i.e. alert box, sound , flashing screen!!!)
How would i exactly do this?
is there an event that can that is triggerd when a new document is inserted that i can subscribe to?
I have no code tested as i don’t have any idea how to do it.
So i found the answer to my question
I used the cursor.observe function to observer when a document is added to my collection.
Template['incomingOrders'].helpers({
orders:function(){
var cursor = Orders.find({},{sort: {createdAt: -1}});
// watch the cursor for changes
var handle = cursor.observe({
added:function(order){
if(!initializing){
console.log('order from handle');
console.log(order);
document.getElementById('xyz').play();
}
}
});
return cursor;
}
});
I have an initialising check (which is set to false in the rendered function of the template) as this function seems to be called on every element while the template is rendering . And then i simply call play on an audio element to alert me of a new document being added.
If there is a better way , please inform me!
I m actually facing a problem while developping my chat application between angularjs (BTFord library) and NodeJS Socket.IO...
In fact, I need a user A to send a message to a user B.
- If I log the message in node, it works perfectly, and it's sent only one time.
- In angular js the event on("message") is called 3 or 4 times, and I had the message to send 3 or 4 times
It's a problem for me ... I need to receive the message only one time.
Is there any problem with this ?
Thanks for advance
I had a similar situation, it ends up that the controller was executing more than once, so be aware of that, check if it's running more than once using a message with console.log, beign said that probably you are registering several listeners on the client side to avoid that put a condition in your client: (let's asume that you are registering the events on $rootscope)
if( $rootScope.$$listeners['socket:someListener']===null || $rootScope.$$listeners['socket:someListener']===undefined){
$rootScope.$on('socket:someListener', function(ev, data){
//Client code
}
}
that will register your listener only once!
cheers!
Events handlers are not destroyed when switching/reloading controllers. So you may have subscribed multiples times to your event.
See the docs : https://github.com/btford/angular-socket-io#socketremovelistener
Can you try this :
angular.module('Foo').controller('BarCtrl', function ($scope, socket) {
socket.on('message', function (msg) {
// ... do something
});
$scope.$on('$destroy', function() {
socket.removeListener('message');
});
});
What is the best way to pass message in the below scenario.
In the success scenario of $scope.p.$save, the result contains a message (res.message), which I like to display in the next view ($location.path("/test/"+res.reply.Id)). Without AngularJS, I may pass it in the url or save it in session cookies. But, I guess there might be a better way in AngularJS as there is no browser redirect and the state should be available. What is the best way to achieve this?
Setting it in rootScope shows it while I use browser back button, and the scope of the message should only for the first navigation to the new view.
function NewCtrl(Phone, $location, $rootScope, $scope) {
$scope.p = new Phone();
$scope.save = function () {
$scope.p.$save(
{},
function (res) {
$rootScope.message = res.message **//<-- this will cause message still set when using browser back button, etc**
$location.path("/test/"+res.reply.Id); **//<-- Req: needs to pass the message to next view**
}, function (res) {
//TODO
}
);
};
}
....
PhoneApp.factory('Phone', function ($resource) {
return $resource('/api/test/:_id')
});
You could use a service which displays the flash on $routeChangeSuccess.
Each time you set a flash message, add it to a queue, and when the route changes take the first item off the queue and set it to the current message.
Here's a demo:
http://plnkr.co/edit/3n8m1X?p=preview
I was looking to implement similar functionality, but actually wanted more of a growl style message.
I've updated the excellent plinkr code that Andy provided above to include a 'pop' method that leverages the toastr growl-style notification library.
My update also lets you to specify the notification type (info, warning, success, error) and title.
The 'pop' method skips adding the message to the queue, and instead pops it up on the screen immediately. The set/get functionality from Andy's previous plinkr remains mostly unchanged.
You can find my update here: http://plnkr.co/edit/MY2SXG?p=preview
I don't believe there's a way to do this default to AngularJS. Your best bet would just be passing the message (encoded) through a query string.