window.dataLayer push more key value in the same event - reactjs

In a ReactJS app, in the root.tsx, push an event "website_pageview" with pageInfo
window.dataLayer.push({
event: "website_pageview",
pageInfo: {
path: window.location,
title: window.document.title,
}
});
In another route file blog.tsx which contains tags and authors, how can I push more key-value to the existing pageInfo in the same event: "website_pageview"?
I tried the following
window.dataLayer[0].push({
pageInfo: {
tags: {categoryList},
author: {authorList}
}
});
But it will return 2 entries as following when console.log `window.dataLayer``
(2)[
{
pageInfo: {
tags: "Tag1, Tag2",
authors: "Author1, Author2",
}
},
{
event: "website_pageview",
pageInfo: {
path: "https://google.com",
title: "Page title",
}
}
]
What I want to achieve is the following. In GTM, based on the event: "website_pageview" it will trigger a pageview with all the needed info - path, title, tags, authors.
(1)[
{
event: "website_pageview",
pageInfo: {
path: "https://google.com",
title: "Page title",
tags: "Tag1, Tag2",
authors: "Author1, Author2",
}
}
]
Thank you in advance.

I think it's okay that it's not in the same dataLayer array. Because in Google Tag Manager, it will combine it automatically in the Datalayer Variable
Here is the test result from you dataLayer :
I paste the dataLayer code in the console.
Create the Datalayer Variable in Google Tag Manager
We can see that the test-Pageinfo variable already merge all the parameters!

Related

Pushing an array of objects into Firebase Collection Angular 8

I am trying to add a document into an array studyList in my users collection.
So i have a collection users where i have name, etc.. and studyList.
When i click on a button buy into a DocumentItemComponent i want to add that document into this studyList array.
My code works partially because it adds the document into the array but when i click on another document it changes the first one, it doesn't add another document.
This is my code for the adding function:
addToStudyList(user) {
const userRef: AngularFirestoreDocument<any> = this.afs.doc(`users/${user.id}`);
const data: UserInterface = {
studyList: [{
title: this.document.title,
language: this.document.language,
description: this.document.description,
cover: this.document.cover,
category: this.document.category,
field: this.document.field,
id: this.document.id,
author: this.document.userUid,
urlDocument: this.document.urlDocument
}]
}
return userRef.set(data, {merge: true});
}
Can you help me, please?
Thank you! Have a good day!
There is no direct way to update an array inside a document, but if you are using Firestore, it provides arrayUnion and arrayRemove functions which you can use for adding/removing unique items in the array.
From firestore documentation https://firebase.google.com/docs/firestore/manage-data/add-data#update_elements_in_an_array :
Try this:
userRef.update({
studyList: firebase.firestore.FieldValue.arrayUnion(data)
});
This is because when you declare:
studyList: [{
title: this.document.title,
language: this.document.language,
description: this.document.description,
cover: this.document.cover,
category: this.document.category,
field: this.document.field,
id: this.document.id,
author: this.document.userUid,
urlDocument: this.document.urlDocument
}]
in this piece of code you are assigning just one object to the the studyList array which overwrites the existing array, instead you should utilize the existing user studyList array and push your new object into it, something like this:
addToStudyList(user) {
const userRef: AngularFirestoreDocument<any> = this.afs.doc(`users/${user.id}`);
user.studyList.push({
title: this.document.title,
language: this.document.language,
description: this.document.description,
cover: this.document.cover,
category: this.document.category,
field: this.document.field,
id: this.document.id,
author: this.document.userUid,
urlDocument: this.document.urlDocument
});
const data: UserInterface = {
studyList: user.studyList
}
return userRef.update(data);
}

Is there a way to open new tab in navigation bar for custom lightning component with record name as tab label?

I need to open the new navigation tab in navigation bar in Lightning Experience in Non Console App environment.Tab should have record name pre-populated as label.
Tried following approach:
Created custom tab for target lightning component
In Source Component:
Created Page Reference with type as standard__navItemPage.
for attributes specified custom tab name for target component.
Using navigation service redirected the control to new URL.
In Target Component:
Using interface isUrlAddressable to retrieve the page param.
var pageReference = {
type: 'standard__navItemPage',
attributes: {
apiName: 'Product_Overview',
},
state: {
c__productId: itemId,
c__isfavourite : isfavourite,
c__isSourceSearchResultCmp : false
}
};
var navService = component.find("navService");
navService.generateUrl(pageReference)
.then($A.getCallback(function(url) {
console.log('Using Navigate'+url);
navService.navigate(pageReference);
}), $A.getCallback(function(error) {
console.log(error);
}));
The issue is , the navigation tab which is getting open is not having details like record name and I could not find any API or methods the same.
Any guidance here would be appreciated.
var pageReference = {
type: 'standard__navItemPage',
attributes: {
apiName: 'Product_Overview',
},
state: {
c__productId: itemId,
c__isfavourite : isfavourite,
c__isSourceSearchResultCmp : false
}};
var navService = component.find("navService");
navService.generateUrl(pageReference).then($A.getCallback(function(url) {
console.log('Using Navigate'+url);
//---add this line which allows you to open url in new tab instead of navService
window.open('https:'+url,
'_blank' // <- This is what makes it open in a new window.
);
}),$A.getCallback(function(error) {
console.log(error);
}));
navigateToRecord(event) {
this[NavigationMixin.GenerateUrl]({
type: 'standard__recordPage',
attributes: {
recordId: event.target.name,
objectApiName: 'Product2',
actionName: 'view',
},
}).then((url) => {
window.open(url);
});
}
If you encounter any issue, please let me know.

Proper redux store shape

What is the recommended way to handle this sort of store in redux? I can see a couple different options but am looking to evaluate the tradeoffs of each and see what is the more idiomatic approach.
These are the base models, think of a blog:
Post
Author
Comment
Let's say I have these pages:
View a paginated list of posts: /posts
View one post: /posts/:id
View author profile: /authors/:id
When I go to Page 1 I will fetch a list of posts from the server, ultimately, when I get this data from the server, and the reducers handle it, the store will look like this:
{
posts: {
byId: {
'post-id-1': {
title: 'Post 1', authorId: 'author-id-1'
},
'post-id-2': {
title: 'Post 2', authorId: 'author-id-2'
},
allIds: ['post-id-1', 'post-id-2']
},
authors: {
byId: {
'author-id-1': {
name: 'Author 1'
},
'author-id-2': {
name: 'Author 2'
},
allIds: ['author-id-1', 'author-id-2']
}
}
Now I can render the post title with the author's name. Now when a user clicks on a specific post they are going to go to Page 2 (/posts/:id). When they do this, how should this be handled:
Option 1 Reset the store posts when they leave the page, so the store "posts" and "authors" will be empty, and when Page 2 componentDidMount() fires then make another network request to download the post content + the author details for only that one post. If so, now the store will look like:
// when the user navigates away from `/posts`
{
posts: { byId: {}, allIds: []}
authors: { byId: {}, allIds: []}
}
// when the user loads `/posts/:id`
{
posts: {
byId: {
'post-id-1': {
title: 'Post 1', authorId: 'author-id-1'
},
allIds: ['post-id-1']
},
authors: {
byId: {
'author-id-1': {
name: 'Author 1'
}
allIds: ['author-id-1']
}
}
Option 2 Alternatively, should I keep the data in the store that I already have and do something like this:
// when componentDidLoad() for `/posts/1`
componentDidMount () {
// check the `store` to see if we already have this post loaded
// if we do, then do nothing
// if we do not, then fetch it from the server
if (this.props.getPostForId(1)) {
} else {
this.props.fetchPost({postId: 1})
}
}
Next, if the user clicks into an author's profile page /authors/1, and I want to fetch the posts that author has written. How does this make my store look? Following Option 1, I can clear out the posts store again before the user navigates, and then on the author's page I can fetch all the posts that author wrote so I can render them on the author's page. But if I do not clear out the full list of posts, and now I fetch the posts that only this author wrote (post 3, post 4, post 5, post 6, etc.), so I keep the posts I already have in the store and merge the results?
{
posts: {
'post-id-1': {
title: 'Post 1', authorId: 'author-id-1'
},
'post-id-2': {
title: 'Post 2', authorId: 'author-id-2'
},
'post-id-3': {
title: 'Post 3', authorId: 'author-id-1'
},
'post-id-4': {
title: 'Post 4', authorId: 'author-id-1'
},
},
authors: {
byId: {
'author-id-1': {
name: 'Author 1'
},
'author-id-2': {
name: 'Author 2'
},
allIds: ['author-id-1', 'author-id-2']
}
}
My main confusion is around how data in the store is shared between pages (components).
When loading one page, how do I know if I already have the data in the store that I need, or if I need to make a request to get it?
When do I clear out old data from the store? Do I ever do this? Should every component be responsible for both dispatching an action to load the data that it needs on mount? And also dispatching an action to remove that data on un-mount?

CKEDITOR Image upload - fileUploadResponse event is not firing

I am trying to integrate CKEditor in Angular App. In CKEditor, I am trying to use uploadimage. In run method of my app I have written following code to listen the events of CKEditor.
CKEDITOR.on( 'instanceCreated', function( event ) {
console.log("CKEditor instance created");
});
CKEDITOR.on( 'fileUploadResponse', function( evt ) {
// Prevent the default response handler.
console.log("Image Uploaded");
evt.stop();
// Ger XHR and response.
var data = evt.data,
xhr = data.fileLoader.xhr,
response = xhr.responseText.split( '|' );
if ( response[ 1 ] ) {
// Error occurred during upload.
data.message = response[ 1 ];
evt.cancel();
} else {
data.url = response[ 0 ];
}
console.log("Image Uploaded");
} );
In console it is printing CKEditor instance created, but not printing Image Uploaded. Somehow it is not listening to fileUploadResponse event.
My config file of CKEditor is as follow:
CKEDITOR.editorConfig = function( config ) {
// Define changes to default configuration here.
// For complete reference see:
// http://docs.ckeditor.com/#!/api/CKEDITOR.config
// The toolbar groups arrangement, optimized for two toolbar rows.
config.toolbarGroups = [
{ name: 'clipboard', groups: [ 'clipboard', 'undo' ] },
{ name: 'editing', groups: [ 'find', 'selection', 'spellchecker' ] },
{ name: 'links' },
{ name: 'insert' },
{ name: 'forms' },
{ name: 'tools' },
{ name: 'document', groups: [ 'mode', 'document', 'doctools' ] },
{ name: 'others' },
'/',
{ name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] },
{ name: 'paragraph', groups: [ 'list', 'indent', 'blocks', 'align', 'bidi' ] },
{ name: 'styles' },
{ name: 'colors' },
{ name: 'about' }
];
// Remove some buttons provided by the standard plugins, which are
// not needed in the Standard(s) toolbar.
config.removeButtons = 'Underline,Subscript,Superscript';
// Set the most common block elements.
config.format_tags = 'p;h1;h2;h3;pre';
// Simplify the dialog windows.
config.removeDialogTabs = 'image:advanced;link:advanced';
config.extraPlugins = 'uploadimage';
config.uploadUrl = '/notice/fileupload';
};
Everything is working fine and my image file is also uploading successfully and I am getting following JSON response:
{
"uploaded": 1,
"fileName": "checkout.PNG",
"url": "/img/syllabus/checkout.PNG",
"error": null
}
But fileUploadResponse is not firing after so many tries. I am not sure which part I am missing.
I think the 'fileUploadResponse'-Event has to be registered on the ckeditor-instance and not on CKEDITOR itself.
var editor = $( 'textarea#editor1' ).ckeditor();
editor.on( 'fileUploadResponse', function( evt ) {...});
Thanks, #Benjamin Schüller for pointing in the right direction.
I am using ng-ckeditor library for CKEditor Textarea along with ng-model data. This library has the directive in which they are initiating the CKEditor instance. All I needed is to get that instance and register fileUploadResponse event to it.
Following is my textarea in template html:
<textarea id="noticeDetails" ckeditor="editorOptions" name="description" ng-model="ctrl.notice.description" ></textarea>
And in my Angular Controller, I am defining editorOptions and binding fileUploadResponse:
$scope.editorOptions = {
language: 'en',
allowedContent: true,
entities: false
};
$scope.$on("ckeditor.ready", function( event ) {
var noticeCkEditor = CKEDITOR.instances["noticeDetails"];
noticeCkEditor.on( 'fileUploadResponse', function( evt ) {
// Prevent the default response handler.
evt.stop();
// Get XHR and response.
var data = evt.data,
xhr = data.fileLoader.xhr,
response = xhr.responseText;
var respJson = angular.fromJson(response);
console.log(respJson);
if ( respJson.error ) {
// Error occurred during upload.
data.message = respJson.error.message;
evt.cancel();
} else {
data.url = respJson.url;
}
} );
});
Following is my JSON response on file upload:
{
"uploaded": 1,
"fileName": "IMG_1202.PNG",
"url": "/img/society/notice/IMG_1202.PNG",
"error": null
}
Few things to note here:
You can get an instance after CKEditor completely initialized. ng-ckeditor has broadcast called ckeditor.ready. So on ckeditor.ready you can get an instance and bind events specific to the editor.
CKEditor gives name to the instance using id of the textarea. In my case id is noticeDetails, so it will create an instance with name noticeDetails. In case you have not given the id then it will create the instance with names editor1, editor2 and so on. In my case, I am getting the CKEditor instance with noticeDetails name.
CKEditor documentation has mentioned example code to handle file upload response manually. But it is not working. They are binding whole JSON string to data.message or data.url which is not the way to do as per my experiment. What we need to do is create the JSON object from the response string and appropriately get the message or URL from that JSON object and bind it with data object as shown in the above code.

sencha touch routing extraparams kitchensink

I am trying to extend the routing in the Sencha Touch Kitchensink app as follows:
My data (in List store) are as follows:
{ category: 'fruit', str: 'tomato'},
{ category: 'fruit', str: 'green bean'},
{ category: 'vegetable', str: 'celery'},
{ category: 'vegetable', str: 'sprouts'},
{ category: 'notAVegetable', str: 'ketchup'},
{ category: 'notAVegetable', str: 'prune'}
I would like to show only those data selected by a particular category, such as "fruit"
In the Main.js controller, I am trying to do this by grabbing another parameter from the "List" node in the Demos TreeStore
routes: {
'demo/:id/:category': 'showViewById',
'menu/:id': 'showMenuById'
},
Where the showViewById action adds the extra parameter for use later
showViewById: function (id, category) {
var nav = this.getNav(),
view = nav.getStore().getNodeById(id);
console.log('view ' + id);
this.showView(view);
this.setCurrentDemo(view);
this.hideSheets();
// do stuff with category
},
I am trying to add and access 'category' as an extraParameter in my Demos.js store in the "List" tree node as follows:
{
text: 'List',
leaf: true,
id: 'list',
extraParams: {
category: 'fruit'
}
},
A few questions: Can I use an extraParameter to add this attribute to the Store? If so, how can I access it to use for my routing? I thought it would be available as metadata for my Demos store, but have not been able to access it.
Any alternatives short of creating multiple stores (one for "fruit", "vegetable", "notAVegetable," etc.) with filters on them to achieve the same thing?
TIA!

Resources