In my Nativescript app I have a loop and want to display a dialog for each item being iterated over. When the dialog displays it contains "Accept" and "Reject" options, both of which when clicked I would like to call a method which I pass the iterated item into. The issue is since the option selection returns a promise I lose the reference to the iterated item. What can I do to get around this? Here's an example of my code.
EDIT: I also really don't like that I'm declaring a function in the loop after the promise returns.
function _showPendingConnections() {
for (var i = 0; i < ViewModel.pendingConnections.length; i++) {
var pendingConnection = ViewModel.pendingConnections[i];
dialog.confirm({
message: pendingConnection.PatientFirstName + " would like to share their glucose readings with you.",
okButtonText:"Accept",
cancelButtonText:"Reject"
}).then(function(result) {
if(result === true) {
ViewModel.acceptConnection(pendingConnection);
} else {
ViewModel.removeConnection(pendingConnection);
}
});
}
}
The following change worked for me (I have probably created different viewModel but however the idea is the same) - all I have done is to change when your item index is passed.
For example:
// main-page.js
"use strict";
var main_view_model_1 = require("./main-view-model");
var dialogModule = require("ui/dialogs");
var viewModel = new main_view_model_1.MyViewModel();
viewModel.pendingConnections = [{ PatientFirstName: "John" }, { PatientFirstName: "Merry" }, { PatientFirstName: "Abygeil" }];
// Event handler for Page "navigatingTo" event attached in main-page.xml
function navigatingTo(args) {
// Get the event sender
var page = args.object;
page.bindingContext = viewModel;
for (var index = viewModel.pendingConnections.length - 1; index >= 0; index--) {
connectionDealer(index);
}
}
exports.navigatingTo = navigatingTo;
function connectionDealer(index) {
var pendingConnection = viewModel.pendingConnections[index];
dialogModule.confirm({
message: pendingConnection["PatientFirstName"] + " would like to share their glucose readings with you.",
okButtonText: "Accept",
cancelButtonText: "Reject"
}).then(function (result) {
if (result === true) {
// your code follow.. pass pendingConnection[index] to your method
console.log("accepted by " + pendingConnection["PatientFirstName"]);
}
else {
// your code follow.. pass pendingConnection[index] to your method
console.log("Rejected by " + pendingConnection["PatientFirstName"]);
}
});
}
// main-view-model.js
"use strict";
var observable = require("data/observable");
var MyViewModel = (function (_super) {
__extends(MyViewModel, _super);
function MyViewModel() {
_super.apply(this, arguments);
}
Object.defineProperty(MyViewModel.prototype, "pendingConnections", {
get: function () {
return this._pendingConnections;
},
set: function (value) {
if (this._pendingConnections !== value) {
this._pendingConnections = value;
}
},
enumerable: true,
configurable: true
});
return MyViewModel;
}(observable.Observable));
exports.MyViewModel = MyViewModel;
Related
So i'm trying to make two functions that allow a user to move an item from their cart to the "saved cart" and vise versa. These functions depend on the "cart group item" module which also contains the events for the button clicks. My question is, i'm unsure how to correctly call these functions to allow the click event to take place in my current js file. Hopefully someone can help!
Event's in module:
cartGroupItem.prototype.createMoveEvent = function (elem) {
if (undefined !== elem && null !== elem) {
var button = elem.querySelector('.cartGroupItem__move');
if (button !== null) {
button.addEventListener('click', function (e) {
ajax('GET',
'/resources/ajax/cart.aspx?sub=3&Saved=0&Cart_Group_ID='+this.cartGroupId,
true, {}, function () {
this.moveToCartCallback();
}.bind(this), function () {
this.showErrorDiv();
}.bind(this));
}.bind(this));
}
}
};
cartGroupItem.prototype.createSaveEvent = function (elem) {
if (undefined !== elem && null !== elem) {
var button = elem.querySelector('.cartGroupItem__save');
if (button !== null) {
button.addEventListener('click', function (e) {
ajax('GET',
'/resources/ajax/cart.aspx?sub=3&Saved=1&Cart_Group_ID='+this.cartGroupId,
true, {}, this.saveForLaterCallback.bind(this), this.showErrorDiv.bind(this));
}.bind(this));
}
}
};
Move functions:
function moveToSaved(cartGroupId) {
for (var i = 0, l = activeCartList.length; i < l; i++) {
if (activeCartList[i].cartGroupId === cartGroupId) {
activeCartList.remove();
savedCartList.push(activeCartList[i]);
}
}
}
function moveToActive(cartGroupId) {
for (var i = 0, l = savedCartList.length; i < l; i++) {
if (savedCartList[i].cartGroupId === cartGroupId) {
savedCartList.remove();
activeCartList.push(savedCartList[i]);
}
}
}
Your Event module probably define the function cartGroupItem right?
What you need to do is pass this function from its file to your current file, and then "instanciate" a carteGroupItem:
// In your current JS file
var myCartGroupItem = new cartGroupItem();
myCartGroupItem.createMoveEvent();
myCartGroupItem.createSaveEvent();
We also would need to see this function "constructor" (where it is defined) as it probably takes a few callbacks as parameters. Otherwise you can add them manually:
myCartGroupItem.moveToCartCallback = function() {
// do what you want when the item is moved to cart
};
// Same for the other callbacks
myCartGroupItem.saveForLaterCallback = function() { ... };
myCartGroupItem.showErrorDiv = function() { ... };
Finally, the way to pass things with RequireJS is that for instance, your Event module returns cartGroupItem so in your file module:
define(['Event'], function(cartGroupItem) {
// Your code where you can use cartGroupItem
});
I have service called "sharedData" with some functions, how to call one of these functions from another such functions? here the code(marked trouble functions with "???????"): Thanks
service('sharedData', function ($http) {
var refillList = [];
var orderCart = {
orderPlace: null,
orderList: [],
totalSum: 0
};
return {
....
addRefill: function(value) {
...here some logic....
},
addOrder: function(order) {
...here some logic....
},
sendOrder: function(order, refill) {
$http.get(config.urls.ajaxOrder + "{\"order\":{\"table_id\":" + orderCart.orderPlace + ",\"item_id\":" + order.id + ",\"amount\":1,\"action\":1}}").success(function(dataDetails) {
if (dataDetails.success) {
if (refill == 1) {
// Filling refill list
??????????????????this.addRefill(order);?????????
}
// Filling order cart
?????????this.addOrder(order);?????????????
}
});
}
};
}).
You should save reference to this.
var self = this; is a common practice.
sendOrder: function(order, refill) {
var self = this;
$http.get(config.urls.ajaxOrder + "{\"order\":{\"table_id\":" + orderCart.orderPlace + ",\"item_id\":" + order.id + ",\"amount\":1,\"action\":1}}")
.success(function(dataDetails) {
if (dataDetails.success) {
if (refill == 1) {
// Filling refill list
self.addRefill(order);
}
// Filling order cart
self.addOrder(order);
}
}
});
}
Update 2016
Now, with ES6 you can use arrow functions like this:
sendOrder: function(order, refill) {
$http.get(config.urls.ajaxOrder + "{\"order\":{\"table_id\":" + orderCart.orderPlace + ",\"item_id\":" + order.id + ",\"amount\":1,\"action\":1}}")
.success(dataDetails => {
if (dataDetails.success) {
if (refill == 1) {
// Filling refill list
this.addRefill(order);
}
// Filling order cart
this.addOrder(order);
}
}
});
}
Arrow functions doesn't change a context, so this will be the same this.
MDN article about arrow functions
The problem is that this in this callback function refers to the parent container of the callback which is $http in this case. What you'll want to do is create an instance of the parent object outside of the callback and reference that from within the callback.
Something like:
....
{
....
addRefill: function(value) {
...here some logic....
},
addOrder: function(order) {
...here some logic....
},
sendOrder: function(order, refill) {
var rootObj = this;
$http.get(config.urls.ajaxOrder + "{\"order\":{\"table_id\":" + orderCart.orderPlace + ",\"item_id\":" + order.id + ",\"amount\":1,\"action\":1}}").success(function(dataDetails) {
if (dataDetails.success) {
if (refill == 1) {
// Filling refill list
rootObj.addRefill(order);
}
// Filling order cart
rootObj.addOrder(order);
}
});
}
};
....
This is of course just a solution but the main concept to keep in mind is that the function is being called from the success promise not from your object.
I'm writing a lightweight jQuery plugin to detect dirty forms but having some trouble with events. As you can see in the following code, the plugin attaches an event listener to 'beforeunload' that tests if a form is dirty and generates a popup is that is the case.
There is also another event listener attached to that form's "submit" that should in theory remove the 'beforeunload' listener for that specific form (i.e. the current form I am submitting should not be tested for dirt, but other forms on the page should be).
I've inserted a bunch of console.log statements to try and debug it but no luck. Thoughts?
// Checks if any forms are dirty if leaving page or submitting another forms
// Usage:
// $(document).ready(function(){
// $("form.dirty").dirtyforms({
// excluded: $('#name, #number'),
// message: "please don't leave dirty forms around"
// });
// });
(function($) {
////// private variables //////
var instances = [];
////// general private functions //////
function _includes(obj, arr) {
return (arr._indexOf(obj) != -1);
}
function _indexOf(obj) {
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (obj, fromIndex) {
if (fromIndex == null) {
fromIndex = 0;
} else if (fromIndex < 0) {
fromIndex = Math.max(0, this.length + fromIndex);
}
for (var i = fromIndex, j = this.length; i < j; i++) {
if (this[i] === obj)
return i;
}
return -1;
};
}
}
////// the meat of the matter //////
// DirtyForm initialization
var DirtyForm = function(form, options) {
// unique name for testing purposes
this.name = "instance_" + instances.length
this.form = form;
this.settings = $.extend({
'excluded' : [],
'message' : 'You will lose all unsaved changes.'
}, options);
// remember intial state of form
this.memorize_current();
// activate dirty tracking, but disable it if this form is submitted
this.enable();
$(this.form).on('submit', $.proxy(this.disable, this));
// remember all trackable forms
instances.push(this);
}
// DirtyForm methods
DirtyForm.prototype = {
memorize_current: function() {
this.originalForm = this.serializeForm();
},
isDirty: function() {
var currentForm = this.serializeForm();
console.log("isDirty called...")
return (currentForm != this.originalForm);
},
enable: function() {
$(window).on('beforeunload', $.proxy(this.beforeUnloadListener, this));
console.log("enable called on " + this.name)
},
disable: function(e) {
$(window).off('beforeunload', $.proxy(this.beforeUnloadListener, this));
console.log("disable called on " + this.name)
},
disableAll: function() {
$.each(instances, function(index, instance) {
$.proxy(instance.disable, instance)
});
},
beforeUnloadListener: function(e) {
console.log("beforeUnloadListener called on " + this.name)
console.log("... and it is " + this.isDirty())
if (this.isDirty()) {
e.returnValue = this.settings.message;
return this.settings.message;
}
},
setExcludedFields: function(excluded) {
this.settings.excluded = excluded;
this.memorize_current();
this.enable();
},
serializeForm: function() {
var blacklist = this.settings.excludes
var filtered = [];
var form_elements = $(this.form).children();
// if element is not in the excluded list
// then let's add it to the list of filtered form elements
if(blacklist) {
$.each(form_elements, function(index, element) {
if(!_includes(element, blacklist)) {
filtered.push(element);
}
});
return $(filtered).serialize();
} else {
return $(this.form).serialize();
}
}
};
////// the jquery plugin part //////
$.fn.dirtyForms = function(options) {
return this.each(function() {
new DirtyForm(this, options);
});
};
})(jQuery);
[EDIT]
I ended up fixing this by using jQuery's .on() new namespace feature to identify the handler. The problem was that I was passing new anonymous functions as the handler argument to .off(). Thanks #FelixKling for your solution!
this.id = instances.length
[...]
enable: function () {
$(window).on('beforeunload.' + this.id, $.proxy(this.beforeUnloadListener, this));
},
disable: function () {
$(window).off('beforeunload.' + this.id);
},
Whenever you are calling $.proxy() it returns a new function. Thus,
$(window).off('beforeunload', $.proxy(this.beforeUnloadListener, this));
won't have any effect, since you are trying to unbind a function which was not bound.
You have to store a reference to the function created with $.proxy, so that you can unbind it later:
enable: function() {
this.beforeUnloadListener = $.proxy(DirtyForm.prototype.beforeUnloadListener, this);
$(window).on('beforeunload', this.beforeUnloadListener);
console.log("enable called on " + this.name)
},
disable: function(e) {
$(window).off('beforeunload', this.beforeUnloadListener);
console.log("disable called on " + this.name)
},
I want to test this function:
/js/lib/front.js
var Front = function(){
this.onSignUp = function(){
if (!Form.assertInput("email")) {
$("input[name=email]").focus();
this.showHiddenMessage("Email not set.");
return false;
}
}
}
I have in:
/js/lib/form.js
function Form() {
this.assertInput = function (name, defaultValue) {
var text = $("input[name=" + name + "]").val();
if (defaultValue != null) {
if (defaultValue && text == defaultValue)
return false;
}
if(this.trim(text)) return true;
return false;
}
}
This simple test passing:
test("Front", function() {
var front = new Front()
ok(front);
});
But if I write something like this:
test("On Sign Up ", function() {
var front = new Front()
equal(front.onSignUp(),false,"passing test");
});
I have error:
Died on test #1: Form.assertInput is not a function
I don't understand, what I need test in function like this and how include function inside another function?
I've saved a working fiddle here. As a side note, you might want to check out a tutorial on using qUnit, here.One thing that you need to pay attention to is when you're declaring your functions. It's saying Form.assertInput is not a function because you can't access it like that. You need to use the this keyword, which refers to current context. The code should be something like this:
var Form = function () {
//good to have assertInput first if you're using it in a later function
this.assertInput = function (name, defaultValue) {
var text = $("input[name=" + name + "]").val();
if (defaultValue != null) {
//safer to explicitly close your if statements with {}
if (defaultValue && text == defaultValue) {
return false;
}
}
if ($.trim(text)) { return true; }
return false;
};
this.showHiddenMessage = function (message) {
alert(message);
};
this.onSignUp = function() {
//this will point to the current context, in this case it will be Form class
if (!this.assertInput("email")) {
$("input[name=email]").focus();
this.showHiddenMessage("Email not set.");
return false;
}
};
};
Also in the example code that you gave you're missing the Front class. So I created a dummy one in my fiddle like this:
var Front = function() {};
Here are the tests that were run:
$(document).ready(function() {
test("Front", function() {
var front = new Front();
ok(front);
});
test("On Sign Up ", function() {
var form = new Form();
equal(form.onSignUp(), false, "passing test");
});
});
I have a json object retrieved from server in my $(document).ready(...); that has an string that I would like to resolve to a function also defined within $(document).ready(...); so, for example:
$(document).ready(function{
$.getJSON(/*blah*/,function(data){/*more blah*/});
function doAdd(left,right) {
return left+right;
}
function doSub(left,right) {
return left-right;
}
});
with json string:
{"doAdd":{"left":10,"right":20}}
One way I thought about was creating an associative array of the function before loading the json:
var assocArray=...;
assocArray['doAdd'] = doAdd;
assocArray['doSub'] = doSub;
Using eval or window[](); are no good as the function may not be called for some time, basically I want to link/resolve but not execute yet.
Change your JSON to
{method: "doAdd", parameters : {"left":10,"right":20}}
Then do
var method = eval(json.method);
// This doesn't call it. Just gets the pointer
Or (haven't tried this)
var method = this[json.method]
How about something like this?
$(function(){
// Function to be called at later date
var ressolvedFunc = null;
// Ajax call
$.getJSON(/*blah*/,function(data){
// Generate one function from another
ressolvedFunc = (function(data) {
var innerFunc;
var left = data.left;
var right = data.right;
// Detect action
for (action in data) {
if (action == "doAdd")
innerFunc = function() {
return left + right;
};
else
innerFunc = function() {
return left - right;
};
}
return innerFunc;
})(data);
});
});
The anonymous function returns fresh function, with the new values stored within the enclosure. This should allow you to call the function at later date with the data previously retrieved from the GET request.
Rich
try this:
var doX = (function() {
var
data = [],
getDo = function(action) {
for(var d in data) {
if (data[d][action]) {
return data[d];
}
}
return null;
};
return {
set: function(sdata) {
data.push(sdata);
},
doAdd: function() {
var add = getDo("doAdd");
if (!add)
return 0;
return add.doAdd.left + add.doAdd.right;
},
doSub: function() {
var sub = getDo("doSub");
if (!sub)
return 0;
return sub.doAdd.left + sub.doAdd.right;
}
};
})();
$(document).ready(function{
$.getJSON(/*blah*/,function(data){ doX.set(data); });
});