Office 2013 Javascript API - Pass Variable to Callback of ".BindingDataChanged" - javascript

Background
I am writing a sidebar app for Excel 2013, and I have a wrapper function which updates the view of the sidebar. I need to trigger that function (and so trigger an update of the view) from multiple events, one of which is when the data in a bound area changes.
The problem I'm having is that I need to pass along a variable to that wrapper function. It needs data which I want to be able to save in a Setting and then load just once.
Code
Current Code:
Office.select("bindings#"+bindingID).addHandlerAsync(Office.EventType.BindingDataChanged, onBindingDataChanged);
function onBindingDataChanged(eventArgs) {
searchThroughData(eventArgs.binding.id);
}
function searchThroughData(bindingID) {
//repaint view
}
The above works to trigger the repaint. But it doesn't include the passed variable. What I'd expect is for the code to be something like this:
Attempted, Doesn't Work:
Office.select("bindings#"+bindingID).addHandlerAsync(Office.EventType.BindingDataChanged, onBindingDataChanged(eventArgs,data));
function onBindingDataChanged(eventArgs,data) {
searchThroughData(eventArgs.binding.id,data);
}
function searchThroughData(bindingID,data) {
//repaint view
}
This doesn't work, however.
Question
Any ideas how I can pass along this variable?

In your attempted solution, you are calling the onBindingDataChanged function instead of making it available for the handler to call. You'd need to do something like this (assuming that the variable "data" is available beforehand)
Office.select("bindings#"+bindingID).addHandlerAsync(Office.EventType.BindingDataChanged, onBindingDataChanged(data));
function onBindingDataChanged(data) {
return function(eventArgs) {
searchThroughData(eventArgs.binding.id, data);
};
}
function searchThroughData(bindingID,data) {
//repaint view
}
If data is a global variable, you can just do
Office.select("bindings#"+bindingID).addHandlerAsync(Office.EventType.BindingDataChanged, onBindingDataChanged(data));
function onBindingDataChanged(eventArgs) {
searchThroughData(eventArgs.binding.id, data);
}
function searchThroughData(bindingID,data) {
//repaint view
}

Related

jQuery trigger function from code loaded by append

