We have Adobe Analytics set up on our site, and we have a handful of share buttons. I've set up a direct call event to track a custom "Share" event whenever one is clicked, and to record the type of share (Facebook, Twitter, etc) in a prop so that we can categorize the shares and compare them
This works fine when the events come in with plenty of time between them, but when I try doing multiple shares back to back, only the last one is incrementing its property (Example: If I do a Twitter share, a Facebook share, and then a Pinterest share, I get 3 share events, but only Pinterest gets tracked as a share type instance)
Is there a way to keep these prop updates from getting conflated? Or is there a better way of subcategorizing a particular event?
sendEvent function:
sendEvent: function(analyticsEvent) {
if(analyticsEvent.condition){
if(analyticsEvent.props){
for (var prop in analyticsEvent.props){
if (analyticsEvent.props.hasOwnProperty(prop)) {
_satellite.setVar(prop,analyticsEvent.props[prop]);
};
}
}
_satellite.track(analyticsEvent.condition);
}
}
Object I'm passing:
{
condition: 'share',
props: {shareType:'Twitter'}
}
DTM code:
var _shareType = _satellite.getVar('shareType');
s.prop123 = _shareType;
I think I may have solved the issue. On the DTM side I'd only referenced the prop I was modifying in the custom code, not in the props section of the rule. I added prop123 = "undefined" there, and it looks like that fixed things somehow.
Related
I am setting up InstantSearch icw Algolia for products of a webshop with the plain JavaScript implementation.
I am able to follow all the documentation, but I am running into the problem that we have prices specific to customer groups, and things like live stock information (need to do another API call for that).
These attributes I would like to ideally load after getting search results, from our own back-end.
I thought it would simply be a matter of manipulating the search results after receiving them and re-rendering only the front-end (without calling the Algolia search API again for new results).
This is a bit tricky. The transformItems functionality is possible, but I want to already display the results and load the other data into the hit templates after, not before displaying the hit results.
So I end up with a custom widget, and I can access and manipulate the results there, but here the problem is that I don’t know how to reflect these changes into the rendered templates.
The code of my widget (trying to set each stock number to 9) is as follows:
{
render: function(data) {
const hits = data.results.hits;
hits.forEach(hit => {
hit.stock = 9
});
}
}
The data is changed, but the generated html from the templates does not reflect any changes to the hit objects.
So how could I trigger a re-render after altering the hits data, without triggering a new search query?
I could not find a function to do this anywhere in the documentation.
Thanks!
There is the transformItems function in the hits widget that allows you to transform, remove or reorder items as you wish.
It is called before items displaying.
If I use your example, it would be something like this :
transformItems(items) {
return items.map(item => ({
...item,
stock: 9,
}));
}
Of course you can add you API call somewhere in that.
The documentation for the vanilla JS library :
https://www.algolia.com/doc/api-reference/widgets/hits/js/#widget-param-transformitems
So basically I am playing with Svelte trying to spin up a quick app, details of the app aren't important but basically it hosts a bunch of embedded sites. see example here & for replicability:
https://svelte.dev/repl/6f3484554ef8489b9a5960487a0a1f95?version=3.47.0
My problem is that when I add a new url & title to the sites list, the {#each} block that creates the embedded views doesn't update to reflect the new state of the list, even though the list is clearly updating in the console output. Is it something to do with scope or is it a Svelte issue of not triggering reactivity on prop reassignments from components?
Update: some sites don't allow embedding so use https://wikipedia.org as a safe one for testing.
if you replace a hard-coded url in the sites list with wiki address it should work fine. i basically want a new window to pop up as the {#each} block creates a new SiteView component
There are several things wrong with your code, the first being that you do not propagate the changes made to the sites array back to the main application, you should use bind: to keep the two arrays in sync.
<InputBar bind:sites {site} />
The second is that you are modifying an object when adding a new site and then adding that object to the array, this will always be the same object so if you change it the previously added sites will also change. You can solve this by spreading the new object into the array instead:
function add() { sites = sites.concat({...site}); console.log(sites)}
// or alternatively
function add() { sites = [...sites, {...site}]; console.log(sites); }
That said, the application is not very "Svelte" like as it mixes responsibilities and exposes data to components that don't need that data. For example, why would the input bar need to know about the current sites ? It would be a lot better to have the input bar be just that, an input bar. When the user clicks 'add' it raises an event that says 'something has been added' and resets the fields. Then the parent is responsible to add it to the array. This will make for a more flexible solution. If you do that you will see there is also no reason to have a 'site' variable on the top level (or even have that object at all, you can just have two fields)
<script>
import { createEventDispatcher } from 'svelte'
let url = ''
let title = ''
const dispatch = createEventDispatcher()
function add() {
dispatch('add', { url, title })
url = ''
title = ''
}
</script>
<div class="rounded">
<p>Enter a site to stream:</p>
<input type="text" placeholder="www.example.com" bind:value={url}>
<br>
<input type="text" placeholder="example" bind:value={title}>
<button on:click={add}>add</button>
</div>
<InputBar on:add={(ev) => sites = [...sites, ev.detail]} />
On a final note, to add things to the head of the html use <svelte:head> instead.
If you want to change a value from another component you need to bind the property, otherwise the relationship is one-way only (from parent component to child).
<InputBar bind:sites {site}/>
I’m using JavaScript events on an embedded Looker report and attempting to update the filters on it. It seems reasonable that the dashboard:filters:update method would allow me to update a dashboard with new filters but the documentation explicitly mentions that this cannot be done, though it doesn’t not mention how I actually can add new filters to a dashboard.
Do I need to run the dashboard:load event with additional_filters passed as part of the payload object? Or how can I add filters to a dashboard that doesn’t already have any filters applied? Are there any examples of this?
My current code (triggered on button click) looks like this but the filters are not applied:
const handleButtonClick = () => {
const lookerIframe = document?.querySelector('iframe')?.contentWindow;
const eventPayload = {
type: "dashboard:load",
id: "looker_dashboard_id",
dashboard_filters: {
"Company Name": "XYZ Corp"
},
};
lookerIframe.postMessage(
JSON.stringify(eventPayload),
lookerDashboardUrl,
);
}
Is there a different event I should be triggering or a different property I can pass?
In this situation, the key here is the definition of "add"ing a filter. For my purposes, the Company filter already existed on the dashboard it just hadn't been applied in the UI with any value. Even though it had no value, this didn't mean I had to "add" it, running this event was still valid because it was simply an update.
The documentation means that if you need to add an entirely new filter to the dashboard then you'll need to do this through the Looker dashboard configuration.
It may be a very dumb question... I am using Meteor-ui-progress-circle and I want redrawing the template when the percentage (wich is store in a reactive collection Progress) is changed (currently, when I click on a "play" button).
I think I have to use Blaze.render but I don't really understand how it work.
Here a part of my main template (in Jade) :
div.panel-body
div.col-md-9.col-sm-8
p Lorem ipsum...
div.col-md-3.col-sm-4#progress-circle
+progressCircle progress="0" radius="100" class="green"
And my JavaScript :
Template.controlBar.events(
{
"click .play-button": function ()
{
var tmp = Progress.findOne({});
if (!tmp)
{
Meteor.call('createProgress');
tmp = Progress.findOne({});
}
var val = tmp.progressValue;
val += 10;
if (val > 100)
return;
Meteor.call('updateProgess', tmp._id, val);
Template.progressCircle.progress = tmp.progressValue;
Blaze.render(Template.progressCircle, $("#progress-circle")[0]);
},
Doing this... I have several template that are displaying each time I click on the play button. I don't understand how to specify that I don't want a new template but just re-render the one I already have.
Not sure I quite understand your question, but I'll try to help by giving my best understanding of templating and how I have come to use them. If someone sees any incorrect information here, please speak up so I can get a better understanding myself and correct this answer.
First, the Template.XXX.events handlers. In your event handler, you are using a function with no arguments. You can actually accept 2 arguments for these event handler functions: the event and the template. So, you can do something like thus:
Template.controlBar.events({
'click .play_button': function(event, tmpl) {
tmpl.$('div#progress-circle').doSomething();
}
});
Notice the tmpl.$() call? That says to use jQuery to find the specified selector, but ONLY in the current template. This is a wonderful way to use classes to generalize your components, but then be able to filter the selection to only those within the same template...
...Which brings me to my next bit of advice: Use child templates excessively. Any component that I can identify as an "autonomous component" on my page I will consider as a separate template. For instance, I was recently working on a custom reporting page that had a table and some D3 graphs representing some real-time data. In this report page, I had one main template defined for the "page", then each of the D3 graphs where defined as a separate template, and the table was another separate template. This allows several advantages:
Compartmentalization of the "components" of the page, allowing code reuse (I can now put the same graph on ANY page, since it's now an autonomous "component"
The advantage of using the Template.XXX.events trick above to "narrow" the scope of my element searches to elements within that template
Prevents total page refreshes as Meteor is smart enough to only refresh templates that need to be refreshed, which also speeds the responsiveness of the page itself
As a result, I try to apply my Templates liberally. In your case, it would sound to me that if I were to have multiply progress bars on the page that I might turn those into separate templates. I might even do it if I had a single progress bar if it made sense to separate it out for ease of data handling.
Finally, inter-communications between Templates. This can be tricky at times, but the best, most efficient way to do this I have found is through Session variables. The pattern I typically use is to have my data for my template be returned by a Template .helper, which does something like this:
Template.controlBar.helpers({
progressData: function() {
if (!Session.equals('playId', null)) {
return Progress.findOne({_play_id: Session.get('playId')});
}
}
});
Because Helpers are reactive, and Sessions is reactive, the template is re-rendered anytime the 'playId' is altered in the Session. The corresponding Session variable can be set from anywhere in the client code. Again, this tends to work best when you narrow the scope of your templates to the individual components. It is important to note here that the Session object in Meteor is NOT the same as "sessions" in other languages like Java and such, which typically use cookies and a session token/id. Meteor sessions work considerably different, and do not survive page reloads or closing of browsers.
I have implemented an entry in SettingsFlyout control for settings pane. The page itself contains a dropdown. Whatever option user select from this dropdown need to be stored in roaming data store. Obviously this stored data need to be retrieved whenever user gets to this page in settings pane. I am not sure what’s the best place to write this code for data stage and retrieval? I see that SettingsFlyout object has onafterhide, onaftershow, onbeforehide and onbeforeshow events. Should any of these be used for this purpose?
[Windows.Storage.ApplicationData.Current.localSettings] (http://msdn.microsoft.com/en-us/library/windows/apps/windows.storage.applicationdata.aspx) or roamingSettings provide the builtin support for getting/setting setting key-value pair. It also handles the persisting to a file in application data folder. It also does required batching as per documentation.
you can find the reference code in the application data sample
var roamingSettings = Windows.Storage.ApplicationData.current.roamingSettings;
function settingsWriteSetting() {
roamingSettings.values['my setting'] = 'my setting value';
}
regards, events on the flyout - there events can be used to take some action before/after flyout is hidden - in the overall user flow. For example - I have once created a Promise around a signin flyout. afterhide was used to call error callback for the promise, with error as canceled.
Settings changed in a settings flyout should take effect as soon as the user makes the change rather than waiting until the flyout is hidden. I'd suggest treating your flyout as a page control.
Assuming your settings flyout was defined in settings/mySettings.html, create a JavaScript file named settings/mySettings.js and reference it in the head of your settings page. Then add the following code into the script file.
(function () {
"use strict";
var page = WinJS.UI.Pages.define("/settings/mySettings.html", {
ready: function (element, options) {
// wire up event handlers for saving changes
// setup initial state
},
});
})();
Just like any other page control, you add event handlers and initialize the page in the ready function. If you are familiar with the navigation app template, it is the same.