I have an IdentificationView that has to call a method of another view in different app:
var NameAndIdentificationView = Application.Views.ItemView.extend({
loadMailingAddressView: function() {
//call to mailing address view method setAddress() from here
}
});
Now my MailingAddressView is something like this:
var MailingAddressView= Application.Views.ItemView.extend({
setAddress: function(address) {
if (address != null) {
// this.autoCompleteBox.data('inputData') = address;
ProWeb.EXTRACTED_ADDRESS = address;
this.model.set('mailingAddress', address);
}
}
});
I would like to know what is the way to call a method of one view from another. Please provide suggestions.
EDIT:
This is how my application has been setup:
First App is called.
Then the Controller is called.
From within the Controller, View is called and intialized.
So from within my NameIdentificationView, I'm calling the MailingAddress app that in turn controls all the process of calling the controller and it's view.
You can do it many ways using event channel,triggering and listening events on shared model, the simpler way of doing this is using Bsckbone events.
Trigger a event in caller
loadMailingAddressView: function () {
//call to mailing address view method setAddress() from here
Backbonw.trigger('setAddress')
})
and listen in callee's initalize
var MailingAddressView = Application.Views.ItemView.extend({
initialize: function() {
Backbone.on('setAddress', this.setAddress)
},
setAddress: function(address) {
if (address != null) {
// this.autoCompleteBox.data('inputData') = address;
ProWeb.EXTRACTED_ADDRESS = address;
this.model.set('mailingAddress', address);
}
},
});
make sure callee View is initialized by the time you trigger event
Related
My web page has
var bc = new BroadcastChannel('Consumer');
bc.onmessage = function(event) {
alert("a");
}
bc.postMessage("hello");
It broadcasts a message, and the page is also required to receive the same message.
However it doesn't work. Did I miss anything?
You can create two instances of BroadcastChannel on your page. One can act as a broadcaster for messages, the other one for receiving messages.
var broadcaster = new BroadcastChannel('Consumer');
var messageReceiver= new BroadcastChannel('Consumer');
messageReceiver.onmessage = function(event) {
alert(event.data);
}
broadcaster.postMessage("hello");
See this in action: https://jsfiddle.net/h56d3y27/
Or wrapped in a reusable class:
(note: class is not supported by all browsers. See : https://caniuse.com/#search=class for browser compatibility)
class AllInclusiveBroadcaster {
constructor(listener, channelName) {
if (!channelName) channelName = "channel";
this.broadcaster = new BroadcastChannel(channelName);
this.messageReceiver = new BroadcastChannel(channelName);
this.messageReceiver.onmessage = (event) => {
listener(event.data);
}
}
postmessage(data) {
this.broadcaster.postMessage(data);
}
}
var broadcaster = new AllInclusiveBroadcaster((data) => alert(data));
broadcaster.postmessage("Hello BroadcastChannel");
See this also in action a JSFiddle
You could dispatch an event (call it what you like) to, say, document, with the same data ... then have a single handler that listens for BroadcastChannel messages and to the event name you created above
in the following, the code creates and listens for fakeBroadcastMessage
created a function to send both the bc message and the "local" message
var bc = new BroadcastChannel('Consumer');
function handleBroadcastMessage(event) {
// do things here
}
bc.addEventHandler('message', handleBroadcastMessage);
document.addEventListener('fakeBroadcastMessage', handleBroadcastMessage);
function sendMessage(data) {
bc.postMessage(data);
var ev = new Event('fakeBroadcastMessage');
ev.data = data;
document.dispatchEvent(ev);
}
sendMessage('hello');
I'm communicating an iframe with an angular controller using window.postMessage function https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage#The_dispatched_event
The JS that is inside my iframe is as simple as this.
//Call this function after clicking button
function runOperationFromExternal(operationId){
console.log('Button clicked');
window.parent.postMessage({message: {operation: operationId}}, '*');
}
After that I have a service in angular
var angular = require('angular');
var ListenerActionsServiceModule = angular.module('ui.apps.myApp.service',[
])
.service('listenerActionService', function($window, $rootScope){
this.$rootScope = $rootScope;
this.subscribeBeltEvent = function(){
$window.addEventListener('message', function(event){
if(event.origin === 'http://localhost:33333') {
propagateEvent(event);
}
else{
console.log('Origin not allowed');
}
}, false);
};
function propagateEvent(event) {
var eventName = 'eventName.';
var args;
if(event !== null && typeof event.data.message === 'object') {
eventName = eventName.concat('complexOperation');
args = event.data.message.operation;
}
else {
eventName = eventName.concat(event.data.message);
}
$rootScope.$broadcast(eventName, args);
}
});
module.exports = ListenerActionsServiceModule;
Everything is working fine except that, when I click the button inside the iframe the second or third time, the angular service is receiving twice or three times the event. It's like they are stacking in the window and after reading them they are not dissapearing. Is there any way to clean this after reading them? Or should I add to my event a field to know if I have read it or not?
Thank you
EDIT: This is happening only when we go back using the browser arrow
I'm not sure but your postMessage will never work unless you call your service.
In my case, I usually put an addEventListener in the main module and more specifically in the run method.
I have a simple app, which displays a list of available signalR hubs. A user selects a hub and it connects to it, this subscribes an event to add messages to a table on the page. The user can then send messaged to that hub which will also fire the subscription adding that message to the table. This all works great.
Now if the user selects another hub, the app connects and sets up a new subscription, however the original subscription still fires causing duplicate messages to be added to the table. Each time the hub is changed further subscriptions get added causing one send to result in many messages in the table.
I have tried disconnecting the hub, disposing the hub and trying to remove the subscription with hubProxy.off(eventName), but nothing seems to work, other than a page reload.
Here is the code I have just added the onHub changed function as this is where everything is happening.
Any ideas appreciated. :)
function HubViewModel() {
var self = this;
self.hubConnection = '';
self.hub = '';
$.getScript("../signalR/hubs");
self.hubs = ko.observableArray();
self.selectedHub = ko.observable();
self.messageText = ko.observable();
self.messageCollection = ko.observableArray();
self.hubChanged = function () {
// Setup hub connection.
$.connection.hub.url = "../signalR";
self.hubConnection = $.hubConnection();
// Get the selected hub name.
var selectedHubName;
_.each(self.hubs(), function(item) {
if (item.hubId == self.selectedHub()) {
selectedHubName = item.hubName;
}
});
// Check for a selected connection
if (self.selectedHub()) {
// Create proxy.
self.hub = self.hubConnection.createHubProxy(selectedHubName);
// Remove any existing listener(s).
self.hub.off('addNewMessageToPage');
// Setup listener.
self.hub.On('addNewMessageToPage', function (sender, message) {
self.messageCollection().push({ hubName: selectedHubName, name: selectedHubName, message: message, dateTime: new Date().toLocaleString() });
$('#hubMessageGrid').dxDataGrid('instance').refresh();
});
// start connection.
self.hubConnection.start()
.done(function() {
toastr.success('hub connected');
$('#sendMessageButton').click(function() {
self.hub.invoke('sendAll', 'hub management page', self.messageText());
self.messageText('');
});
})
.fail(function(error) {
toastr.error('hub connection ' + error);
});
}
};
You can to disconnect the hub first by calling the self.hub.stop(); function
You need to pass the exact same handler instance when unsubscribing. Passing a different instance (even if the function body is the same) will not remove the handler.
See https://learn.microsoft.com/en-us/javascript/api/#microsoft/signalr/hubconnection?view=signalr-js-latest#off-string---args--any-------void-
im using a service that basically popups a box when a user tries to refresh or close the page, and it works fine, but basically i just want to be apply in a specific page, but the problem is that being applied in the rest of the pages.
I tried applying inside of a if statement that includes a specifc state location, but still is being applied in the rest of the app.
Service:
angular.module('farm')
.factory('beforeUnload', function ($rootScope, $window) {
// Events are broadcast outside the Scope Lifecycle
$window.onbeforeunload = function (e) {
var confirmation = {};
var event = $rootScope.$broadcast('onBeforeUnload', confirmation);
if (event.defaultPrevented) {
return confirmation.message;
}
};
$window.onunload = function () {
$rootScope.$broadcast('onUnload');
};
return {};
})
.run(function (beforeUnload) {
// Must invoke the service at least once
});
Controller:
if ($state.includes("app.cal.lab.result")){
$scope.$on('onBeforeUnload', function (e, confirmation) {
confirmation.message = "All data willl be lost.";
e.preventDefault();
});
$scope.$on('onUnload', function (e) {
console.log('leaving page'); // Use 'Preserve Log' option in Console
});
}
I have a question regarding ASP.NET server controls with client functionalities. I want to improve the client-side event handling: when a child client-control fires an event, I want the parent client-control to be directly notified.
Let's assume we have two server controls, MyWindow and MyTextBox. Both are client-side objects of type "Sys.UI.Control".
The MyTextBox is a child of MyWindow:
public class MyWindow : Control, IScriptControl
{
private MyTextBox _mtb;
//....
protected override void CreateChildControls()
{
_mtb = new MyTextBox();
_mtb.ID = "MTB";
_mtb.OnClientTextChanged = "OnClientTextChanged";
Controls.Add(_mtb);
}
//...
public IEnumerable<ScriptDescriptor> GetScriptDescriptors()
{
ScriptControlDescriptor descriptor = new ScriptControlDescriptor("Test.MyWindow", ClientID);
descriptor.AddComponent("myTextBox", _mtb.ClientID);
yield return descriptor;
}
client-side:
function OnClientTextChanged(sender, args) {
alert('test')
}
Now, whenever the text of the textbox changes, the client-event is fired and calls the function "OnClientTextChanged".
What I want is to notify the client object of "MyWindow". I could do that this way:
function OnClientTextChanged(sender, args) {
$find("MyWindow").textBoxChangedItsText();
}
But how can I notify the client object of MyWindow directly without using this global javascript function? What I tested was
_mtb.OnClientTextChanged = string.Format("($find('{0}')).textBoxChangedItsText", ClientID);
but I the "textBoxChangedItsText" function inside the client object can not access the object itself - "this" is the function itself but not the object which I would find using "$find("MyWindow")"
I hope the question is clear to persons with knowledge in client-side enabled AJAX server side controls.
Thanks!
Edit: Using this event hander on the client-side works:
server-side:
_mtb.OnClientTextChanged = string.Format(" ($find('{0}')).textBoxChangedItsTextDelegate", ClientID);
client-side:
Test.MyWindow = function (element) {
Test.MyWindow.initializeBase(this, [element]);
this.textBoxChangedItsTextDelegate= Function.createDelegate(this, function (sender, e) {
this.textBoxChangedItsText();
});
};
Test.MyWindow.prototype = {
initialize: function () {
Test.MyWindow.callBaseMethod(this, 'initialize');
},
textBoxChangedItsText: function () {
alert(this.get_id());
},
dispose: function () {
Test.MyWindow.callBaseMethod(this, 'dispose');
}
};
What I still don't like is the attaching to the event server-side with the $find in the event handler: _mtb.OnClientTextChanged = string.Format(" ($find('{0}')).textBoxChangedItsTextDelegate", ClientID);
I suppose that you need to define events as it basically done in ASP.NET Client Side controls. So it is necessary to add the following to your MyTextBox client side object prototype:
add_textChanged: function(handler) {
this.get_events().addHandler('shown', handler);
}
remove_TextChanged: function (handler) {
this.get_events().removeHandler('textChanged', handler);
}
raise_textChanged: function (eventArgs) {
var handler = this.get_events().getHandler('textChanged');
if (handler) {
handler(this, eventArgs);
}
}
This will define client side event to which client code (parent controls or page) can subscribe and perform required actions. To raise this event in your MyTextBox you can use the code like this:
this.raise_textChanged(Sys.EventArgs.Empty);
And to use this event in MyWindow client side object you need to modify your code to the following:
Test.MyWindow = function (element) {
Test.MyWindow.initializeBase(this, [element]);
this._myTextBoxID = null;
this._onTextChanged$delegate = Function.createDelegate(this, this._onTextChanged);
};
Test.MyWindow.prototype = {
initialize: function () {
Test.MyWindow.callBaseMethod(this, 'initialize');
$find(this._myTextBoxID).add_textChanged(this._onTextChanged$delegate);
},
_onTextChanged: function(sender, eventArgs) {
// Perform required actions.
// this - will be instance of the this client side object.
}
dispose: function () {
$find(this._myTextBoxID).remove_textChanged(this._onTextChanged$delegate);
Test.MyWindow.callBaseMethod(this, 'dispose');
}
};
Note: in the example provided I used this._myTextBox which equals to client id of the child MyTextBox control. You can use property "myTextBox" without performing $find because you initialize it using descriptor.AddComponent("myTextBox", _mtb.ClientID);