Is there anyway to call a function or trigger an event from html loaded from an append() call? I have a tag It is filled when a user licks on a list of things like this. in my index.html I have something like this:
function doThis(someData) {
$.get("/url/"+someData, function(htmlFromServer) {
$("#something").append(htmlFromServer);
});
}
function doSomething(moreData) {
alert(moreData);
}
I want to be able to do something like this in the returned html
<div>
<p>This is an important message</p>
<script>
doSomething("this message is different for each page");
</script>
</div>
I want to be able to call one function, but depending on what is returned, I alert a different message. I want the front end to call one endpoint, but what happens next is dynamic. I don't want to do a huge if block in my doThis(), or worse, a function for each possibility that "someData" may have.
You could pass a function to doThis. You also probably meant to concatenate the someData.
function doThis(someData, cb) {
$.get("/url/" + someData, function(htmlFromServer) {
$("#something").append(htmlFromServer);
cb()
});
}
doThis('somepath', () => console.log('append successful'));
I got this to work by defining all my functions in a javascript file and import like usual.
I then make a decision on the back end that all of the possible html that is returned by the jQuery get() call, has a hidden form field with the id of "x" and the value being the function name I want to call after the .append(htmlFromServer) is done.
So i have this function in a .js file defined int the head as usual. I then do this:
$.get("/url/"+someData, function(htmlFromServer) {
$("#myDiv").append(htmlFromServer).append(function() {
var functionToCall = $("#x").val(); // x is the id of the element
window[functionToCall]();
});
}
And I now have a generic way of handling different data depending on the server response without a big if block.

Trigger function from within formatter? (responsiveLayout: collapse > toggleList)

Is there a way to trigger a function from within a rowFormatter? I'm using the responsiveLayout: "collapse"-option, and I really like it.
However, I would like to trigger the toggleList function (or what's it's called.... 1 from '19)
I would like to not go the .click() way, so I created my own (rip-off) solution within the rowClick:
let isOpen = row._row.modules.responsiveLayout.open;
var collapseEl = row._row.element.querySelector('div.tabulator-responsive-collapse');
if (!(isOpen)) {
collapseEl.classList.add("open");
if (collapseEl) {
collapseEl.style.display = '';
}
} else {
collapseEl.classList.remove("open");
if (collapseEl) {
collapseEl.style.display = 'none';
}
}
row._row.modules.responsiveLayout.open = !(isOpen);
But... There must be a good way to trigger toggleList(), instead of writing a rip-off function, which doing the same thing...
I've tried to look through the values and functions in row._row, with no luck. I'm 99.7% sure that I missed this part in the documentation........ But I've really tried to search the best I could.
TL;DR: I would like to trigger the toggleList() function defined within formatter, in my rowClick() event-function. Is that possible?
There is no toggleList function built into Tabulator.
In the example you reference there it is simply a function called toggleList that is defined inside the row formatter and triggered when an element added by the row formatted is clicked.
Because the toggleClick function is defined inside the row formatter its scope is limited to that formatter function so it cannot be accessed from outside it.
one way to get around this would be to assign the function to a property on the row data object then you could access it from else where in the table.
So if we take the example you provided a link to and at the top of the customResponsiveCollapseFormatter function add the following:
var data = cell.getData(); //retrieve the row data object
Yhen where we define the toggleList function, instead of the simple function definition we can assign it to a property on the data object, lets call it collapseToggle, we will also tweak it so it dosnt need the isOpen property passed in and insted flips the state of the open variable itself, that way it can be called from anywhere outside the formatter without knowledge of the current state:
data.collapseToggle = function toggleList(){
open = !open;
Then in our cellClick function we can check to see if the collapseToggle property is defined on the row data and then call it:
cellClick:function(e, cell){
var data = cell.getData();
if(data.collapseToggle){
data.collapseToggle();
}
}

How to get notified when grid changed in AngularJS ui-grid?

If there is a way in ui-grid that I can know a grid is finish updating the rows?(like a filter is being applied etc)? I want to run some function after the grid view changes.
I tried the following method:
$scope.filteredRows = $scope.gridApi.core.getVisibleRows($scope.gridApi.grid);
$scope.$watch('filteredRows', function(){console.log('view updated');});
The above approach works when the grid just finish initiating, after that, it won't work anymore.
I also tried using the filterChanged api:
$scope.gridApi.core.on.filterChanged($scope, function() {
console.log('filter changed');
foo();
});
The problem with this method is that although I can get notified when the filter is changed, but if the grid is very large, it needs some time to finish updating the view, and before that, the function foo() is being called before the grid update is finished.
Any idea will be appreciated.
I've seen use of $scope.grid.api.core.on.rowsRendered( $scope, $scope.col.updateAggregationValue ); in ui-grid-footer-cell.js. I'm not sure exactly when rowsRendered fires, but given it's being used to calculate aggregations and aggregations require knowledge whenever the rows are changed, and must run after the rowsProcessors finish running, there's a good chance that it's what you want.
EDIT: the framework to use it would be:
Define a function that you want to call when the visible rows have changed
var myFunction = function() {
do some stuff
};
Set this function to be called whenever rows are rendered
$scope.gridApi.core.on.rowsRendered( $scope, myFunction );
Well, I found a workaround, in order to call the function after the grid is updated, which takes some time, I added a delay in filterChanged event:
$scope.gridApi.core.on.filterChanged($scope, function() {
console.log('filter changed');
$timeout(foo(),800);
});
To use the $timeout, you need to add that to your controller first.

Access a parameter in Javascript

i've a strange problem with JS (probably a noob bug), but i'm stuck with it
In function fillInVersionsList, if i put an alert("tempo") or a break in firebug, i can access to my datas in parameter (ie : alert(pSimulator.simulatorData['LastVersion']) and i've the right result. The problem is that if i don't put an alert/firebug break before my access to datas, i've a JS error pSimulator.simulatorData is undefined.
$(document).ready(function() {
var simulator = new Simulator();
// Load SimulatorData into the simulator class
initSimulatorData(simulator);
// Fill in datas into VersionsList (2nd arg = Id of the list)
fillInVersionsList(simulator, $('#VersionsList'));
});
function initSimulatorData(pSimulator)
{
$.ajax({
url: "getData.php?action=init",
success: function(data) {
pSimulator.initSimulatorData(data);
}
});
}
function fillInVersionsList(pSimulator, pSelect)
{
//alert("tempo");
alert(pSimulator.simulatorData['LastVersion']);
pSelect.html('<option>test</option>')
}
function Simulator()
{
var simulatorData;
this.initSimulatorData = function(pSimulatorData)
{
this.simulatorData = pSimulatorData;
}
}
Is there something to solve this problem?
Thanks in advance
I suspect initSimulatorData is loading some data asynchronously.
Adding the alert gives it long enough for the data to be loaded.
You will need to add some sort of callback function, eg:
initSimulatorData(simulator, function () {
// Fill in datas into VersionsList (2nd arg = Id of the list)
fillInVersionsList(simulator, $('#VersionsList'));
});
Whats looks like from your problem is that simulator is taking time to initialize and when fillInVersionsList is called pSimulator is still not completely initalized.
When you put an alert it is getting some time delay by which time simulator is initalized.
Check if there is any callback method after simulator is completely initialized and then call fillInVersionsList method after that.
what does initSimulatorData(simulator) does? Is there any asynchronous code invloved in this?

AJAX (XMLHTTPRequest) time delay

I am new to AJAX and have this issue.
I am creating a photogallery page and on it I created a Javascript class which manager the whole thing. Here is a method from the class which calls XMLHTTPRequest object which was initialized successfully earlier:
this.AJAX_update = function(id) {
//initialize AJAX - this is done successfully
this.AJAX_initialize();
var url = "ScriptLibrary/gallery_update.php?img=" + id;
//this.ajax_request is an internal variable which is
//initialized in this.AJAX_initialize() and is
//XMLHTTPRequest type
this.ajax_request.open("GET", url, true);
this.ajax_request.onreadystatechange = processAJAX;
this.ajax_request.send(null);
}
I could not figure out how to call class' internal method in onreadystatechange, so I created a function outside of the class which calls a method inside the class provided that the instance of the class has been created on the page which it is. The instance of the class on the page is the variable 'gallery'. Here is the function:
function processAJAX() {
gallery.AJAX_process();
}
Here is the code of the AJAX_process() function:
this.AJAX_process = function() {
if (this.ajax_request.readyState == 4) {
if (this.ajax_request.status == 200) {
//get the response
var response = this.ajax_request.responseXML;
//Here I set the internal variables according to the value
//returned from the server
//...........
//...........
//...........
//change image on the page
var self = this;
setTimeout(function() {
self.swap_dissolve();
}, 50);
}
}
}
So, here is my question:
Initially I tried to call the internal function directly by just doing this.swap_dissolve() however it does not work. Basically the AJAX_process() method changes values of certain class' internal variables according to the response from the server, and those values are used in the swap_dissolve() to actually change the image on the page. What would happen is that swap_dissolve() would not change the image correctly because it was seeing null or not updated values of the variables AJAX_process() updated. When I added the time delay, the problem disappeared. I don't understand why that is happening. I do check that the readyState == 4, so the response from the server has been completely returned, therefore whatever values the function sets, are final and can be used right away, so why can't I use those values right away and have to wait?
Any suggestions or faults in my code to resolve this issue would be highly appreciated.
If you have any question to me, please post response to this question.
Thank you.
You want to use a 'closure' (e.g. pass a function-object to setTimeout).
Imagine this (heck, substitute it in your code to see):
// when response = 200
var someValue = "blahblah" // whatever you read in
setTimeout(function () {
alert(someValue)
}, 1000)
The alert should display "blablah" (or whatever you assigned to it). The value is available to the anonymous function object because a closure-binding has been created (the function can access the variables of the enclosing function scope(s).
Here is a reference: http://jibbering.com/faq/notes/closures/ and more of a soft intro: http://blog.morrisjohns.com/javascript_closures_for_dummies.html
Happy coding.
Edit/P.S. You can also write the AJAX handler like (it's just a closure to force the correct 'this' context -- some frameworks offer convenience functions for this):
var self = this
this.ajax_request.onreadystatechange = function () {
self.process_AJAX()
}
You can even use a double-binding, but... happy coding, again :-)

Categories