I can't get UserFlow to work for our AngularJS app.
The product runs on old AngularJS (1.8) and we love the concept of UserFlow , but the typical injection and init model runs in the core JS scope which AngularJS does not have access to... so, even after following the onboarding instructions, every user that registers is appearing to UserFlow as the same {{userId}}
We believe this is happening (UserFlow is not able to receive the user ID in userflow.identify as described here) because the user ID is not known outside of the AngularJS digest. i.e. - the method was called in a place where angularJS is not taking effect, so the handlebars never get rewritten.
Got it fixed. An overview of how we fixed it is below:
Simply split UserFlow's initialization into two distinct steps:
userflow.init() - this can be directly in your index.html or otherwise injected into the <body>
userflow.identify() - this has to be done within your AngularJS controller
.
DETAILED STEPS---------------
1. init() normally, but use a build variable and don't identify yet
In index.html, at the bottom of the <body> tag, add the following script:
<!-- 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 use Grunt as a build tool (which I don't recommend, but you can replicate the same pattern with different technologies), we put the environment-specific token, ##grunt_userflow, into our build script which replaces the individual token to match the respective environment.
You'll notice here we're not calling userflow.identify() yet...
2. Execute the UserFlow identify() directly within the controller
When the user first logs in, now you need to execute the userflow.identify() function and pass in the right IDs. Personally, I like putting AngularJS-agnostic functions like this outside of the controller and then inherit them in:
const startUserFlow = function(userId, login) {
userflow.identify(userId, {
email: login
});
};
And, now calling that 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. Finally, to reinitialize your Content, use ng-click=() on any HTML you'd like
That's right - since we're scoping it in and doing this the AngularJS way, use ng-click like any other function and bind it directly. Example below.
$scope.launchUserFlowChecklist = function () {
userflow.start('[insert content ID here]');
};
I hope this helps! Cheers.
I want to call a javascript function from within a controller function that I use:
Public Function redirectTo() As JavaScriptResult
Return JavaScript("ToSignUp()")
End Function
in my controller. But the program never goes to the script. I've already checked similar answers but I haven't found any solution to the problem. Can someone assist me with this?
ADDITION 4/3/19 12:41
I use the following for redirection from my controller... but nothing is happening:
Public Function redirectTo() As RedirectToRouteResult
Dim routes As New RouteCollection With {.RouteExistingFiles = True}
Return RedirectToAction("../login/SignUp")
End Function
End Class
ADDITION 4/3/19 23:20
The issue was solved by this way
In the code behind of my .aspx Page and at the proper place I add it the following code:
Dim routes As New RouteCollection With {.RouteExistingFiles = True}
Response.Redirect("SignUp")
The Response.Redirect instruction is not new.
But in order to be functional it needs to add before the following instruction
Dim routes As New RouteCollection With {.RouteExistingFiles = True}
And that is because the MVC did not recognize the existent files which means the property RouteExistingFiles is always False
Thus in order to work the code we need to turn this property to True
Anyway thanks to all for your assistance.
From other examples online (like this one), I would say that if you temporarily changed your function to:
Public Function redirectTo() As JavaScriptResult
Return JavaScript("alert("HERE");")
End Function
It will likely work... so without seeing the JS function contents, it's hard to tell. To the second point, a RedirectToAction call will work if called from the server; if the client is calling this, use 'window.location' instead.
So, recently I've been trying to call a controller action via javascript $.get. I was suggested by a fellow Stack Overflow member to use
$.get("custom/balance", function(){ });
Where custom is the name of the controller that I am using and balance is actionBalance()—a function that I have declared inside of that controller. I have tried to do so but it seems that the function is not being called. I have placed intentional errors inside of that function so I am sure it is not being called via the $.get function.
previously, I had directed $.get to a file in assets like so
$.get("assets/balance.php, function() { });
This had worked perfectly.
Finally, here is the actionBalance that I have declared - is it possible that I need to then call that function? I'm not sure why custom/balance is not calling the action itself.
public function actionBalance() {
// Return a string
echo '7000';
}
I apologize for the previously incomplete answer which left you confused, as I assumed everyone would want to remove the index.php script name from the URL.
If you are using the default settings, yes, you should add the index.php?r= before the path. index.php is called the entry script in Yii. Other files are hidden/protected from the public in the protected folder.
To hide this entry script from the URL, please follow this tutorial on Yii's website:
Yii 1.1: Url: hide index.php
I need to pass a parameter to a page using JSP include directive find my code below:
<script>
var gTIID
function showPlayerList(pTIID)
{
gTIID=pTIID;
$("#IdtournamentMessageMain").hide();
$("#userListFrame").show();
}
</script>
<%# include file="players_list.jsp" %>
How can I pass gTIID to players_list.jsp in the players_list.jsp page gTTID is named tiID (/player_list.htm?tiID=gTIID) ?
Thanks in advance!
since you are using the <include> directive, gTIID should already be available.
Alternately, you could set them in one of the scopes (request, session, application) and then fetch from the same scope in your other JSP.
Or, you could also use <jsp:include> and <jsp:param> to achieve this.
You'll be able to acces gTIID in players_list.jsp. There's no need to pass it as a parameter, as it's a global-scope defined variable. However, take into account that the variable is only defined and initialized in the including page, not players.jsp. If that page is referenced somewhere else, it may be undeclared.
I solved the issue using an hidden input box(id="tidhide") in the "players_list.jsp page initialized to -1 and I set it in the showplayer function, find my snippet code below:
Parent page:
function showPlayerList(pTIID)
{
gTIID=pTIID;
$("#IdtournamentMessageMain").hide();
$("#tidhide").val(pTIID);
oTable.fnDraw();
$("#userListFrame").show();
}
bye!
I know it's easily possible to execute javascript through a controller, but is it possible to do it the other way around?
Use case scenario:
I have a list of products on the left side of a page. When I click one of these products, I have a CSS highlight appearing. I'd then like the javascript to rerender my search results function, "showtheresults".
It would allow users to drill down the product whose data they are searching through. The only way I can think of doing it is through javascript. Other suggestions welcome.
You can try using the Ajax toolkit. In JavaScript:
// Include Ajax toolkit
{!REQUIRESCRIPT("/soap/ajax/22.0/connection.js")}
{!REQUIRESCRIPT("/soap/ajax/22.0/apex.js")}
// Get selected Lead Ids
var leadIds = {!GETRECORDIDS($ObjectType.Lead)};
// Call your class method
var result = sforce.apex.execute('CalledFromJavaScript', 'theMethod', {arg: leadIds});
Then in Apex:
// Make your class global and method a webservice:
global class CalledFromJavaScript
{
webService static Integer theMethod(List<Id> sObjectIds)
{
...
}
}
You can do this with an output panel that you rendered selectively based on a boolean in your controller. In this example set 'renderScriptPanel' when you'd like the showResults function to run. If you're doing a partial page refresh make sure to refresh the id of the output panel.
<apex:outputPanel id="scriptPanel" rendered="{!renderScriptPanel}">
<script type="text/javascript">
showTheResults();
</script>
</apex:outputPanel>
You may want to look at JavaScript Remoting for Apex Controllers