How can I implement user flow using AngularJS?

We're facing an issue with implementing UserFlow in our AngularJS app.

Our product is built on older version of AngularJS (1.8) and although we find the concept of UserFlow appealing, we are running into a problem. The standard injection and initialization model operates within the core JavaScript scope which is not accessible by AngularJS. As a result, even after following the provided instructions, every user that signs up is being recognized as the same {{userId}}

We suspect that this issue occurs because UserFlow cannot retrieve the user ID in userflow.identify as outlined here. This is likely due to the fact that the user ID is not available outside of the AngularJS digest cycle. Essentially, the method was invoked in a context where AngularJS is not active, causing the handlebars to remain unchanged.

Answer №1

Issue Resolved. Here's How We Fixed It:

We decided to split the initialization of UserFlow into two separate steps:

  1. userflow.init() - this can be done directly in your index.html or injected into the <body>
  2. userflow.identify() - this step should be carried out within your AngularJS controller

.

STEP-BY-STEP PROCESS---------------

1. Execute init(), but hold off on identify

In your index.html, add the following script at the end of the <body> tag:

<!-- UserFlow -->
        <script ng-if="customization.features.userflow">
          !function(){var e="undefined"==typeof window?{}:window,t=e.userflow;if(!t){var r="https://js.userflow.com/";t=e.userflow={_stubbed:!0};var n=e.USERFLOWJS_QUEUE=e.USERFLOWJS_QUEUE||[],o=function(e){t[e]=function(){var t=Array.prototype.slice.call(arguments);i(),n.push([e,null,t])}},s=function(e){t[e]=function(){var t,r=Array.prototype.slice.call(arguments);i();var o=new Promise((function(e,r){t={resolve:e,reject:r}}));return n.push([e,t,r]),o}},a=function(e,r){t[e]=function(){return r}},u=!1,i=function(){if(!u){u=!0;var t=document.createElement("script");t.async=!0;var n=e.USERFLOWJS_ENV_VARS||{};"es2020"===(n.USERFLOWJS_BROWSER_TARGET||function(e){for(var t=[[/Edg\//,/Edg\/(\d+)/,80],[/OPR\//,/OPR\/(\d+)/,67],[/Chrome\//,/Chrome\/(\d+)/,80],[/Safari\//,/Version\/(\d+)/,14],[/Firefox\//,/Firefox\/(\d+)/,74]],r=0;r<t.length;r++){var n=t[r],o=n[0],s=n[1],a=n[2];if(e.match(o)){var u=e.match(new RegExp(s));if(u&&parseInt(u[1],10)>=a)return"es2020";break}}return"legacy"}(navigator.userAgent))?(t.type="module",t.src=n.USERFLOWJS_ES2020_URL||r+"es2020/userflow.js"):t.src=n.USERFLOWJS_LEGACY_URL||r+"legacy/userflow.js",t.onerror=function(){u=!1,console.error("Could not load Userflow.js")},document.head.appendChild(t)}};o("_setTargetEnv"),o("closeResourceCenter"),o("init"),o("off"),o("on"),o("prepareAudio"),o("registerCustomInput"),o("remount"),o("reset"),o("setCustomInputSelector"),o("setCustomNavigate"),o("setCustomScrollIntoView"),o("setInferenceAttributeFilter"),o("setInferenceAttributeNames"),o("setInferenceClassNameFilter"),o("setResourceCenterLauncherHidden"),o("setScrollPadding"),o("setShadowDomEnabled"),o("setPageTrackingDisabled"),o("setUrlFilter"),o("openResourceCenter"),o("toggleResourceCenter"),s("endAll"),s("endAllFlows"),s("endChecklist"),s("group"),s("identify"),s("identifyAnonymous"),s("start"),s("startFlow"),s("startWalk"),s("track"),s("updateGroup"),s("updateUser"),a("getResourceCenterState",null),a("isIdentified",!1)}}();
          userflow.init('@@grunt_userflow')
        </script>

Since we utilize Grunt as a build tool (not the ideal choice, but you can replicate this pattern with different technologies), we inserted the environment-specific token, @@grunt_userflow, into our build script to replace it accordingly based on the environment.

It's important to note that we are not triggering userflow.identify() yet...

2. Implement UserFlow identify() directly in the controller

Upon initial user log-in, make sure to call the userflow.identify() function with the appropriate IDs. I prefer placing AngularJS-independent functions like this outside the controller and then incorporating them in:

const startUserFlow = function(userId, login) {
  userflow.identify(userId, {
    email: login
  });
};

Now, invoke this function from within AJS:

$scope.processCredentials($scope.username, response.data.access_token).then(function (result) {
    trackEvent('signIn', $rootScope.userProfile.id);
    startUserFlow($rootScope.userProfile.id, $scope.username);

3. Lastly, to reset your Content, utilize ng-click=() on any relevant HTML element

That's correct - as we're encapsulating it and following the AngularJS approach, utilize ng-click just like any other function and bind it directly. See example below.

$scope.launchUserFlowChecklist = function () {
    userflow.start('[insert content ID here]');
};

We hope this guide proves helpful! Cheers.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

How to Enhance Angular ui-router nested views with Resolve?

I have been working on creating a customized version of my Angular 1.4.12 application with nested views. The main purpose behind this customization is to accommodate sections within the app that require a header/content/footer structure, while others do no ...

Is it possible to host multiple React applications on a single port? Currently experiencing issues with running both an Admin panel and the Front side in production mode on the same Node.js API server

Is it possible to host multiple React applications on the same port? I am experiencing issues with running both an Admin panel and a Front side React app in production mode on the same Node.js API server. ...

retrieving data from identical identifiers within a loop

Within my while loop, I am retrieving various dates for each event. <?php while( have_rows('_event_date_time_slots') ): the_row(); ?> <div> <h3 class="date-<?php echo $post->ID?>" name="tttdate<?php e ...

A step-by-step guide on incorporating Google Ads Manager Ads units (Banners) into your website with NuxtJs and VueJs

I currently have an Ads unit set up on my website <script type="text/javascript"> google_ad_client = "ca-pub-2158343444694791";/* AD7 */ google_ad_slot = "AD7"; google_ad_width = 300; google_ad_height = 250; </script ...

I just finished crafting a dynamic line chart with d3.js within a React environment. However, I am now looking to add some personalized touches to enhance its appearance. Could you kindly review the details and code

I want to create a line chart using React and D3, similar to the one depicted in this image. Presently, I have partially implemented the chart with the code provided below. You can see it here. The code snippet I've developed so far is outlined as f ...

Personalized parallax design

I am in the process of developing my own custom parallax plugin to allow me to control the direction in which items transition off the screen. However, I am currently facing a challenge in ensuring that regardless of how a user scrolls or the size of the w ...

Issues arise when attempting to display a div element in AngularJS based on the value of a hidden input

I have been struggling to make my div elements appear based on a hidden input value in the code snippet below: <form method="post"> <fieldset data-ng-app=""> <input type="hidden" id="Method" name="Method" value="1" data-ng-model="meth ...

Investigate the CSS display property of an element using JavaScript

Can JavaScript be used to determine if an element's CSS display == block or none? ...

Utilizing JSTL: Executing a function within <script> through the c:set tag in JSTL

Can someone help me with this code snippet? <c:set var="cls" value="${myFunction(param)}"/> ..... <script> function myFunction(param) { if(param == true) { return "aaa"; } else { return "bbb"; ...

Encountering an endless loop while attempting to retrieve data from Firebase in Next.js through the use of useEffect

Currently, I am in the process of setting up a video section for a project using NextJS. The videos are stored in firebase storage. I have implemented a dynamic route that retrieves all videos from a specific reference within the bucket. For instance, if ...

What is the best way to retrieve access to my container/store from this main file?

As a beginner in React/Redux, I am facing an issue where everything works fine in Redux when testing it. However, I am struggling to integrate it into my actual application. I believe that I need to use connect(), but I am unsure of how or where to imple ...

Utilizing an attribute's data within a custom Angular directive

Greetings from a new Angular enthusiast! I recently created a custom directive that is designed to display the value of an attribute in a span element: myApp.directive("myDirective", function(){ return { restrict : "E", template : "&l ...

Unable to stop React HTMLAudioElement from playing

In the realm of React, there exists a component that requires a URL input to function. The purpose of this component is to display a button that allows users to control the playback of an audio file. It's worth noting that this particular component is ...

Are we retrieving multiple APIs the right way?

Looking for some guidance on fetching two APIs in React. I have created two functions to handle this task and called them simultaneously within another function. Should I stick with this approach or move the API calls to componentDidMount? Additionally, I& ...

Issue with accessing form in Angular 6 Reactive forms for custom validator functionality

I am facing an issue with creating a password validation for reactive forms in Angular. Every time I try to verify the password, I get a “Cannot read property 'get' of undefined” error in the console. I have tried different methods to access ...

Playing a game of rock, paper, scissors with two players using JavaScript

Hello! I am a beginner in JavaScript and I am trying to create a simple rock, paper, scissors game. However, when I run the code, I receive two prompt messages and an error saying 'TypeError: playerOneChoice is not a function'. What mistake did I ...

Is it possible to integrate markup within AngularJS IDs?

I am trying to repeat an array of 3 items using "ng-repeated" but encountering a problem. <li ng-repeat="item in obj.items id="testobj{{testobj.number}}"> </li> After checking the page, I noticed that the id for all 3 items is just "testobj" ...

Angular 4 Operator for adding elements to the front of an array and returning the updated array

I am searching for a solution in TypeScript that adds an element to the beginning of an array and returns the updated array. I am working with Angular and Redux, trying to write a reducer function that requires this specific functionality. Using unshift ...

Data uploaded onto Meteor using angular-meteor

Currently, I am using angular-meteor for my application and I am looking for a way to check if the content has finished loading. I require this waiting period as I need to scroll down the page. Think of it like the view on Instagram where you see a feed o ...

The unexpected occurence of the Onbeforeunload exception

I am attempting to create an exception for onbeforeunload and display a warning about potential data loss whenever the quantity is not zero: Here is what I have tried so far: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www. ...