Event handler ItemSend is calling again Office.onReady - outlook

I hade defined an event in my manifest
<ExtensionPoint xsi:type="Events">
<Event Type="ItemSend" FunctionExecution="synchronous" FunctionName="itemSendHandler" />
</ExtensionPoint>
And my main.js has something like
window.test1 = 5
Office.onReady((info) => {
if (info.host === Office.HostType.Outlook && !sendEvent) {
// $() is equivalent to $(document).ready() which is deprecated
$(function () {
window.test1 = 20
$('#test_button').on('click', () => { window.test1 = 10 })
// do more stuff
})
}
})
function itemSendHandler (event) {
sendEvent = event
//...
}
I load the Addin and check for window.test1 and is 20 as expected. I click #test_button, check again and now window.test1 is 10 as expected. But when I click the button Send in Outlook and dump window.test1 into Office.context.mailbox.item.notificationMessages now window.test1 is 20 again.
Why is Office.onReady being called again when I click the Send button?
How can I detect that Office.onReady is being called by ItemSend event handler?

When you say "I load the Addin" does this mean in a Taskpane/ExecuteFunction?
If so, the ItemSend feature does not use the same sandbox/instance as the Taskpane. (In fact all buttons / extensions points do not reuse any web instances). In other words, if you have a Taskpane on one Button, and an ExecuteFunction on another Button, and ItemSend handler. Then they are treated as separate instance, and will all trigger their own onReady.
If you need to communicate information from one instance to another, it is recommended to use the CustomProperties to save information to the item, and then it can be referenced from other add-in instances.
https://learn.microsoft.com/en-us/javascript/api/outlook/office.customproperties?view=outlook-js-preview
i.e In your taskpane you could save a custom property test1 = 20. Then in your itemSend handler load custom property to get the value of test1.

Related

Ajaxinate Endless scolling has stopped product Quick View from working

