Javascript array with multiple objects - send to GTM - Google data layer - javascript

I have multiple products on a page each with several infos and i need to send them to google data layer on page load within an array of objects.
My script the way it is is sending one array with a object with all fields on it, what i need is to have that same array with an object per product. Here's my code and hope someone can help me. Thanks
$(function () {
var Container = (".product");
var itemName = $($(Container)).find(".item-name").text();
var itemId = $($(Container)).find(".item-id").text();
var itemPrice = $($(Container)).find(".item-price").text();
var itemBrand = $($(Container)).find(".item-brand").text();
var itemCategory = $($(Container)).find(".item-category").text();
window.dataLayer.push({
event: 'Ecommerce - Item List Views',
event_name: 'view_item_list',
view_item_list: {
items: [{
item_name: itemName,
item_id: itemId,
price: itemPrice,
item_brand: itemBrand,
item_category: itemCategory,
}]
}
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="product">
<div class="item-name">
<h1>Nike blue shoes</h1>
</div>
<div class="item-id">
SKU-3456
</div>
<div class="item-price">
€22.00
</div>
<div class="item-brand">
Nike
</div>
<div class="item-category">
Shoes
</div>
</div>
<div class="product">
<div class="item-name">
<h1>Adidas red shoes</h1>
</div>
<div class="item-id">
SKU-4335
</div>
<div class="item-price">
€55.00
</div>
<div class="item-brand">
Adidas
</div>
<div class="item-category">
Shoes
</div>
</div>
<div class="product">
<div class="item-name">
<h1>Nike Yellow Sandals</h1>
</div>
<div class="item-id">
SKU-9654
</div>
<div class="item-price">
€11.00
</div>
<div class="item-brand">
Nike
</div>
<div class="item-category">
Sandals
</div>
</div>
<div class="product">
<div class="item-name">
<h1>Vans Black Sneakers</h1>
</div>
<div class="item-id">
SKU-362364
</div>
<div class="item-price">
€99.00
</div>
<div class="item-brand">
Vans
</div>
<div class="item-category">
Sneakers
</div>
</div>
</div>

Instead of pushing right away, try putting the object into a variable first:
const dlObject = {
event: 'Ecommerce - Item List Views',
event_name: 'view_item_list',
view_item_list: {
items: [{
item_name: itemName,
item_id: itemId,
price: itemPrice,
item_brand: itemBrand,
item_category: itemCategory,
}]
}
}
now you can enrich it by adding more stuff into it:
dlObject.view_item_list.items.push({
item_name:'second item',
item_id:'qweqwe',price:"15",
item_brand:"foo",
item_category:"bar"
});
finally, once you're done pushing products to the items array, you push the whole thing in:
window.dataLayer.push(dlObject);

Related

Filter html elements based on data attribute

I have the following html structure
<div id="container">
<div id="child_1" data-customId="100">
</div>
<div id="child_2" data-customId="100">
</div>
<div id="child_3" data-customId="100">
</div>
<div id="child_4" data-customId="20">
</div>
<div id="child_5" data-customId="323">
</div>
<div id="child_6" data-customId="14">
</div>
</div>
And what I want to do is to get the count of child divs that contains different data attribute. For example, I'm trying this:
$(`div[id*="child_"]`).length); // => 6
But that code is returning 6 and what I want to retrieve is 4, based on the different data-customId. So my question is, how can I add a filter/map to that selector that I already have but taking into consideration that is a data-attribute.
I was trying to do something like this:
var divs = $(`div[id*="child_"]`);
var count = divs.map(div => div.data-customId).length;
After you getting the child-divs map their customid and just get the length of unique values:
let divs = document.querySelectorAll(`div[id*="child_"]`);
let idCustoms = [...divs].map(div=>div.dataset.customid);
//idCustoms: ["100", "100", "100", "20", "323", "14"]
//get unique values with Set
console.log([... new Set(idCustoms)].length);//4
//or with filter
console.log(idCustoms.filter((item, i, ar) => ar.indexOf(item) === i).length);//4
<div id="container">
<div id="child_1" data-customId="100">
</div>
<div id="child_2" data-customId="100">
</div>
<div id="child_3" data-customId="100">
</div>
<div id="child_4" data-customId="20">
</div>
<div id="child_5" data-customId="323">
</div>
<div id="child_6" data-customId="14">
</div>
</div>
Note: $ is equivalent to document.querySelectorAll in js returns a NodeList that's why I destructure it by the three dots ...
You'll have to extract the attribute value from each, then count up the number of uniques.
const { size } = new Set(
$('[data-customId]').map((_, elm) => elm.dataset.customid)
);
console.log(size);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="container">
<div id="child_1" data-customId="100">
</div>
<div id="child_2" data-customId="100">
</div>
<div id="child_3" data-customId="100">
</div>
<div id="child_4" data-customId="20">
</div>
<div id="child_5" data-customId="323">
</div>
<div id="child_6" data-customId="14">
</div>
</div>
No need for jQuery for something this trivial, though.
const { size } = new Set(
[...document.querySelectorAll('[data-customId]')].map(elm => elm.dataset.customid)
);
console.log(size);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="container">
<div id="child_1" data-customId="100">
</div>
<div id="child_2" data-customId="100">
</div>
<div id="child_3" data-customId="100">
</div>
<div id="child_4" data-customId="20">
</div>
<div id="child_5" data-customId="323">
</div>
<div id="child_6" data-customId="14">
</div>
</div>
Note that the property customid is lower-cased in the JavaScript. This could be an easy point of confusion. You might consider changing your HTML from
data-customId="14"
to
data-custom-id="14"
so that you can use customId in the JS (to follow the common conventions).

Binding issue in repeat.for in aurelia

I have a list of cards that i populate, when i click on each card i want to get and display the correct items displayed for that card.
The problem i am facing is that when i return an array its not binding the correct item to the specific card.
This is my HTML
<div repeat.for="Grouping of categoryItems">
<div class="row">
<div class="col s12 m3 l3">
<div class="card blue-grey darken-1">
<div class="card-content" style="padding:10px">
<span class="card-title white-text truncate">${Grouping.name}</span>
<a if.bind="Grouping.hideDetails" class="btn-floating halfway-fab waves-effect waves-light" click.delegate="Activate(list, Grouping)"><i class="material-icons">add</i></a>
</div>
</div>
</div>
</div>
<div repeat.for="categoryGroupingTypes of categoryItemTypes">
<div class="row" if.bind="!Grouping.hideDetails">
<div class="col" style="position:absolute;padding:5%">
<div class="rotate-text-90-negative">
<span class="blue-grey-text"><b>${categoryGroupingTypes.name}</b></span>
</div>
</div>
<div repeat.for="item of categoryItemsTypes.items" class="col s12 m3 l3 ">
<div class="card-content">
<span class="card-title white-text truncate">${items.Name} ${items.Quantity}</span>
</div>
</div>
</div>
</div>
</div>
</div>
my ts
async Activate(list: ListModel[], grouping: any) {
this.categoryItemsTypes = await this.getTypes(list);
let result = this.categoryItem.filter(x => x.name == grouping.name)
if (result) {
grouping.hideDetails = false;
}
}
so this.categoryItemsTypes has the following array
0: {name: "Clothes", items: Array(3)}
1: {name: "Shoes", items: Array(2)}
so when the page loads it loads the cards as follows
then when i click on "Clothes"
i only want it to load the array associated with clothes and if "shoes" is clicked then only load that array as follows
but what is currently happening with my above code is the following
this line is where i bind the items
repeat.for="item of categoryItemsTypes.items"
how can i bind the items to the correct ${Grouping.name} as shown in picture 2?
You are in the right direction, but not complete yet.
Array assignment are not observed by aurelia.
In general, when populating arrays with a new set of elements, this is a good way:
destarray.splice(0, destarray.length, ...sourcearray);
over
destarray = sourcearray;
because the former is observed by aurelia by default and the latter is not.

dynamically add classes to content coming from *ngFor from an array

Im not quite sure something like this is possible but say in my html component I have an ngFor like so..
<div *ngFor="let card of cards">
... stuff in here
</div>
now say I have an array of classNames like so
classNames = [
'red',
'yellow',
'blue',
'green'
]
and inside my *ngFor I have a div like so
<div *ngFor="let card of cards">
<div [class]='...'>
<div class="card">
</div>
</div>
</div>
basically what I want to happen is for every item in the ngFor give loop through the classNames array and dynamically add it to the incoming data so for example
say I have 6 items in cards so each card needs a classname so it loops through classNames and gives it a class so like this..
<div [class]='red'>
<div class="card">
</div>
</div>
<div [class]='yellow'>
<div class="card">
</div>
</div>
<div [class]='blue'>
<div class="card">
</div>
</div>
<div [class]='green'>
<div class="card">
</div>
</div>
<div [class]='red'>
<div class="card">
</div>
</div>
<div [class]='yellow'>
<div class="card">
</div>
</div>
and so on and so forth..
how could i accomplish something like this?
EDIT
component.html
<div class="card" *ngFor="let card of cards; let i = index">
<div [class]="classNames[i%classNames.length]">
....
</div>
</div>
component.ts
export class...
classNames = [
'light-green',
'dark-green',
'aqua',
'blue',
'blue-purple',
'purple',
'purple-pink',
'purple-orange'
];
You can leverage remainder (%) operator to achieve that:
<div *ngFor="let card of cards; let i = index">
<div [class]="classNames[i%classNames.length]">
<div class="card">
{{ card }}
</div>
</div>
</div>
Ng-run Example
Update:
You should define array as follows:
classNames = [
'light-green',
'dark-green',
'aqua',
'blue',
'blue-purple',
'purple',
'purple-pink',
'purple-orange'
];
Note: i use = instead of :
Instead of randomly applying any class to any card or deciding it on view based on some %, the best way, I believe would be read it from the Cards object itself, since it is logical to have all details of a card read from the card itself.
So that view is independent of those extra stuffs.
classNames = ['red','yellow','blue','green'];
cards = [{text: 1, class: this.classNames[0]},{text: 2, class: this.classNames[1]}];
your view should simply do its task (render)
<div *ngFor="let card of cards; let i = index">
<div [class]="card.class">
<div class="card">
{{ card.text}}
</div>
</div>
</div>

Knockout.js : ObservableArray with ObservableArrays inside?

I'm using this plugin called Dragula which needs an ObservableArray as a source for the data..
HTML/Knockout-bindings
<div class="widget-container">
<div class="widget-content visible" id="team-setup">
<div class="header">
<p>Lagoppsett</p>
<p></p>
</div>
<div class="col-sm-12">
<div class="row">
<div class="widget-container">
<div class="col-xs-6 widget-content visible">
<div class="header">
<p>Tilgjengelige spillere</p>
<p></p>
</div>
<div class="player-card-container-mini" data-bind="dragula: { data: availablePlayers, group: 'playerz' } ">
<div class="player-card-mini">
<div class="player-card-left">
<div class="player-avatar" style="margin-left: 85%;">
<img src="Content/Images/player-female.png" id="imgAvatar" runat="server" />
<div class="player-shirt-no" data-bind="text: ShirtNo"></div>
</div>
</div>
<div class="player-card-subtext">
<div class="player-text">
<div class="player-card-header-small" data-bind="text: PlayerName"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-xs-6 widget-content visible">
<div class="header">
<p>Lag</p>
<p></p>
</div>
<div data-bind="foreach: teamsetup">
<div data-bind="foreach: SubTeams">
<h1 data-bind="text: TeamSubName"></h1>
<div class="player-card-container-mini" data-bind="dragula: { data: Players, group: 'playerz' } " style="border: 1px solid red; min-height:200px">
<div class="player-card-mini">
<div class="player-card-left">
<div class="player-avatar" style="margin-left: 85%;">
<img src="Content/Images/player-female.png" id="img1" runat="server" />
<div class="player-shirt-no" data-bind="text: ShirtNo"></div>
</div>
</div>
<div class="player-card-subtext">
<div class="player-text">
<div class="player-card-header-small" data-bind="text: PlayerName"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div style="clear:both"> </div>
</div>
</div>
Knockout code :
var TeamSetupViewModel = function () {
var self = this;
self.teamsetup = ko.observableArray();
self.availablePlayers = ko.observableArray();
self.testPlayers = ko.observableArray();
}
var model = new TeamSetupViewModel();
ko.applyBindings(model, document.getElementById("team-setup"));
var uri = 'api/MainPage/GetTeamSetup/' + getQueryVariable("teamId");
$.get(uri,
function (data) {
model.teamsetup(data);
model.availablePlayers(data.AvailablePlayers);
model.testPlayers(data.AvailablePlayers);
console.log(data);
}, 'json');
});
The problem is... that i'm having a ObservableArray at the top node, and i do need ObservableArrays further down in the hierarchy.
model.availablePlayers works fine, but when accessing the other players in the html/ko foreach loops through teamsetup -> SubTeams -> Players it doesn't work due to Players isn't an ObservableArray. (There might be everyting from 1 to 7 SubTeams with players).
So how can i make the Players in each SubTeams an ObservableArray ?
See the image for the datastructure :
You could use Mapping plugin, but if players is the only thing you need, you can do it manually:
Simplify your view model:
var TeamSetupViewModel = function () {
var self = this;
self.availablePlayers = ko.observableArray();
self.subTeams = ko.observableArray();
}
After you get the data from the server, populate the view model converting the array of players on every team to an observable array of players:
$.get(uri,
function (data) {
model.availablePlayers(data.AvailablePlayers);
model.subTeams(data.SubTeams.map(function(t) { t.Players = ko.observableArray(t.Players); return t; }));
}, 'json');
});
Finally, remove the following line in your template (with its closing tag) - nothing to iterate over anymore:
<div data-bind="foreach: teamsetup">
... and update the name of the property in the next line, so it is camel case like in the VM:
<div data-bind="foreach: subTeams">

