setAttribute function doesn't work in live mode - google-tag-manager

I am searching for an answer/solution why my JS function with get attribute() doesn't work in live mode? In GTM debug mode, everything is ok, all events based on this function are sent normally.
Thank You
function() {
var x = Array.prototype.slice.call(document.querySelectorAll("div[data-product]"))
for (i=0; i< x.length;i++) {
if(Number.isInteger(i/2) && Number.isInteger(i/3)) {
x[i].setAttribute("seen-product", "");
}
}
return undefined;
}

This looks like a variable template. In preview mode, all variables are evaluated, no matter if they are used or not. In the live version, variables are only evaluated when they are being used (in a tag, template, or trigger).
Since you do not use the variable to return any data, I suspect that you not actually use it anywhere, so it would not be evaluated (i.e. the function would not be run) in live mode.
A variable changing things instead of returning a value is called a "side effect". Variables should not have side effects (read e.g.here: https://www.simoahava.com/analytics/variable-guide-google-tag-manager/ (half way down the page under the headline "Variables must never have side effects"). You could do this e.g. in a custom tag.

Related

Is console.log asynchronous?

I had an incident in my Angular 6 application the other day involving some code that looked like this:
console.log('before:', value);
this.changeValue(value);
console.log('after:', value);
changeValue() modifies value in some way. I expected to see the unmodified value in the console before the call to changeValue() then the modified value after. Instead I saw the modified value before and after.
I guess it proved that my changeValue() function worked, but it also indicated to me that console.log() is asynchronous--that is, it doesn't print out the value to the console right away; it waits for a bit... and when it finally does print the value, it's the value as it is at that moment. In other words, the first call to console.log() above waited until after the call to changeValue() before printing, and when it did, the value had already changed.
So am I correct in inferring that console.log() is asynchronous. If so, why is that? It causes a lot of confusion when debugging.
No, console.log() is synchronous. It's actually your changeValue() function that doesn't work the way you think.
First, JavaScript doesn't pass variables by reference, but by pointer to object (as do many other languages). So although the variable value inside your function and the variable value in the outer scope both hold the same object, they are still separate variables. Mutatin the object affects both variables, but direct assignments to one variable do not affect the other. For example, this obviously wouldn't work:
function changeValue(x) { x = 123; }
Second, in JavaScript the shorthand assignment a += b does not mutate the existing object stored in a. Instead it works exactly like a = a + b and assigns a new object to the variable. As mentioned above, this only affects the variable inside the function, but doesn't affect the one outside, so your changes are lost after the function returns.
More examples:
function willwork(obj) { obj.foo = "bar"; }
function willwork(obj) { obj["foo"] = 1234; }
function willwork(obj) { obj.push("foo"); }
function wontwork(obj) { obj = "foo"; }
function wontwork(obj) { obj += 123; }
function wontwork(obj) { obj = obj + 123; }

Connect signal to intrinsic handler in JavaScript

Take MenuItem as an example, normally in QML, specifying the handler for the triggered signal is simple:
MenuItem {
onTriggered: {
console.log("Hey");
}
}
Now if I want to do the same thing, but instead to a dynamically created MenuItem, e.g. via Menu.addItem(), then what is the syntax like to connect and specify the signal handler?
I didn't expect this to work, but here is a working solution:
function onTriggered() {
console.log("Hey");
}
var newItem = myMenu.addItem("Item 1");
newItem.triggered.connect(onTriggered);
Nevertheless is there a better way? Above I defined a custom function that happened to be named onTriggered, but it can be named anything, right? So this code piece doesn't make use of the built-in handler, that's why I'm wondering if there's a neater solution?
More importantly, later on I've noticed further problems with this approach: in a for loop, if there is a temporary variable used by the handler, things don't work any more:
for (var i = 0; i < myArray.length; i ++) {
var info = myArray[i];
var newItem = myMenu.addItem("Item " + i);
newItem.triggered.connect(function() {
console.log(info);
});
}
Here you'll see that console prints the last info in myArray for all added menu items when triggered. How can I properly set up independent handlers for each individual menu item?
In addition to the comments, you can easily make it "easier":
Menu {
id: myMenu
function add(text, handler) {
var newItem = addItem(text)
newItem.triggered.connect(handler)
}
}
And there you have it, problem solved, now you can simply myMeny.add("Item 1", onTriggered)
As for the result you get in the loop and functor, that's because of JS's scoping rules. Check the linked answer for details how to work around that.
So this code piece doesn't make use of the built-in handler
Don't think of onSignal as a handler, it is just a hook to attach a handler. Think of it as the declarative connection syntax. Sure, you can also use the Connection element in declarative, but it only makes sense when the situation actually merits it.
I think this confusion stems from some other language / framework which does generate handler methods for you. A onSignal is different from function onSignal() { expression } - the latter is a handler function, the former is handler hook, which just connects the signal to the bound expression.eval(). The Qt documentation too refers to onSignal as a handler, which IMO is technically and conceptually wrong, since the handler is the code which gets executed, the handler is whatever you bind to onSignal.
So you can rest easy, the code you are worried about does not result in any sort of redundancy or inefficiency and doesn't leave anything unused and is in fact the correct way to do things in QML.
All that being said, you can have "built in handlers", but it is a very different thing:
// SomeItem.qml
Item {
signal someSignal
onSomeSignal: console.log("I am a built in handler")
}
// main.qml
SomeItem {
onSomeSignal: console.log("I am another handler")
Component.onCompleted: {
someSignal.connect(function(){console.log("Yet another handler")})
someSignal()
}
}
And the output in the console will say:
qml: I am a built in handler
qml: I am another handler
qml: Yet another handler
As you see, it not really a handler, but a connection hook. There is no shadowing, no "replacing / not using the built in handler", there is just a signal with 3 connections to the evaluation of three expressions.
Using signal.connect() with a named function does come with one advantage, you can later signal.disconnect(namedFunction) if you need to remove a built in or another handler. I am not sure if you can do this if you use onSignal: expr since you don't have a way to reference that anonymous expression. Note that if you use onSignal: namedFunction() this will not work, you will not be able to signal.disconnect(namedFunction) because the signal is not directly connected to that function, but to an anonymous expression invoking it.

In Drupal 7, why is autocomplete not attaching to this list of form elements?

I'd like to attach autocomplete to a particular list of fields in Drupal 7. The fields have FIELD_CARDINALITY_UNLIMITED, so there could be anywhere from 1 to whatever. I'm using the following code:
function mymodule_form_alter(&$form, &$form_state, $form_id) {
if (array_key_exists('mymodule', $form)) {
$indices = array_filter(
array_keys($form['mymodule']['und']),
function($item) {
return is_numeric($item);
}
);
foreach($indices as $index) {
$form['mymodule']['und'][$index]['value']['#autocomplete_path'] = 'api/node/title';
}
}
}
...however, my autocomplete behavior is not being attached. I've used the exact same code in a similar situation - the only difference is that I was adding the autocomplete to a field that had a cardinality of 1 rather than unlimited. That doesn't seem like it should change anything. I've verified that the autocomplete is attaching by doing a debug($form['mymodule']) after the assignment statement, and it is definitely there. I have also debugged the exact array path I am trying to get in each iteration of the foreach loop, and it is definitely the correct form value.
EDIT: Is it possible that the issue is with more than one module altering this form using hook_form_alter()? I'm performing the exact same operation as above (but on a single field) in a different module, on the same form.
EDIT2: I've noticed that if I put a debug statement inside the foreach loop, I see the autocomplete value is set on the proper value each iteration. If I place the debug statement outside the foreach loop, the autocomplete path is no longer set. Somehow, either during the course of iteration, or after iteration, it looks like my changes are being destroyed? I tested this by assuming $index to be 0, and writing a hard-coded statement to attach autocomplete - this allowed auto complete to work correctly. To be clear, I am seeing something like the following:
function mymodule_form_alter(&$form, &$form_state, $form_id) {
if (array_key_exists('mymodule', $form)) {
$indices = array_filter(
array_keys($form['mymodule']['und']),
function($item) {
return is_numeric($item);
}
);
foreach($indices as $index) {
$form['mymodule']['und'][$index]['value']['#autocomplete_path'] = 'api/node/title';
// Debug statements here show that the value '#autocomplete_path' is set properly
debug($form)['mymodule']['und'][$index]['value']);
}
// Now, the '#autocomplete_path' key does not exist
debug($form)['mymodule']['und'][0]['value']);
// This will make autocomplete attach correctly
$form['mymodule']['und'][0]['value']['#autocomplete_path'] = 'api/node/title';
}
}
You've spelt it #autcomplete_path...it should be #autocomplete_path :)
If you're defining the field (and widget) yourself then you should just add the autocomplete in your module's implementation of hook_field_widget_form() rather than altering the form.
If you're not defining the widget yourself, take a look at hook_field_widget_form_alter() and hook_field_widget_WIDGET_TYPE_form_alter() which will let you alter the widget form for a specific field.
Try this:
1) change ['mymodule']['und'][$index]['value'] in your code to the id of your text form input example
$form['search_form_block']
['#autocomplete_path']='yourcall_back_function_which_returns_data';
I think the mistake is your are trying to work to replace the value of the the field but you have to change the value of the format widget. In this case the input field.
2) Also make sure 'api/node/title' call back works using x debug.
Let me know if it worked.
Cheers,
vishal
I resolved the problem by manually enumerating my indices rather than programmatically doing so, e.g. $form['mymodule']['und'][0]... - this appears to be a PHP issue related to scoping of variables in foreach rather than a Drupal problem.