I am using Shopify "Streamline Theme" with quick product view and I recently added infinite scroll to products on each collection using Ajaxinate.js.
When I open a collection page it loads with some products which is supposed to do, The products already there work fine with quick view and quick add to cart and also.
The Infinite scroll works fine and it loads new product fine but the problem is raised when the new products loaded through AJAX call doesn't have work with the quick view function.
I have tried to create a callback function to activate the quick view with no success, using the theme initialisation code with no success.
function callBack(){
theme.init();
theme.initQuickShop();
};
document.addEventListener("DOMContentLoaded", function() {
var endlessClick = new Ajaxinate({
method: "scroll",
loadingText: 'Loading...',
callback: callBack
});
});
Edit -------
My problem, is that when the page is loaded only the initial loaded products quickview elements are loaded in the DOM. When the scroll more button is clicked, the newly loaded products are loaded without their respective quickview elements. Hence why the quickview does't work for them. The theme.js file comes with this initialisation code:
theme.reinitProductGridItem = function($scope) {
if (AOS) {
AOS.refreshHard();
}
if (theme.settings.currenciesEnabled) {
theme.currencySwitcher.ajaxrefresh();
}
// Reload quick shop buttons
theme.initQuickShop(true);
// Refresh reviews app
if (window.SPR) {
SPR.initDomEls();SPR.loadBadges();
}
// Re-register product templates in quick view modals.
// Will not double-register.
sections.register('product-template', theme.Product, $scope);
// Re-hook up collapsible box triggers
theme.collapsibles.init();
};
I have tried to integrate this into a callback but no success, the quickview modal doesn't seem to load for the newly loaded products:
function callBack(){
ReloadSmartWishlist();
var $container = $('#CollectionSection');
theme.reinitProductGridItem($container);
// I have tried the following init qith no success:
// theme.init();
// theme.initQuickShop(true);
// theme.initQuickShop();
// sections.register('product-template', theme.Product, $container);
// AOS.refreshHard();
};
document.addEventListener("DOMContentLoaded", function() {
var endlessClick = new Ajaxinate({
method: "click",
loadingText: 'Loading...',
offset: 0,
callback: callBack
});
});
I am missing something but what? :/
Note for other things like loading products images with the callback and the wishlist app, it works as intended...
When you load elements via AJAX and if the events are not attached to a parent element that is not removed from the DOM, those elements will not have an attached event to them.
The term used here is event delegation.
Here is an example of non-delegated event:
document.querySelectorAll('a').addEventListener('click', function(){
// Do something
})
Since you are attaching the event to the existing "a" elements if you add new 'a' via AJAX those elements will not have the event since Javascript already attached all the events and it will not reattach them if you don't specifically recall them again.
Here is an example of a delegated event:
document.querySelector('body').addEventListener('click', function(target){
let target = event.target;
if (target.tagName === 'A'){
// Do something here
}
})
Where we attach the event to the body tag ( where it's a better idea to attach it to a closer none-modified parent element of the ajax items ) and once we click we check if our target tag is an "a" and do something then.
So long story short, you will need to delegate the quick cart link so that it works after you load the items via AJAX.
Drip is correct you need to delegate your event, but for people like me it's hard to completely understand how to do that.
I'm not sure how your quickview is structured, but if you open it with a .click function and can use jquery use the [.on() function][1].
For example: I use a quickview that opens on a button click. My button is attached to my product-grid-item.liquid with this bit of code:
<div class="quick-view-button">
<a class="quick-view" data-handle="{{ product.handle }}" href="javascript:void(0);">Quick View</a>
</div>
My quickview function originally looked like this:
function quickView() {
$(".quick-view").click(function () {
//all of the quickview code
What happens is exactly like you described. The event listeners only loaded on the first product load but nothing after an AJAX load.
Using jquery's .on() binds the event listener to the element meaning when it's loaded in later it'll still have the event. Here's an example of what my code looks like after using .on()
function quickView() {
$('body').on('click','.quick-view',function(){
I really hope this helps you or someone else with this problem.
[1]: http://api.jquery.com/on/

Event Registration in UI5 - attaching multiple listeners to an event

How can I add multiple event listeners to an event in UI5?
We have a master list with a dropdown that is correctly firing a select event on its controller. Sub controllers also need to be informed that this dropdown has changed in order to reload model data.
onAllRolesChange: function(oEvent) {
var key = oEvent.getParameter("selectedItem").getProperty("text");
if (this.ScreenId != null) {
this.loadScreenByRole(key);
// I could invoke the controllers directly, but that seems wrong
// controller2.update();
// controller3.update();
}
},
I assume what I should be aiming for is to call some sort of registerForEvent() method in each of the controllers, but I don't see anything like that in the SDK. fireEvent() and attachEvent() exist, but the examples I've seen appear to be for creating custom controls, or responding to browser events that SAP hasn't implemented.
As of UI5 1.65, multiple event handlers can be assigned when creating ManagedObjects / Controls:
(...) ManagedObjects now accept an array of multiple event listeners for an event. To
distinguish this use case from the already supported array with [data, listener, this], an array with multiple listeners must use nested array notation for each listener as well [in JS]. In XMLViews, multiple listeners have to be separated by a semicolon. (source)
Syntax
In XMLView
<Button press=".myControllerMethod; .mySubController.thatMethod" />
In JS
new Button({
press: [
[ listener1 ], // 1st listener
[ data, listener2, thisArg2 ] // 2nd listener
]
});
Demo
sap.ui.getCore().attachInit(() => sap.ui.require([
"sap/ui/core/mvc/XMLView"
], XMLView => XMLView.create({
definition: `<mvc:View xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
height="100%"
displayBlock="true"
>
<Button text="Press" class="sapUiTinyMargin"
press="alert('1st event handler'); alert('2nd event handler')"
/>
</mvc:View>`,
}).then(view => view.placeAt("content"))));
<script id="sap-ui-bootstrap"
src="https://openui5nightly.hana.ondemand.com/resources/sap-ui-core.js"
data-sap-ui-libs="sap.ui.core, sap.m"
data-sap-ui-async="true"
data-sap-ui-compatVersion="edge"
data-sap-ui-theme="sap_fiori_3"
></script>
<body id="content" class="sapUiBody"></body>
You could use the EventBus to inform about the change, and who ever wants could listen for the change. However, if the other controllers are not yet loaded they won't get the events of course... Maybe you can combine this with promises...
You could also use a global model with 2 way binding and use it for your dropdown. When ever the dropdown changes the change is reflected in the corresponding model. At the same time, in your sub controllers you could create a sap.ui.model.Binding(...) for the same global model + path etc used for your dropdown. Additionally, you would attach a handler for the change event of the Binding... That should work as well. However, this has the same disadvantage like using the EventBus, but maybe thatÄs not an issue for you...

Kendo UI Scheduler Event Link

Is it possible to open an url when an event is clicked? I want to route my users to an url contains details related to event.
Yes, you can. Just handle the change event of your scheduler like this:
$("#scheduler").kendoScheduler({
change: function (e) {
e.preventDefault(); // prevent the default action
if (e.events.length > 0) { // if true then user clicked an event, not an empty slot
window.location.href = 'stackoverflow.com'; // replace with the desired url
}
},
// ... set other properties ...
});
The e.events is an array that has list of clicked events.
Kendo Scheduler Change Event

Kendo Scheduler prevent editing/destruction of certain events

I've created a Kendo Scheduler that binds to a remote data source. The remote datasource is actually a combination of two separate data sources. This part is working okay.
Question is... is there any way to prevent certain events from being destroyed?
I've stopped other forms of editing by checking a certain field in the event's properties and calling e.preventDefault() on the edit, moveStart and resizeStart events if it should be read-only. This works fine, but I can't prevent deletes.
Any suggestions greatly appreciated.
Just capture the remove event and process it as you have with the edit, moveStart, and reviseStart events. You should see a remove event option off the kendo scheduler. I can see it and capture it in version 2013.3.1119.340.
I think better way is to prevent user from going to remove event in the first place. Handling the remove event still has its validity as you can delete event for example by pressing "Delete" key).
In example below I'm assuming event has custom property called category and events with category equal to "Holiday" can't be deleted.
remove: function(e)
{
var event = e.event;
if (event.category === "Holiday")
{
e.preventDefault();
e.stopPropagation();
}
},
dataBound: function(e)
{
var scheduler = e.sender;
$(".k-event").each(function() {
var uid = $(this).data("uid");
var event = scheduler.occurrenceByUid(uid);
if (event.category === "Holiday")
{
// use .k-event-delete,.k-resize-handle if you want to prevent also resizing
$(this).find(".k-event-delete").hide();
}
});
},
edit: function (e) {
var event = e.event;
if (event.category === "Holiday")
{
e.container.find(".k-scheduler-delete").hide();
}
}
FYI, you can do this...
#(Html.Kendo().Scheduler<ScheduledEventViewModel>()
.Name("scheduler")
.Editable(e => e.Confirmation(false))
)
which will deactivate the default confirmation prompt for the scheduler. Then you can do your own prompt on items you want.
There is also a
.Editable(e => e.Destroy(false))
that you can do to remove the X on the event window. This particular example would remove it for all of the events, but there might be a way to remove it for specific ones.

ExtJS 4 how to properly create a KeyMap for a window when using MVC

I have a simple ExtJS application that is written MVC style (much of my information from here).
My application creates a viewport, and has a view with a form, some fields and a button.
The application has a controller with a 'loginButtonClick" function, and the controller watches the click event with a:
this.control({
'loginwindow button[action=save]': {
click: this.loginButtonClick
}
}
That works great, but now when the login window is showing, I want the enter key to also execute the loginButtonClick method.
I have tried all kinds of things, but the basic issue I am having is WHERE to put the code for creating the keymap, and how to bind it to the proper instances.
For example, if I create the keymap in the controller (which is my preference), I need to get the specific view instance for that controller (I might have multiple windows open of the same kind).
So, How would you create a key map (or?) from within a controller for it's view (window), calling a local method (this.loginButtonClick) when the enter key is pressed?
What you can do is bind the code that initializes the keyMap to the login window afterrender event like this:
this.control{(
'loginwindow' : {
afterrender: this.initializeKeyMap
}
Then make a function that sets up the keyNav:
initializeKeyMap: function(window, options) {
this.keyNav = Ext.create('Ext.util.KeyNav', window.el, {
enter: this.loginButtonClick,
scope: this
});
}
Now when the dialog is loaded if the user presses the Enter key, it should execute your function.
You could setup all these things on your window render event. So when it is rendered you add an eventlistener for the enter key, and in the handler you call programatically click on the login button.
You can achieve this by adding the following listeners to your text/password fields in the form
listeners: {
specialkey: function(field, e){
if (e.getKey() == e.ENTER) {
var form = this.up('form').getForm();
submitLoginForm(form);
}
}
}
Here 'form' is your form and 'submitLoginForm' is the function called at form submit which I guess is loginButtonClick in your case.
Hope this helps.

Resources