angularJS Custom directive functionality works using ng-click but not using ng-mouseOver

Requirement goes like this :- I have left navigation panel which has to be in sync with the items added in the main active view by the user and has to display in tree structure. Basic idea is to provide context aware sub-view that change based on active view.
Custom directive used to display tree structure: https://github.com/nickperkinslondon/angular-bootstrap-nav-tree/blob/master/src/abn_tree_directive.js
my HTML code: (using ng-click)
<div class="add-data-request-panel" style="min-height:1071px;"
ng-click="expandPanel()">
<ul>
<li class="icon-drd icon-drd-diactive" ng-if="panelCollapse" ></li>
<li class="icon-pie-chart icon-pie-active" ng-if="panelCollapse"></li>
<li class="icon-publish-req" ng-if="panelCollapse"></li>
<li class="icon-view-changes" ng-if="panelCollapse"></li>
</ul>
</div>
<div class="data-slider-panel" style="min-height:1071px;display" ng-if="panelExpand">
<div class="data-slider-row mtop" ng-click="collapsePanel()">
<div class="slider-row-left">
<span class="first-char" >S</span>
<span class="second-char">ection</span>
</div>
<div class="slider-row-right">
<div class="icon-drd icon-drd-diactive">
</div>
</div>
</div>
<div class="data-slider-row">
<div class="slider-row-left">
Section2
<div class="sub-slider-row-left">
<abn-tree tree-data="mainArrayObj"></abn-tree> // passing data to tree directive
</div>
</div>
<div class="slider-row-right">
<div class="icon-pie-chart icon-pie-active">
</div>
</div>
</div>
<div class="data-slider-row" ng-click="collapsePanel()">
<div class="slider-row-left">
Section3
</div>
<div class="slider-row-right">
<div class="icon-publish-req">
</div>
</div>
</div>
<div class="data-slider-row" ng-click="collapsePanel()">
<div class="slider-row-left">
Section4
</div>
<div class="slider-row-right">
<div class="icon-view-changes">
</div>
</div>
</div>
</div>
JS implementation in my controller
$scope.panelExpand = false; //setting default flag
$scope.panelCollapse = true; //setting default flag
$scope.expandPanel = function() {
$scope.panelExpand = true;
$scope.panelCollapse = false;
$scope.mainArrayObj = []; // array that holds the data passed in html to custom directive
initialJsonSeparator($scope.model.Data); // method used for iteration
};
$scope.collapsePanel = function() {
$scope.panelExpand = false;
$scope.panelCollapse = true;
};
my HTML code: (using ng-mouseover which is not working and displaying the data passed to navigation bar)
<div class="add-data-request-panel" style="min-height:1071px;" ng-mouseover="hoverIn()"
ng-mouseleave="hoverOut()">
<ul>
<li class="icon-drd icon-drd-diactive"></li>
<li class="icon-pie-chart icon-pie-active"></li>
<li class="icon-publish-req"></li>
<li class="icon-view-changes"></li>
</ul>
</div>
<div class="data-slider-panel" style="min-height:1071px;display"
ng-mouseover="hoverIn()" ng-mouseleave="hoverOut()" ng-show="hoverEdit">
<div class="data-slider-row mtop">
<div class="slider-row-left">
<span class="first-char">S</span>
<span class="second-char">ection1</span>
</div>
<div class="slider-row-right">
<div class="icon-drd icon-drd-diactive">
</div>
</div>
</div>
<div class="data-slider-row">
<div class="slider-row-left">
Section2
<div class="sub-slider-row-left">
<abn-tree tree-data="mainArrayObj"></abn-tree> // array that holds the data passed in html to custom directive
</div>
</div>
<div class="slider-row-right">
<div class="icon-pie-chart icon-pie-active">
</div>
</div>
</div>
<div class="data-slider-row">
<div class="slider-row-left">
Section3
</div>
<div class="slider-row-right">
<div class="icon-publish-req">
</div>
</div>
</div>
<div class="data-slider-row">
<div class="slider-row-left">
Section4
</div>
<div class="slider-row-right">
<div class="icon-view-changes">
</div>
</div>
</div>
</div>
js Implementation for the ng-mouseOver: (while debugging all the iteration and methods executed as expected)
$scope.hoverIn = function() {
this.hoverEdit = true;
$scope.mainArrayObj = []; // array that holds the data passed in html to custom directive
initialJsonSeparator($scope.model.Data); //method used to iterate the data
};
$scope.hoverOut = function() {
this.hoverEdit = false;
};
Any solution to this issue would be of gr8 help. If there is any other better approach other than the ng-mouseOver and ng-mouseLeave to implement hover, please do let me know.

Categories