I'm trying to better understand the fundamentals of A-Frame.
I understand how to use the getAttribute() and setAttribute() methods for
accessing component data. However I don't understand why we use them instead of just accessing the components attributes using dot notation.
A lot of people use something like:
document.querySelector("#myText").getAttribute('text').value
Why not use:
document.querySelector("#myText").components['text'].data.value
Is there something wrong with using that second way? What are the pitfalls? In my experiments I have experienced both ways returning "undefined" due to the code running before the scene is finished loading, but I have learned how to avoid that using the "loaded" event.
As you can see at this glitch example (https://glitch.com/~text-hierarchy) I've successfully printed data to the console using both ways.
Part of the standard
Both getAttribute(prop) and setAttribute(prop, value) are used to get/set attributes of any other DOM elements. MDN links here (get, set)
Compatible with aframe components
If you have a update() function in your a-frame component, setAttribute() calls it, notyfing, that a property of a component has changed.
It's not mandatory, it's even faster to modify some properties directly, but it has risks, like undefined behavior if a developer uses update() to monitor changes.
Related
I created a click event in adobe launch which will capture the value of the link and send it to analytics. I have created a data element for saving the value and in my DOM I am saving value in local storage.
Local storage code:
$('.card').click(function() {
var name = $(this).text();
localStorage.setItem('productName', name);
});
My problem is when I click the first link no value got saved, but after when I click second link it saves the value of first link and on third link saves value of second link and so on. I want to save current link value in evar3 variable.
Data element:
Rule:
Set variables:
Thanks,
Harshit
I'm scratching my head a little bit about why your jQuery selector doesn't match your Rule selector, but that's probably not immediately related or relevant, considering you said you are seeing data pop, in general, so at face value, I'm going to ignore that.
But in general, it sounds like your jQuery code is getting executed after the Rule is evaluated, so it's effectively one step behind. I'm not sure there's much if anything you can do about that if you aim to keep two separate click event listeners like this.
You're going to have to restructure to have one chain off the other (which I don't think is actually feasible with jQuery > Launch as-is. Maybe if you write two separate custom code blocks with promise chaining but that kind of sidesteps Launch and IMO is over-complicating things to begin with (see below)). Better yet, merge them into a single click event listener. On that note..
Why do you have two separate click event listeners? Is the sole reason for this to pass off the link text? Because you can reference the clicked element in the Launch Rule itself.
You should be able to reference %this.innerText% in the Set Variables fields. Or you can reference this object in custom code boxes within the Rule.
IOW at face value I don't see why you need or should be creating/using that jQuery listener or pushing to local storage like that.
Update:
You commented the following:
I tried with %this.innerText% it is also not showing current values. I
am pushing the value first to local storage because my link values are
generating on runtime through an API. They are not hardcoded. I am
also trying to figure out why my rule is getting fired before my
jquery is evaluated. But when I check in console using
_satellite.getVar('Product Name'); it shows me the correct value, but in debugger console value is wrong. Can you show me the way you want
to create rule to getting it fired correctly ? Thanks,
Okay so your link values are generated runtime through an API call? Well okay now that sounds like where the (timing) issue is, and it's the same issue in principle I mentioned you prolly had between the jQuery and Launch code. Assuming you can't change this dynamic link functionality, you have two options:
1. Explicitly trigger a launch rule (direct call rule) in a callback from the API code that's generating the link values.
This is the better method, because you won't have race condition issues with your API vs. link tracking code. The biggest caveat about this method though is that it requires requires you to actively add code to the site, which may or may not be feasible for you.
I have no idea what your code for generating the link looks like, but presumably it's some ajax call and generated from the success callback. So in the callback, you'd add a line of code something like this:
_satellite.track('product_image_click', {
text : elem.innerText
});
Again, I don't know what your API code that generates the link looks like, but presumably you have within it some element object you append to or update the DOM, so for this example, I will refer to that as elem.
'product_image_click' - This is the value you use for the direct call rule identifier in the interface, e.g. :
And then _satellite.track() call also includes an object payload in 2nd argument that you can pass to the direct call rule. So in the code above, I set a property named text and give it a value of elem.innerText.
Then, within the direct call rule, where you set variables, the data you passed can be referenced from event.details object within custom code box (e.g. event.details.text), or using % syntax within the interface fields (e.g. %event.details.text%).
2. Make use of setTimeout to delay evaluating the link click.
The one advantage of this method over option #1 is that it is passive. You don't have to add code directly to your site to make it work.
However, this is the shadier option, because you don't really know how long it will take for your link to be generated. But generally speaking, if you can determine it takes on average say 250ms for the link generation API to do its thing, and you set the timeout to be called at say 300-500ms, then you will probably be okay most of the time. However, it's never a 100% guarantee.
In addition, if clicking on the link ultimately redirects the visitor to another page, then this solution will likely not work for you at all, since the browser will almost certainly have navigated before this has a chance to execute. Because of this, I normally I wouldn't even mention this as an option, but since you mentioned this link involves an API that generates the link values when clicked, I figured maybe this isn't a navigation / redirect link, so maybe this is an option you can go for, if you can't do option #1.
First, create a direct call rule the same as shown in option #1. This will be the rule that receives the link text and makes the Adobe Analytics (or whatever other marketing tag) calls.
Then, create a separate rule as a click event, similar to what you are currently trying to do (listening for the link click, based on your css selector). In this rule, you won't be setting any AA variables. Instead, add a custom js box with code like this:
var elem = this;
(function (elem) {
window.setTimeout(function() {
_satellite.track('product_image_click', {
text : elem.innerText
});
}, 500);
})(elem);
So when the rule is triggered, it will create a setTimeout callback to call the first rule, passing the link text in the payload. And.. hopefully the 500ms timeout shown in the code example is enough time for the API to do its thing. You may be able to adjust it up or down.
Rather than defining it in Data Element I would say its better to keep it directly in the Rule itself. Please try with this %this.#text%.
Let me know if this helped.
I'm new to Ractive.js and using "magic mode" so that updates to my JS objects automatically trigger updates to my UI. Everything is working great except that I have some properties which are added to my bound objects after my Ractive instances are rendered. This means that Ractive doesn't see changes to those properties, so the "magic" doesn't work for them.
As a workaround, I've found that I can just initialize those properties with 'empty' values before passing them to Ractive, but it feels a little unnatural.
Is there a preferred way to inform Ractive that I've added a new property to an object that it should start tracking?
My experince is that you can du that in 2 ways.
The first is the one you describe in your post.
The second is to "add" the property through the template that you use to render the object.
What ractive actually do when you set magic mode to true is "wrapping/replacing" your field with a property wich monitores the changes to the field.
I have had som issues using magicmode when i use Object.DefineProperty on my data objcect. (stuff get called twice)
I would go for soulution 1, and define all fields ahead of time.
Also be aware of setting your field(wich is actually now a property replaced by ractive) to something that is not a valuetype as that will again overwrite the property and mess up magicmode.
Debugging an app & I stumbled upon something I never noticed before. For a quick example, I've got a simple link with 2 helpers to style it, like this:
<a class="{{tabHasError}} {{activeTab}}">Test</a>
The helpers that go into this are as follows:
tabHasError: function() {
console.log('invalidated!');
}
activeTab: function() {
if (Session.equals('activeTab', this.tabIdx)) return 'active';
}
Now, every time the Session var changes, activeTab gets invalidated, which is expected. What's not expected is that tabHasError is also invalidated. Why does this happen? Is this normal? Is it because they're both attached to the same element? Aside from merging the functions, any way to avoid this? Or even better, why did MDG make this design decision?
With iron-router, it's normal to observe the behavior you're describing.
The current template in use will be refresh as soon as there is a change into the main computation dependencies. Calling Session.set will call the refresh of the template variable. For sure, it's a lot, but it is one of the simplest way to be sure the template is always up-to-date.
If you're looking for larger app, you could have a look on React.js integration, which will give you the ability to refresh only the good variable on your template.
In fact, in your example, the value of tabHasError should not change, but the re-rendering of the template will called the function tabHasError to check if there is any change. In this case, no.
I'm around if the behavior isn't clear enough. Have a tremendous Sunday!
I noticed that this only happens in an element's attributes. I think this behaviour is very specify, according to Event Minded videos regarding the previous UI engine (Shark): it only rerenders affected DOM elements.
Having in consideration that in your code Blaze is rerendering the DOM element, it makes sense to invalidate previous computations related to it. If you place this helper inside the a element it won't be invalidated.
I've got a model class with custom change events, which is working fine if I make a reference to that class in my mxml using;
[Bindable] private var firstClass:FirstClass;
The objects gets filled by a server side script, so don't worry, firstClass isn't null.
Anyhow, accessing firstClasses properties in mxml works perfectly fine using curly brackets. The binding works just as expected.
However, is there any way to access firstClasses properties and set them to say a label with pure Actionscript.
lblTest.text = firstClass.property;
The code above doesn't work. I suppose because it sets a fixed value to the label.
I'm aware of using BindingUtils.bindProperty to explicitly set the source and destination for the binding. However, this turned out to cause huge performance issues in my (mobile) application.
So is there a simpler, more efficient way to do this?
No. The BindingUtils uses propertyChanged events to detects when an object's property changes. You won't be able to bind something without listening to events, and the most painless way to do it is using BindingUtils.
I have 2 components for example (editor.mxml using mx:windows), when I click an edit button, I want to get the current value from the other component's datafield? (datagrid.mxml using mx:window)
I do know how to access the main MXML's datagrid by parentDocument or Application.application method, but stumped block if I want to access other way as mentioned above. Keep the code as simple as possible.
You could either do dependency injection, that is, give component A a reference to component B so that they can communicate directly (example of tighter coupling,) or have both components communicate through a common mediator using events (example of more loose coupling.)
Both of those options would be implemented wherever it is that you're creating those components (A and B in this example) and adding them to the display list.
This might be more complicated than it deserves, and it smacks of Pattern-Fever, but you could use a mediator class that listens for the CLICK event from the button and knows enough about the other component to query its property. It could even transmit that data using a custom event, which the button listens for.
While this involves three classes instead of two, it often turns out to be easier to have two components that focus on looking good and one that worries about coordination.
Cheers
Try this:
FlexGlobals.topLevelApplication
This points Your root. From the root You can grab every element You want.
You can also add an id to the custom component like this,
<custom:Editor id="myCustomComponent">
</Editor:AddressForm>
and
access your datagrid's value like this,
var data:ArrayCollection = myCustomComponent.DatagridID.dataProvider;