Executing a salesforce controller through javascript? - javascript

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

Related

userFlow with AngularJS?

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.

Function to expand a parent node dynamically from json in a dhtmlx tree

On the dhtmlx documentation, I see this:
The following method is responsible for specifying the way server side URL is constructed during dynamical loading calls:
<script>
tree.setXMLAutoLoadingBehaviour(mode);
</script>
The following modes variants are available here:
function - is used for calling user-defined handler that should be set as the first parameter of setXMLAutoLoading() method.
So I understand that I need to write a function to add one layer of children to the actual node (the selected node), if it has children. But what I don't understand, is how I should do this, as I have some difficulty finding the right parameters to use, to be able to find the children and add them to the tree.
I am loading a local json file, with .loadJSON("data.json");. Right now, I know I should change the behavior to function and call a function that will load the children of the clicked/expended node. I've named that function loadBranch in the code below.
myTree.setXMLAutoLoadingBehaviour("function");
myTree.setXMLAutoLoading(function (id) { loadBranch(id)});
But I am not able to write a function that will only find and add those children into my dhtmlx tree. Could any of you give me a code snippet that could be used as this function?
Thanks anyway.
PS: My "ultimate" goal is to create a default dhtmlx tree that can load a json dynamically, to compare it's performance with other trees.
Try something like this:
mytree.setXMLAutoLoadingBehaviour("function");
mytree.setXMLAutoLoading (function(id){
// here based on ID you need to load some XML
mytree.loadXML(myFunction(id));
});

Using AJAX to call a Rails controller method that takes a parameter

I know there are lots of other questions that address calling rails controller methods using AJAX, but I don't see any that call a rails controller method that requires a parameter, in my case, "def sum_graph(date)"
I am using the Chart.js bar graph and I get the values of the bars using this rails method. Upon updating the values that are used by the sum_graph(date) method via best_in_place, I would like to be able to call that method using the new values to update the chart without refreshing the page.
My code so far looks like this (I'm only updating one bar in the graph since I'm just trying to get it to work):
$(document).ready(function() {
$('.best_in_place').bind("ajax:success", function() {
myBar.datasets[0].bars[0].value = <%= sum_graph(Date.current - 24.months) %>;
myBar.update();
});
});
If I replace the sum_graph(date) method in this code with a random number generator, it indeed updates the bar with the random value every time I change a value using best_in_place so I think I'm headed in the right direction. So is my code indeed re-calling this method each time I change a value, just without the new values? Either way, any help here to resolve this problem would be much appreciated.
Thanks so much in advance.

What is the standard workaround for dynamic js files

Is there a standard workaround for something like this in ASP.NET:
//myinclude.js
$(document).ready(function() {
for(recs=0; recs < {{server-side-value}}; recs++) {
// process records
}
});
Note this is a js file. I know about WinForms ability to insert dynamic quoted scripts into the page. But how about a page's js file that is dependent on server-side values? I know you can use something like:
//myview.cshtml
var instance = new MyObject(<%= ServerSideValue =%>);
and include it on the page to pass it to the js file, but I'm wondering about the architecture of keeping js separate from html code so that an html/css designer can work with the template free of javascript; keeping everything separate. I primarily use MVC now.
What are some of the patterns to deal with this? Is the only solution dynamically inserting js into the actual page or having partial views included separately into the page.? Or is there a way to sprinkle server-side values in separated js? In short, no dynamic js files?
I'm not trying to fix an exact project at this time, I have just been curious about this on past projects.
Thanks...
There are multiple ways to achieve this. One of the ways would be populating your data into a Javascript objects on the HTML page directly.
//myview.cshtml
<script>
var pageData = {
name : '#variable1',
value1: #value1
};
</script>
And, in the javascript file:
//pageUI.js
if (pageData) {
$('#page_tile').html(pageData.name);
}
I am sure you can optimize a whole lot (for example, having a single communication between the server side data and the client side code). At the end of the day, you want to make sure that your javascript code can be resusable.
for example one can do this:
A. have the main .js code read any context-specific parameters from the current window like this (page.js):
!function(window){
var configData = window.MyAppNameConfigData;
// rest app code here..
}(window);
B. the server side script can inject these context-specific data in the page's html like this:
<script>
window.MyAppNameConfigData = {
param1: //..
param2: //..
// etc..
};
</script>
Note if needed make sure that the page.js is enqueued/loaded after the data have been injected/inserted (using a script dependency chain for example)
If it's not "inline" (on the View/Page itself), you could do a Partial View/Page:
Trivial example: _PartialJs.cshtml
$(document).ready(function() {
var foo = "#DateTime.Now.Year";
});
Then in your view:
<script>
#Html.Partial("_PartialJs")
</script>
Renders:
<script>
$(document).ready(function() {
var foo = "2015";
});
</script>
Hth...

passing a parameter using JSP include directive

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!

Categories

Resources