Rational Functional Tester - How can I get scripts called from a parent script to use the parent's data pool?

I'm fairly new to Rational Functional Tester (Java) but I have one large blank. I have an application that is in an agile development environment so some of the screens can flux as new interfaces are brought online.
For this reason I'm trying to modularize my test scripts. For example: I would like to have a login script, a search script, and a logout script.
I would then stitch these together (pseudo code)
Call Script components.security.Login;
Call Script components.search.Search;
//verification point
Call Script components.security.Logout;
By breaking the testing script into discrete chunks (functional units) I believe that I would be better able to adapt to change. If the login script changed, I would fix or re-record it once for every script in the application.
Then I would call that script, say, "TestSituation_001". It would have need to refer to several different data pools. In this instance a User datapool (instead of a superUser datapool) and a TestSituation_001 datapool, or possibly some other datapools as well. The verfication point would use the situational datapool for its check.
Now, this is how I would do it in an ideal world. What is bothering me at the moment is that it appears that I would need to do something entirely different in order to get the child scripts to inherit the parents.
So my questions are these:
Why don't child scripts just inherit the calling script's data pool?
How can I make them do it?
Am I making poor assumptions about the way this should work?
If #3 is true, then how can I do better?
As a side note, I don't mind hacking the heck out of some Java to make it work.
Thanks!
I solved my own problem. For those of you who are curious, check this out:
public abstract class MyTestHelper extends RationalTestScript
{
protected void useParentDataPool() {
if(this.getScriptCaller() != null) {
IDatapool dp = this.getScriptCaller().getDatapool();
IDatapoolIterator iterator = DatapoolFactory.get().open(dp, "");
if(dp != null && iterator != null) {
//if the datapool is not null, substitute it for the current data pool
this.dpInitialization(dp, iterator);
}
}
}
}
This will use the same iterator too. Happy hunting...
Actually, after some reflection, I made a method that would make any given script use the Root calling script's DataPool. Again, happy hunting to those who need it...
/*
* preconditions: there is a parent caller
* postconditions: the current script is now using the same datapool / datapool iterator as the root script
*/
protected void useRootDataPool() {
//if there is no parent, then this wouldn't work so return with no result;
if(this.getScriptCaller() == null) return;
//assume that we're at the root node to start
RationalTestScript root = this;
while(root.getScriptCaller() != null) {
root = root.getScriptCaller();
}
//if this node is the root node, no need to continue. the default attached datapool will suffice.
if(this.equals(root)) return;
//get the root's data pool (which would be the parent's parent and so on to the topmost)
IDatapool dp = root.getDatapool();
if(dp != null) {
//check to make sure that we're not trying to re-initialize with the same datapool (by name)
//if we are, then leave
if(dp.getName().equals(this.getDatapool().getName())) return;
//this basically says "give me the iterator already associated to this pool"
IDatapoolIterator iterator = DatapoolFactory.get().open(dp, "");
//if we have an iterator AND a data pool (from above), then we can initialize
if(iterator != null) {
//this method is never supposed to be run, but this works just fine.
this.dpInitialization(dp, iterator);
//log information
logInfo("Using data pool from root script: " + root.getScriptName());
}
}
}

Can i get an access to a flash player`s events pool?

I want to trace every event on every object, is there any way to do it?
Yes and no.
The one way is to simply override its dispatchEvent function:
override public function dispatchEvent(event:Event):Boolean
{
// Do something with event.
return super.dispatchEvent( event );
}
The problem, however, is that this does not always work -- sometimes dispatchEvent is not called if a child object does something. It also will not work if you are unwilling to create a special class for each instance.
Another alternative is to iterate through an array of different event types:
var evtTypes:Array = [ MouseEvent.CLICK, MouseEvent.ROLL_OVER,
MouseEvent.MOUSE_DOWN...
Event.ADDED, Event.ADDED_TO_STAGE... etc.];
for( var i:int = 0; i < evtTypes.length; i++ )
{
target.addEventListener( evtTypes[ i ], trace );
}
The problem with this method is that you'll not be able to capture custom events, only the events you have in your list. I would definitely recommend the second method for most learning and debugging problems.
I suppose a more important question, however, is "What do you want to do with these events?" Most of the documentation lists all of the events an object will dispatch: if you scroll down in the MovieClip documentation, you'll see an example.
You have to create your own registry and access it that way. So yes, there is a way to do it, but no, not easily.

Resources