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.
Related
I am new to knockout and I like to have a text change value depends on a counter. Can it be done using observable or I need to do a subscription ?
I am doing udacity js design pattern course and it has not cover subscription
but when I google for similar solution of this it uses subscription
thanks for all the help!
<h3 data-bind="text: catLevel"></h2>
<div data-bind="text: clickCount"></div>
this.incrementCounter = function(){
this.clickCount(this.clickCount()+1);
//if (this.ClickCount > 10){
//this.catLevel='Infant';
//}
this.catLevel = ko.computed(function() {
if(this.clickCount>10){
return "Infant";
}else{
return "Newborn";
}
//return this.firstName() + " " + this.lastName();
}, this);
};
https://jsfiddle.net/launeric/b3929xcr/#&togetherjs=GkChVL86zO
First of all, don't put your computed definition inside your increment function. You're redefining the computed every time the function runs, which defeats the purpose of a computed.
Remember that to get the value from clickCount, you need to invoke it (an observable is a function). So your code should look like this:
this.catLevel = ko.computed(function () {
if (this.clickCount() > 10) {
return "Infant";
} else {
return "Newborn";
}
}, this);
this.incrementCounter = function () {
this.clickCount(this.clickCount() + 1);
};
var viewModel = function() {
this.name = ko.observable('Tabby');
this.clickCount = ko.observable(0);
this.catLevel = ko.observable('Newborn');
this.imgSrc = ko.observable('https://static.pexels.com/photos/33358/cat-fold-view-grey-fur.jpg');
this.imageAttribution = ko.observable('https://www.flickr.com/photos/onesharp/9648464288');
this.incrementCounter = function() {
this.clickCount(this.clickCount() + 1);
if (this.clickCount() > 10) {
console.log("brabra");
this.catLevel("inFant");
}
};
}
ko.applyBindings(new viewModel());
http://jsbin.com/tiyijer/edit?html,css,js,console,output
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;
I'm running into a peculiar issue with jQuery/JS in general.
I'm working on a JS file that let me create portlets for a client site. I'm using SignalR to communicate changes to the users.
The following piece of code gives me a bit of a headache as to why it won't work.
Eenheden: function (poPortlet) {
this.moPortlet = poPortlet;
this.Init = function () {
$(this.moPortlet).find('.portlet-title').text($(this.moPortlet).attr('data-cpv-type') + ' Eenheden')
};
this.BindHub = function () {
CPV.moHub.on('onEenheidUpdate', this.Events.Update);
};
this.Events = {
Update: function (pnId, psStatus) {
CPV.Log('Error', 'Update: ' + pnId + ' ' + psStatus);
}
};
}
I am trying to bind the function this.Events.Update on the SignalR event onEenheidUpdate. Instances of these Eenheiden objects are not unique on the pages. The idea is that they contain the same data, but can get filtered, creating a different portlet depending on some configs.
My problem is that the onEenheidUpdate function doesn't trigger the proper event. I want to do it like this so I can use references I set for the unique object, such as the jQuery object I set on initialization.
Problem
Your problem is that when jQuery or javascript in general triggers an event callback, the "context" (value of this) is changed. For example
var MyModule = {
init: function () {
$('.amazing-element').on('click', function () {
// This breaks because `
// `this` is not MyModule
// When the callback is triggered
// `this` is `<p class="amazing-element"></p>`
this.onClick()
})
},
onClick: function () {
console.log('wee, I was clicked')
}
}
Function.prototype.bind to the rescue!
var MyModule = {
init: function () {
$('.amazing-element').on('click', function () {
this.onClick()
}.bind(this))
// ^ here we bind the context of the callback to the
// correct version of `this`.
},
onClick: function () {
console.log('wee, I was clicked')
}
}
So your exact example would look like:
Eenheden: function (poPortlet) {
this.moPortlet = poPortlet;
this.Init = function () {
$(this.moPortlet).find('.portlet-title').text($(this.moPortlet).attr('data-cpv-type') + ' Eenheden')
};
this.BindHub = function () {
CPV.moHub.on('onEenheidUpdate', this.Events.Update.bind(this));
// ^ change here
};
this.Events = {
Update: function (pnId, psStatus) {
CPV.Log('Error', 'Update: ' + pnId + ' ' + psStatus);
}
};
}
I'm writing an interface for user settings in a Chrome extension. Here's how I define the settings:
function Setting(label, type, defaultData) {
this.label = label;
this.type = 'checkbox';
this.defaultData = defaultData;
}
var Settings = {};
Settings['setting-one'] = new Setting('Setting one', 'checkbox', 'true');
Settings['setting-two'] = new Setting('Setting two', 'checkbox', 'true');
Here's how I set the default value for each setting:
function setDefaultSetting(setting, defaultValue) {
chrome.storage.sync.get(setting, function(results) {
if (!results[setting]) {
// If the setting has not yet been defined, define it now
var dataToSave = {};
dataToSave[setting] = defaultValue;
chrome.storage.sync.set(dataToSave, function() {
debugMsg('set `' + setting + '` to ' + defaultValue);
});
}
});
}
for (var setting in Settings) {
if (Settings.hasOwnProperty(setting)) {
var s = Settings[setting];
if (s.type == 'checkbox') {
setDefaultSetting(setting, s.defaultData);
}
}
}
So far, so good. Now I want to print the list of settings as checkboxes. Here's what I've tried:
function printSettingsModal() {
var output += '<form>';
for (var setting in Settings) {
if (Settings.hasOwnProperty(setting)) {
var s = Settings[setting];
if (s.type == 'checkbox') {
chrome.storage.sync.get(setting, function(results) {
output += '<p><input id="setting-' + setting + '" type="checkbox"';
if (results[setting] == 'true') { output += ' checked="checked"'; }
output += '><label for="setting-' + setting + '">' + s.name + '</label></p>';
});
}
}
}
output += '</form>';
return output;
}
This doesn't work because chrome.storage.sync.get() is asynchronous. How can I loop through my array of settings and retrieve the associated chrome.storage data for each setting?
You don't actually have to do that sequentially, or even set defaults explicitly beforehand.
chrome.storage.local.get has 3 forms for its first argument:
"key" (string): retrieves just one key, as you're doing
["key1", "key2"] (array): retrieves all the values for keys in the array
{ key1: default1, key2: default2 } (dictionary object): retrieves all the values for keys specified, returning the provided default value if it's not in storage
You can collect all the settings you want to retrieve and get them in one operation.
A simple way to do it is to use a recursive function, add each option inside the callback of the chrome.storage.sync.get() method, and call the printSettingsModal() function increasing the index. To do this you'll need an array structure to at least store the name of the settings so you can iterate over it, you can edit your Settings object and add the method add() that will do this for you. You can then call the chrome.storage.sync.get() method recursively and call a callback when finished.
So, first, here is your new Settings object:
function Setting(label, type, defaultData) {
this.label = label;
this.type = 'checkbox';
this.defaultData = defaultData;
}
function Settings() {
var _this = this;
this.list = [];
this.add = function(key, obj) {
this.list.push(key);
_this[key] = obj;
}
}
var mySettings = new Settings();
mySettings.add('setting-one', new Setting('Setting one', 'checkbox', 'true'));
mySettings.add('setting-two', new Setting('Setting two', 'checkbox', 'true'));
Now you will have something like this:
console.log(mySettings);
Settings {list: Array[2], add: function, setting-one: Setting, setting-two: Setting}
Here comes the recursive function, you will need to:
Initialize the output as "<form>" at the beginning.
Add all the settings in the intermediate steps.
Finalize the output adding "</form>" at the end.
Call a callback with the output so you can use it properly.
Here is an example of what your function will look like using recursion:
function printSettingsModal(index, output, callback) {
var s = mySettings[mySettings.list[index]];
if (index === 0) {
// first element is going to be created
output = "<form>";
} else if (index > mySettings.list.length-1) {
// the last setting has been added
// call the callback
callback(options);
}
if (s.type == 'checkbox') {
chrome.storage.sync.get(setting, function(results) {
output += '<p><input id="setting-' + setting + '" type="checkbox"';
if (results[setting] == 'true') { output += ' checked="checked"'; }
output += '><label for="setting-' + setting + '">' + s.name + '</label></p>';
if (index === mySettings.list.length-1) {
// last element has been created
output += '</form>';
printSettingsModal(++index, output, callback);
}
});
}
}
Now you just need to define a callback and call it:
function myCallback(options) {
// do something with the option string
// like:
document.getElementById('form-container').innerHTML = options;
}
printSettingsModal(0, null, myCallback);
If I wrote all correctly, this will work for you. Hope it helped.
I cannot find an proper example for the love of my life on how to do this or even if this is possible. Based on my pieced together understanding from fragments of exmaples, I have come up with the following structure
var t = function()
{
this.nestedOne = function()
{
this.nest = function()
{
alert("here");
}
}
}
t.nestedOne.nest();
However this is not working (obviously). I would greatly appreciate if someone could point me in the right direction!
That is simply done with:
var t = {
nestedOne: {
nest: function() {
alert('here');
}
}
};
Your code otherwise doesn't make sense. this inside function doesn't refer to the function itself, it refers to the object context that the function is invoked in. And you are not even invoking the functions in your code.
If I say obj.func() then this inside func will be obj for that call. So assigning this.asd = true will assign true to that object's "asd" property.
If you wanted to do a nested class, it looks very different:
ClassA = (function() {
function ClassA() {
}
ClassA.prototype.method1 = function() {
};
function ClassB() {
}
ClassB.prototype.method1 = function() {
};
return ClassA;
}())
only ClassA can now make instances of ClassB. This should achieve same goals as nested classes in java.
See http://jsfiddle.net/CstUH/
function t(){
function f(){
this.nest = function()
{
alert("here");
}
}
this.nestedOne = new f();
}
var myt=new t();
myt.nestedOne.nest()
Edit 1:
You can also use
new t().nestedOne.nest()
instead of
var myt=new t();
myt.nestedOne.nest()
(http://jsfiddle.net/CstUH/1/)
Edit 2:
Or even more condensed:
function t(){
this.nestedOne = new function(){
this.nest = function(){
alert("here");
}
}
}
new t().nestedOne.nest()
http://jsfiddle.net/CstUH/2/
In JS functions are prime class objects, and you can access them directly in the code [i.e. without using reflection or so].
The code you put inside t body would be performed when actually executing t:
t();
You wrote t.nestedOne,nest(), but t has no nestedOne property - you should do like this:
var t = {
nestedOne : {
nest : function()
{
alert("here");
}
}
};
t.nestedOne.nest();
I advice you to have a trip on John Resig's Learning Advanced JavaScript tutorial, it was very enlightening for me.
A simple callback handler I wrote today as an example of how I do deep nesting. I apologize if it's not the bees knees when it comes to code style, it made the concept a little clearer for me.
function test () {
this.that = this;
this.root = this;
this.jCallback = new Array(new Array()); // 2d
this.jCallbackCount = -1;
this.str = "hello";
// Callback handler...
this.command = {
that : this, // let's keep a reference to who's above us on the food chain
root : this.root, // takes us back to the main object
// add : function() { var that = this; console.log(that.that.str); },
add : function(targetFnc, newFunc) {
var that = this;
var home = that.that; // pretty much root but left in as an example of chain traversal.
var root = this.root; // useful for climbing back up the function chain
// console.log(that.that.str);
home.jCallbackCount++;
// target, addon, active
home.jCallback[home.jCallback.length] = { 'targetFunc' : targetFnc, 'newFunc' : newFunc, 'active' : true, 'id': home.jCallbackCount};
console.log('cbacklength: ' + home.jCallback.length);
console.log('added callback targetFunction:[' + targetFnc + ']');
return home.jCallbackCount; // if we want to delete this later...
},
run : function(targetFnc) {
var that = this;
var home = that.that;
console.log('running callback check for: ' + targetFnc + ' There is : ' + (home.jCallbackCount + 1) + 'in queue.');
console.log('length of callbacks is ' + home.jCallback.length);
for(i=0;i < home.jCallback.length - 1;i++)
{
console.log('checking array for a matching callback [' + targetFnc + ']...');
console.log('current item: ' + home.jCallback[i]['targetFunc'] );
if( home.jCallback[i]['targetFunc'] == targetFnc )
{
// matched!
home.jCallback[i]['newFunc']();
}
// console.log(that.that.jCallback[i].targetFunction);
}
}
};
}
test.prototype = {
say : function () {
var that = this;
console.log('inside');
// that.command('doSay');
that.command.run('doSay');
console.log(that.str);
}
} // end proto
// BEGIN TESTING **************************************************************************
// BEGIN TESTING **************************************************************************
// BEGIN TESTING **************************************************************************
var testing = new test();
testing.command.add('doSay', function () { console.log('213123123'); } );
testing.command.add('doSay', function () { console.log('12sad31'); } );
testing.command.add('doSay', function () { console.log('asdascccc'); } );
testing.say();
live:
http://jsfiddle.net/Ps5Uf/
note: to view console output, just open inspector in chrome and click on the "console" tab.