I am having trouble using JQueryUI with salesforce standard elements. Basically, I want to auto suggest the record names to the user, instead of the user clicking on the salesforce search button.
<apex:inputField value="{!MyRecord.ChildRecord__c}" id="inpId" required="true/>
<script>
jq$(document.getElementById('{!$Component.inpId}')).autocomplete( {
minLength: 2,
autoFocus: true,
source: mySource
});
</script>
Therefore, I want to know if anyone attempted to use JQueryUI with standard salesforce input elements. In my case, the JQueryUI events are not firing for salesforce elements.
{!$Component.[elementid]} doesn't always work for me; I'm not sure why. I prefer to use the Attribute Ends With Selector (http://api.jquery.com/attribute-ends-with-selector/).
Try something like this:
<apex:includeScript value="/soap/ajax/18.0/connection.js" />
<apex:includeScript value="/soap/ajax/18.0/apex.js" />
<script>
var j$ = jQuery.noConflict();
j$(document).ready(function(){init();});
function init()
{
var mySourceText = "ActionScript AppleScript Asp BASIC C "
+ "C++ Clojure COBOL ColdFusion Erlang Fortran Groovy "
+ "Haskell Java JavaScript Lisp Perl PHP Python Ruby "
+ "Scala Scheme";
var mySource = mySourceText.split(" ");
j$("[id$='myInput']").autocomplete({
minLength: 2,
autoFocus: true,
source: function(request, response){
response(GetSourceAjaxAPI(request.term)); }
});
}
function GetSourceAjaxAPI(s)
{
var result = sforce.apex.execute("TestAutocomplete",
"GetAutocompleteValuesAjaxAPI", {SearchTerm:s});
return result;
}
</script>
<apex:form >
<apex:pageblock >
<apex:pageblocksection >
<apex:pageblocksectionitem >
<apex:inputfield id="myInput" value="{!Contact.FirstName}" />
</apex:pageblocksectionitem>
</apex:pageblocksection>
</apex:pageblock>
</apex:form>
Controller:
global class TestAutocomplete
{
global TestAutocomplete(ApexPages.StandardController myStandardController) {}
webservice static List<String>
GetAutocompleteValuesAjaxAPI(String SearchTerm)
{
String mySourceText = 'ActionScript AppleScript Asp BASIC C '
+ 'C++ Clojure COBOL ColdFusion Erlang Fortran Groovy '
+ 'Haskell Java JavaScript Lisp Perl PHP Python Ruby '
+ 'Scala Scheme';
List<String> mySourceList = mySourceText.split(' ');
List<String> myReturnList = new List<String>();
for(String s : mySourceList)
{
if(s.contains(SearchTerm)){ myReturnList.add(s); }
}
return myReturnList;
}
}
Hope that helps,
Matt
I figured out the reason why JQeuryUI was not working on SalesForce standard input element. I was trying to use JQueryUI autocomplete on the input element. The action function that was supposed to be invoked was not called because I did not have
<apex:actionFunction immediate="true" />
That is we must have immediate=true attribute set so that action function is called immediately. If we do not have this attribute set, SalesForce tries to validate all the standard input elements and if the validation fails, action function is never called.
Related
I am using vue-i18n and I need to translate a sentence with an anchor tag in the middle. Obviously I want to keep the html specific markup out of my translations, but how best to handle this?
Consider the following example:
This is a test sentence which cannot
be split
or it will not make sense
The only solution I can come up with is:
{
"en": {
"example": "This is a test sentence which cannot {linkOpen}be split{linkClose} or it will not make sense"
}
}
and then in the component template
<p v-html="$t('example', {
'linkOpen': `<a href="https://example/com" class="test-class test-another-class">`,
'linkClose: '</a>'
})
"></p>
Not exactly elegant however...
Edit: I've tested this and it doesn't actually work (can't put html in params) so now I'm really out of ideas!
You can come up with some simple markup for links and write a small transformation function, for example:
//In this example links are structured as follows [[url | text]]
var text = `This is a test sentence which
cannot [[https://example.com | be split]] or it will not make sense`
var linkExpr = /\[\[(.*?)\]\]/gi;
var linkValueExpr = /(\s+\|\s+)/;
var transformLinks = (string) => {
return text.replace(linkExpr, (expr, value) => {
var parts = value.split(linkValueExpr);
var link = `${parts[2]}`;
return link;
});
}
alert(transformLinks(text));
JSFiddle: https://jsfiddle.net/ru5smdy3/
With vue-i18n it will look like this (which of course you can simplify):
<p v-html="transformLinks($t('example'))"></p>
You can put the HTML into an element that is not part of the displayed DOM and then extract its textContent. This may not work for what you're actually trying to do, though. I can't tell.
new Vue({
el: '#app',
data: {
html: `This is a test sentence which cannot
be split
or it will not make sense`,
utilityEl: document.createElement('div')
},
methods: {
htmlToText: function (html) {
this.utilityEl.innerHTML = html;
return this.utilityEl.textContent;
}
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.1/vue.js"></script>
<div id="app">
<p v-html="html"></p>
<p>{{htmlToText(html)}}</p>
</div>
I have found myself in a similar situation, and I propose using Vue-i18n slots.
I have a JSON i18n file which had error messages that were html. These rendered fine but they will not be compiled as vue templates, and cannot have bindings. I want to call an onclick function when users click the link in a given error message.
In my example I have a cake-state json with some status messages:
// cake_state.json, where I want links in error messages to call a function when clicked
{
"state":{
"stage": {
"mixing": "Cake is being mixed. The current time is {time}",
"resting": "Cake is resting. The current time is {time}",
"oven": "Cake is cooking. The current time is {time}"
},
"error": {
"ovenIssue": "Oven of brand is malfunctioning. Click {email_support_link} to get help",
"chefIssue": "Chef is down. Click {email_support_link} to get help",
"leakIssue": "There is a leak"
},
}
}
Now if we have some Vue SFC, with the template as such:
<template>
<div>
<i18n :path="getMessage">
<!-- enter various i18n slots -->
<template #time>
<span>{{ getTime }}</span>
</template>
<template #email_support_link>
<!-- binding now works because it is not v-html -->
<a href="" #click.prevent="getRightSupportDepartment">here</span>
</template>
</i18n>
</div>
</template>
...
// js
computed: {
getTime(): string { //implementation ...},
getRightSupportDepartment(): string { //implementation ...},
//return strings that are keys to cake_state.json messages
getMessage(): string {
...
switch (this.cakeState) {
case Failure.Overheat:
return "state.error.ovenIssue";
case Failure.ChefIdle:
return "state.error.chefIssue";
case Failure.LeakSensor:
return "state.error.leakIssue";
So what we see here is:
the getMessage function provides us the key to the message in the i18n JSON. This is passed into i18n component
the <template #XXX> slots in the i18n component's scope are supplied with this key from the function, which gets the corresponding message, and then
if the relevant message has any of the keywords, it gets put in from the corresponding template.
To re-iterate, it helps to provide a means to have vue bindings to html elements which would otherwise be served from the i18n json as raw html.
For example now we might see "Oven of brand is malfunctioning. Click here to get help", and we can run an onclick function when user clicks 'here'.
____ INTRO
Hello everyone, first of all, three clarifications:
My english is not good, so I beg your pardon in advance for my mistakes,
I'm a newbie so forgive me for inaccuracies,
I have previously searched and tried the solutions I found on the internet but still I can not solve the problem of embedding a prepopulated database.
____ THE GOAL
I want to develop an app for iOS and Android with a prepopulated database.
Just for example, the database consists of 15.000 records each one made of three key-value pair (id, firstname and lastname).
___ WHAT I DID
Steps:
ionic start myapp blank
cd myapp
ionic platform add ios
ionic platform add android
Then I created an sqlite database for testing purpose, named mydb.sqlite, made of one table people containing two id, firstname, lastname records.
I decided to use the following plugin: https://github.com/Antair/Cordova-SQLitePlugin
That's because it can be installed with cordova tool.
ionic plugin add https://github.com/Antair/Cordova-SQLitePlugin
(Alert: I think that the instructions on the website show an incorrect reference - "cordova plugin add https://github.com/brodysoft/Cordova-SQLitePlugin" - which refers to another plugin).
Then, following the instructions on the plugin website, I copied the database to myapp/www/db/ so that it can now be found at myapp/www/db/mydb.sqlite
I modified the index.html including the SQLite plugin just after the default app.js script:
<!-- your app's js -->
<script src="js/app.js"></script>
<script src="SQLitePlugin.js"></script>
I also write some lines of code in index.html file to show a button:
<ion-content ng-controller="MyCtrl">
<button class="button" ng-click="all()">All</button>
</ion-content>
Finally I had modified ./js/app.js:
// Ionic Starter App
var db = null;
angular.module('starter', ['ionic' /* What goes here? */ ])
.run(function($ionicPlatform) {
$ionicPlatform.ready(function() {
// some predefined code has been omitted
window.sqlitePlugin.importPrepopulatedDatabase({file: "mydb.sqlite", "importIfExists": true});
db = window.sqlitePlugin.openDatabase({name: "mydb.sqlite"});
}); // $ionicPlatform.ready
}) // .run
.controller('MyCtrl', function($scope){
$scope.all = function(){
var query = "SELECT * FROM people";
// I don't know how to proceed
}; // $scope.all
}); // .controller
___ THE PROBLEM
I don't know how to proceed in the controller section to query all the records (just an example of query) and show the results in the console.log.
I think that the following code must be completed in some way:
angular.module('starter', ['ionic' /* What goes here? */ ])
And also the code inside controller section must be completed:
$scope.all = function(){
var query = "SELECT * FROM people";
// I don't know how to proceed
}; // $scope.all
___ FINAL THANKS
Thank you in advance for the help you will give to me.
So this guy's code has helped a lot to encapsulate my DAL. I highly recommend that you use he's code pretty much verbatim.
https://gist.github.com/jgoux/10738978
You'll see he has the following method:
self.query = function(query, bindings) {
bindings = typeof bindings !== 'undefined' ? bindings : [];
var deferred = $q.defer();
self.db.transaction(function(transaction) {
transaction.executeSql(query, bindings, function(transaction, result) {
deferred.resolve(result);
}, function(transaction, error) {
deferred.reject(error);
});
});
return deferred.promise;
};
Let's break this down a bit. The query function takes a query string (the query param) and a list of possible bindings for ? in a query like "SELECT * FROM A_TABLE WHERE ID = ?". Because he's code is a service, the self value points to the service itself for all future invocations. The function will execute a transaction against the db, but it returns a promise that is only fulfilled once the db comes back.
His service provides a second helper function: fetchAll.
self.fetchAll = function(result) {
var output = [];
for (var i = 0; i < result.rows.length; i++) {
output.push(result.rows.item(i));
}
return output;
};
fetchAll will read the rows in their entirety into an array. The result param for fetchAll is the result variable passed in the query function's promise fulfillment.
If you copy and paste his code into your service file, you now have a bonafide DB service. You can wrap that service up in a DAL. Here's an example from my project.
.service('LocationService', function ($q, DB, Util) {
'use strict';
var self = this;
self.locations = [];
self.loadLocked = false;
self.pending = [];
self.findLocations = function () {
var d = $q.defer();
if (self.locations.length > 0) {
d.resolve(self.locations);
}
else if (self.locations.length === 0 && !self.loadLocked) {
self.loadLocked = true;
DB.query("SELECT * FROM locations WHERE kind = 'active'")
.then(function (resultSet) {
var locations = DB.fetchAll(resultSet);
self.locations.
push.apply(self.locations, locations);
self.loadLocked = false;
d.resolve(self.locations);
self.pending.forEach(function (d) {
d.resolve(self.locations);
});
}, Util.handleError);
} else {
self.pending.push(d);
}
return d.promise;
};
})
This example is a bit noisy since it has some "threading" code to make sure if the same promise is fired twice it only runs against the DB once. The general poin is to show that the DB.query returns a promise. The "then" following the query method uses the DB service to fetchAll the data and add it into my local memory space. All of this is coordinated by the self.findLocations returning the variable d.promise.
Yours would behalf similarly. The controller could have your DAL service, like my LocationService, injected into it by AngularJS. If you're using the AngularJS UI, you can have it resolve the data and pass it into the list.
Finally, the only issue I have with the guy's code is that the db should come from this code.
var dbMaker = ($window.sqlitePlugin || $window);
The reason for this is that the plugin does not work within Apache Ripple. Since the plugin does a fine job mirroring the Web SQL interface of the browser, this simple little change will enable Ripple to run your Ionic Apps while still allowing you to work your SQLite in a real device.
I hope this helps.
This is the code:
var MyModel = Backbone.Model.extend({
defaults: {std:"",
pod:""
}
});
var MyView = Backbone.View.extend({
tagName:'ul',
events: {
'change input' : 'changed', // When input changes, call changed.
'hover .std' : 'timepick', //
'mouseout .std': 'doesntwork'
},
template:_.template($('#mytemplate').html()),
initialize: function() {
this.model.bind('change:pod',this.render,this); // When pod changes, re-render
},
timepick: function(e) {
$('.std').each(function(){
$.datepicker.setDefaults({dateFormat:'mm-dd'});
$(this).datetimepicker({timeFormat:'hh:mm',ampm:false});
});
},
doesntwork: function() {
// Would this.model.set here but mouseout happens when you select date/time values with mouse
},
render: function() {
$(this.el).html(this.template(this.model.toJSON()));
return this;
},
changed: function(e) {
var value = $(e.currentTarget).val(); // Get Change value
var cls = $(e.currentTarget).attr('class'); //Get Class of changed input
var obj = {};
obj[cls] = value; // The model variables match class names
this.model.set(obj); // this.model.set({'std':value})
}
});
I have a datetimepicker in the UI I'm working on, and having difficulties assigning the value that is selected from the datetimepicker to MyModel.
It appears from using console.log output that 'change input' is triggered when clicking on the DTP and assigns the default value of (MM-DD 00:00). Even when you select a different date/time value than the default, the 'change input' is not triggered again, unless you click on the input box (for a second time), and then the correct value is assigned. Not very user-friendly.
So I had the idea that I would just assign the value on mouseout, which didn't work since mouseout happens when you start to select date/time values. I also tried blur, and that didn't work either.
Where am I going wrong?
Edit: Here is a jsfiddle.net link that illustrates my problem http://jsfiddle.net/9gSUe/1/
Looks like you're getting tripped up by jQuery UI's CSS. When you bind a datepicker to an <input>, jQuery UI will add a hasDatepicker class to the <input>. Then you do this:
var cls = $(e.currentTarget).attr('class');
on the <input> and get 'std hasDatepicker' in cls.
There are too many things that will mess around with class so you're better off using something else to identify the property you want to change. You could use an id if there is only one std:
<!-- HTML -->
<input id="std" class="std" ...>
// JavaScript
var cls = e.currentTarget.id;
or the name attribute:
<!-- HTML -->
<input name="std" class="std" ...>
// JavaScript
var cls = $(e.currentTarget).attr('name');
or perhaps even a HTML5 data attribute:
<!-- HTML -->
<input class="std" data-attr="std" ...>
// JavaScript
var cls = $(e.currentTarget).data('attr');
I think the name attribute would be the most natural: http://jsfiddle.net/ambiguous/RRKVJ/
And a couple side issues:
Your fiddle was including multiple versions of jQuery and that's generally a bad idea.
You don't have to build an object for set, you can say just m.set(attr, value) if you're just setting one attribute.
You don't have to $(this.el) in your views, newer Backbones have this.$el already cached.
console.log can handle multiple arguments so you can say console.log('Std: ', attrs.std, ' Pod: ', attrs.pod, ' Poa: ', attrs.poa); instead of console.log('Std: ' + attrs.std + ' Pod: ' + attrs.pod + ' Poa: ' + attrs.poa); if you don't want + stringify things behind your back.
I would like to use an autocomplete with ajax. So my goal is to have:
When the user types something in the text field, some suggestions provided by the server appear (I have to find suggestions in a database)
When the user presses "enter", clicks somewhere else than in the autocomplete box, or when he/she selects a suggestion, the string in the textfield is sent to the server.
I first tried to use the autocomplete widget provided by lift but I faced three problems:
it is meant to be an extended select, that is to say you can originally only submit suggested values.
it is not meant to be used with ajax.
it gets buggy when combined with WiringUI.
So, my question is: How can I combine jquery autocomplete and interact with the server in lift. I think I should use some callbacks but I don't master them.
Thanks in advance.
UPDATE Here is a first implementation I tried but the callback doesn't work:
private def update_source(current: String, limit: Int) = {
val results = if (current.length == 0) Nil else /* generate list of results */
new JsCmd{def toJsCmd = if(results.nonEmpty) results.mkString("[\"", "\", \"", "\"]") else "[]" }
}
def render = {
val id = "my-autocomplete"
val cb = SHtml.ajaxCall(JsRaw("request"), update_source(_, 4))
val script = Script(new JsCmd{
def toJsCmd = "$(function() {"+
"$(\"#"+id+"\").autocomplete({ "+
"autocomplete: on, "+
"source: function(request, response) {"+
"response("+cb._2.toJsCmd + ");" +
"}"+
"})});"
})
<head><script charset="utf-8"> {script} </script></head> ++
<span id={id}> {SHtml.ajaxText(init, s=>{ /*set cell to value s*/; Noop}) } </span>
}
So my idea was:
to get the selected result via an SHtml.ajaxText field which would be wraped into an autocomplete field
to update the autocomplete suggestions using a javascript function
Here's what you need to do.
1) Make sure you are using Lift 2.5-SNAPSHOT (this is doable in earlier versions, but it's more difficult)
2) In the snippet you use to render the page, use SHtml.ajaxCall (in particular, you probably want this version: https://github.com/lift/framework/blob/master/web/webkit/src/main/scala/net/liftweb/http/SHtml.scala#L170) which will allow you to register a server side function that accepts your search term and return a JSON response containing the completions. You will also register some action to occur on the JSON response with the JsContext.
3) The ajaxCall above will return a JsExp object which will result in the ajax request when it's invoked. Embed it within a javascript function on the page using your snippet.
4) Wire them up with some client side JS.
Update - Some code to help you out. It can definitely be done more succinctly with Lift 2.5, but due to some inconsistencies in 2.4 I ended up rolling my own ajaxCall like function. S.fmapFunc registers the function on the server side and the function body makes a Lift ajax call from the client, then invokes the res function (which comes from jQuery autocomplete) on the JSON response.
My jQuery plugin to "activate" the text input
(function($) {
$.fn.initAssignment = function() {
return this.autocomplete({
autoFocus: true,
source: function(req, res) {
search(req.term, res);
},
select: function(event, ui) {
assign(ui.item.value, function(data){
eval(data);
});
event.preventDefault();
$(this).val("");
},
focus: function(event, ui) {
event.preventDefault();
}
});
}
})(jQuery);
My Scala code that results in the javascript search function:
def autoCompleteJs = JsRaw("""
function search(term, res) {
""" +
(S.fmapFunc(S.contextFuncBuilder(SFuncHolder({ terms: String =>
val _candidates =
if(terms != null && terms.trim() != "")
assigneeCandidates(terms)
else
Nil
JsonResponse(JArray(_candidates map { c => c.toJson }))
})))
({ name =>
"liftAjax.lift_ajaxHandler('" + name
})) +
"=' + encodeURIComponent(term), " +
"function(data){ res(data); }" +
", null, 'json');" +
"""
}
""")
Update 2 - To add the function above to your page, use a CssSelector transform similar to the one below. The >* means append to anything that already exists within the matched script element. I've got other functions I've defined on that page, and this adds the search function to them.
"script >*" #> autoCompleteJs
You can view source to verify that it exists on the page and can be called just like any other JS function.
With the help of Dave Whittaker, here is the solution I came with.
I had to change some behaviors to get:
the desired text (from autocomplete or not) in an ajaxText element
the possibility to have multiple autocomplete forms on same page
submit answer on ajaxText before blurring when something is selected in autocomplete suggestions.
Scala part
private def getSugggestions(current: String, limit: Int):List[String] = {
/* returns list of suggestions */
}
private def autoCompleteJs = AnonFunc("term, res",JsRaw(
(S.fmapFunc(S.contextFuncBuilder(SFuncHolder({ terms: String =>
val _candidates =
if(terms != null && terms.trim() != "")
getSugggestions(terms, 5)
else
Nil
JsonResponse(JArray(_candidates map { c => JString(c)/*.toJson*/ }))
})))
({ name =>
"liftAjax.lift_ajaxHandler('" + name
})) +
"=' + encodeURIComponent(term), " +
"function(data){ res(data); }" +
", null, 'json');"))
def xml = {
val id = "myId" //possibility to have multiple autocomplete fields on same page
Script(OnLoad(JsRaw("jQuery('#"+id+"').createAutocompleteField("+autoCompleteJs.toJsCmd+")"))) ++
SHtml.ajaxText(cell.get, s=>{ cell.set(s); SearchMenu.recomputeResults; Noop}, "id" -> id)
}
Script to insert into page header:
(function($) {
$.fn.createAutocompleteField = function(search) {
return this.autocomplete({
autoFocus: true,
source: function(req, res) {
search(req.term, res);
},
select: function(event, ui) {
$(this).val(ui.item.value);
$(this).blur();
},
focus: function(event, ui) {
event.preventDefault();
}
});
}
})(jQuery);
Note: I accepted Dave's answer, mine is just to provide a complete answer for my purpose
The thing that confuses me somewhat and it's probably due to the conventions in
the jquery ajax() request .post() function is that it does not indicate anywhere that if request is successful that it should call the handleUpdate() function which gets the returned json object via "var json = context.get_data();", also why is the whole chunk of code starting with "if (data.ItemCount == 0)" in the handleUpdate() identical to the one in the .post() on success run > function (data) { duplicate code } .
Maybe because function (data) {} is callback function it waits for the entire request/response cycle to finish and that includes "var json = context.get_data();" in handleUpdate() ?
Thanks..
Pasted from the tutorial PDF, no other jscript in this view.
<script type="text/javascript">
$(function () {
// Document.ready -> link up remove event handler
$(".RemoveLink").click(function () {
// Get the id from the link
var recordToDelete = $(this).attr("data-id");
if (recordToDelete != '')
{
// Perform the ajax post
$.post("/ShoppingCart/RemoveFromCart", { "id": recordToDelete },
function (data) {
// Successful requests get here
// Update the page elements
if (data.ItemCount == 0)
{
$('#row-' + data.DeleteId).fadeOut('slow');
}
else
{
$('#item-count-' + data.DeleteId).text(data.ItemCount);
}
$('#cart-total').text(data.CartTotal);
$('#update-message').text(data.Message);
$('#cart-status').text('Cart (' + data.CartCount + ')');
});
}
});
});
function handleUpdate()
{
// Load and deserialize the returned JSON data
var json = context.get_data();
var data = Sys.Serialization.JavaScriptSerializer.deserialize(json);
// Update the page elements
if (data.ItemCount == 0)
{
$('#row-' + data.DeleteId).fadeOut('slow');
}
else
{
$('#item-count-' + data.DeleteId).text(data.ItemCount);
}
$('#cart-total').text(data.CartTotal);
$('#update-message').text(data.Message);
$('#cart-status').text('Cart (' + data.CartCount + ')');
}
</script>
The handleUpdate() function is a relic from the previous MVC2 version of the tutorial where the Ajax for removing items from the cart was handled by Microsoft's Ajax called via an Ajax.ActionLink helper. (see below)
This was changed to use JQuery Ajax in the MVC3 version of this tutorial but the handleUpdate() code has been left in it seems by mistake during the conversion from MVC2 to MVC3.
<script src="/Scripts/MicrosoftAjax.js" type="text/javascript"></script>
<script src="/Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>
<script src="/Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
<script type="text/javascript">
function handleUpdate(context) {
// Load and deserialize the returned JSON data
var json = context.get_data();
var data = Sys.Serialization.JavaScriptSerializer.deserialize(json);
// Update the page elements
$('#row-' + data.DeleteId).fadeOut('slow');
$('#cart-status').text('Cart (' + data.CartCount + ')');
$('#update-message').text(data.Message);
$('#cart-total').text(data.CartTotal);
}
</script>
...
<%: Ajax.ActionLink("Remove from cart", "RemoveFromCart",
new { id = item.RecordId },
new AjaxOptions { OnSuccess = "handleUpdate" })%>
There is no way (according to this code) that handleUpdate is being called on success of $.post. Jquery post function has following syntax
$.post(url,data, callback);
and in the code you can see that all three parameters are explicitly specified and callback is an anonymous function with signature
function(data){}
Now, what you can see is that this anonymous function and handleUpdate are doing exactly the same logic. That makes me believe that they belong to the two different scenarios. For example, first scenario is that links are rendered using
Html.ActionLink(LinkText, ActionName, new{#class = "RemoveLink"})
In this case click event is handled by jquery function on the top and all the logic is done in this function (including ajax and callback). Second function might have been used for some
//please confirm all parameters of the function
Ajax.ActionLink(LinkText, ActionName, new AjaxOptions{onSuccess = "handleUpdate"});
and this seems to be connected with microsoftmvc ajax files that that used to exist in ancient times. You can put alert in each function and check what is the case with you.