I am using Decarta JS API PIN event handler
I am displaying pins on a map and the click event for each pin is fired correctly. The trouble comes in when I try pass the pin id to the handler. It ALWAYS displays the last id loaded regardless of which pin is clicked on. My code for declaring the listener is:
for (var idx = 0; idx < jsonObjects.devices.length; idx++) {
var device = jsonObjects.devices[idx]
deCarta.Core.EventManager.listen('click', function() {home.pinPushed(device.id);}, device.pin);
}
This is done in a loop through all the locations I am displaying. They are displayed correctly at their respective coordinates, and logging the previous line clearly shows that each pin is declared with the correct id. However, whenever I click on a pin, the LAST id declared is the only id ever passed to the handler:
home.pinPushed(device.id)
How do I get each pin to pass the unique id when the pin is clicked?
Try this :
deCarta.Core.EventManager.listen('click',
(function(id) { return function (){home.pinPushed(id);}})(device.id), device.pin);
Beautify version :
deCarta.Core.EventManager.listen('click', (function (id)
{
return function ()
{
home.pinPushed(id); //<---- id here is the closured variable
}
})(device.id), device.pin);
Explanation :
Js has a function scope ( not braces ({}) scope.)
Your for loop is not creating ANY scope/function.
What I was doing is to create execution context for each iteration.
for each iteration im sending the current device.id to a function which in turn , returns another function which closured the variable.
Related
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();
}
}
I use this code in GTM , that retrieves a number from another domain and stores it on a dataLayer value, with a dL.push. I want the variable to be available on a specific event.
(function ()
{
var callbackMethodName = "adnscallback";
window[callbackMethodName] = function(data) {
(function() {
dataLayer.push({
'event': 'ga_new',
"id2": data.uid
});
})();
}
var sc = document.createElement("script");
sc.id = "script_" + callbackMethodName;
sc.src = "https://example.site.com/getnumber?callback=" + callbackMethodName;
document.body.appendChild(sc);
})();
But I already create an event called ga_new (hardcoded, that creates all sort of values on the page) and when this codes run, it creates another event called ga_new, instead of pushing the values on the existing event. This creates issues with the tags as they are double firing.
What iam doing wrong and the push, instead of appending the data, it creates the event again?
You can not "update" a custom event. The "event" keyword is a signal to GTM that it needs to update its internal data structure with new values.
The datalayer variable in the page is an array of objects, the internal datastructure is a single object into which new values are merged, and existing values for the same keys are overwritten. GTM overwrites the native implementation of the JS "push" method with its own code that watches for the "event" keyword. So every time it encounters the "event" key changes are triggered.
You either need a different name for the event, or you change the trigger so that it only fires when both the "ga_new" event are set and the data.uid field is set.
// Post Div
<div id="post-1"><img src="art/image01.jpg" /></div>
<div id="post-2"><img src="art/image02.jpg" /></div>
<div id="post-3"><img src="art/image03.jpg" /></div>
// MYSQL Json Encode Generated Information
var allPosts = [{"type":"tumblr", "link":"www.blah.com"},
{"type":"instagram", "link":"blah.com"},
{"type":"tumblr", "link":"www.blah.com"}]
var postTotals = 3;
// Generates FancyBox Links for Clickable Post Div
function generateFancyBox(){
for(i=1;i<=postTotals;i++){
$("#post-"+i).click(function(){
alert(allPosts[i].link); // This alerts "www.blah.com" just fine.
$.fancybox({
"type" : "iframe",
"href" : allPosts[i].link // This Variable is always "Undefined".
});
});
}
}
I don't understand why href : allPosts[i].link - always becomes undefined.
This code is a shorthand version of a larger project I'm working on. I have html DIVs that I need FancyBox to create clickable links.
I can not seem to get the allPosts[i].link to print the URL. However, right above it, when I alert(allPosts[i].link); it prints the URL just fine.
What am I doing wrong?
Replace the $.fancybox call with an immediately executing function, which creates a additional closure.
(function () {
alert(i);
$.fancybox({
"type": "iframe",
"href": allPosts[i].link // This Variable is always "Undefined".
});
})();
2 problems:
1) In the click event handler, wrap the click handler assignment in an Immediately Invoked Function Expression (IIFE).
JSFiddle it.
function generateFancyBox(){
for(i=1;i<=postTotals;i++){
(function (ii) {
$("#post-"+ii).click(function(){
console.log(allPosts[ii-1].link);
//$.fancybox({ "type" : "iframe", "href" : allPosts[ii-1].link });
});
}(i))
}
}
By the time the click handler (and fancybox) does it's job, i is not what it was when it was passed, as a reference, to fancybox. Passing i in as a parameter to the IIFE you're creating a new scope with a new variable ii that gets locked in to the current value of the for loop i when it was passed to the click handler.
2) There's also a problem with the loop and DIV indexes not sync'ing with the image/link array. So I changed:
allPosts[ii] to allPosts[ii-1]
Your loop and image DIVs start with "1" as in for(i=1; and post-1 but your image/link array is zero-based, which means it starts with "0" as in allPosts[0]. So when grabbing the image/link element, decrement counter by one.
The real problem is that you are creating closures inside a loop. See this for further information.
What you have to do is to create the closure outside of the loop and call it as the handler (or callback) of your click event, passing i as argument, so
the closure :
function launchFancybox(i) {
return function () {
$.fancybox({
type: "iframe",
href: allPosts[i].link
});
}
}
Then bind the click events to each div inside your generateFancyBox() function like :
function generateFancyBox() {
for (var i = 0; i < allPosts.length; i++) {
$("#post-"+(i+1)).click( launchFancybox(i) );
}
}
NOTES :
we need to initialize i declaring it with var (in strict mode, it will return an undeclared variable error if you don't)
notice that i should always start with 0, otherwise you will never have access to the first element of the allPosts array. You need to use (i+1) to refer to each div's ID though.
you don't actually need var postTotals = 3; since you can always refer to the length of your allPosts array
See JSFIDDLE
I have a div which contains message updates sent by users. How can I assign a javascript variable to this div so that it's value is always the current content of the div which is consistently updating with messages?
var x = document.getElementById("MessageBox"); // only equals value at the
// time rather than present value
The easiest way to accomplish this would be to assign an .onchange event handler to the element, then assign the node's value to a variable held outside the scope of the function:
var myMSGvalue;
document.getElementById("MessageBox").onchange = function () {
myMSGvalue = this.value;
// this will work for <input>, not <div>
}
If you really need to assign your data to an html element, you should use an <input type="hidden">.
However, it would be much better to assign the data to a variable before you append it to the page. For example, let's say you have a web socket:
var messages = [];
socket.on("msg", function (msg) {
messages.push(msg);
// msg: { content: "Hello!", author: "notSure" };
functionToAppendToPage(msg);
})
Now, you can see all the messages received in messages and messages[messages.length - 1] will return the last message received (provided at least one message has been received).
If your message box field getting data using any Api or through Ajax. You can update the variable before assigning value to message box.
I'm working with two js files and sharing variables between them in a titanium app. In my main app.js I have 3 variables associated with each row in a table. I have an event listener for when a row is clicked to open a modal view whose components are in a separate js file. My three variables are below and on the click event I have an alert of the 3 variables and the 3 global variables.
var titleText = titleInRow.text;
var artistText=artistInRow.text;
Ti.App.myGlobalSongVar = titleText;
Ti.App.myGlobalArtistVar = artistText;
var rowNumber=e.row.name;
Ti.App.myGlobalRowNumber= rowNumber;
alert("titleText is: "+titleText+" and /n artistText is "+artistText+ " and /n row number is "+rowNumber +"/n TiAppmyGlobalSongVar is "+Ti.App.myGlobalSongVar+ " /n TiAppmyGlobalArtistVar is "+Ti.App.myGlobalArtistVar);
These are all returning the correct results. Then in my second js file, I also have the following alert:
alert("\n TiAppmyGlobalSongVar in modal is "+Ti.App.myGlobalSongVar+ " \n TiAppmyGlobalArtistVar in modal is "+Ti.App.myGlobalArtistVar + "TiAppmyGlobalRowNumber in modal is "+Ti.App.myGlobalRowNumber);
In the second js file The first time I click on a row, my second alert's variables are all undefined. The second time I click they are all defined but sometimes wrong. It seems to give the results of the variables for the row I first clicked which was undefined. Hope this question was clear. What am I doing wrong?
UPDATE PLEASE READ!!:
In the end, after trying:
Titanium.API.titleText = titleText;
Titanium.API.artistText = artistText;
and
Ti.App.Properties.setString('globalTitleText', titleText);
Ti.App.Properties.setString('globalArtistText', artistText);
and
Ti.App.myGlobalSongVar = titleText;
Ti.App.myGlobalArtistVar = artistText;
(which ALL worked the second time, but were undefined the first),
the only thing which worked was firing this event in my table event listener:
Ti.App.fireEvent('myCustomEvent', {
myTitleText: titleText,
myArtistText: artistText
});
and having this in my second js file:
var globalTitleText;
var globalArtistText;
Ti.App.addEventListener('myCustomEvent', function(event) {
globalTitleText=event.myTitleText;
globalTitleText=event.myTitleText;
//Ti.App.globalTitleText=event.myTitleText;
//Ti.App.globalArtistText=event.myArtistText;
alert('You sent me: '+event.myTitleText+" and "+event.myArtistText);
});
//However I can't use it here in my second js file (outside the custom event listener) as it is undefined.
Can anyone help me with the last bit of the problem?
I still don't know why the other methods didn't work. I've used them in different contexts before and they did work, but not in this particular instance!
Is the following code IN the eventListener?
var titleText = titleInRow.text;
var artistText=artistInRow.text;
Ti.App.myGlobalSongVar = titleText;
Ti.App.myGlobalArtistVar = artistText;
var rowNumber=e.row.name;
Ti.App.myGlobalRowNumber= rowNumber;
Because for your variables to be global, you need to declare them outside the eventListener function. Something like:
var rowNumber;
tableView.addEventListener('click',function(e) {
rowNumber = e.rowIndex;
}
rowNumber will be global if declared in App.js, whereas:
tableView.addEventListener('click',function(e) {
var rowNumber;
rowNumber = e.rowIndex;
}
won't.
Pleasee go through this link in titanium developer forum
Global Variables in Titanium
.........................
And it's also best to pass your own object it works in that case.
Passing object as global variable
Please Check it out and let me know
Note : also declare the variables in App.js :) if any value is to be modifies then do it in other JS.