knockout.js text change depends on counter - javascript

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

Related

What is the difference between returning a function and returning an object in a Durandal viewmodel?

I'm looking at implementing a wizard type system in my application and looking at the first wizard example on the dfiddle-2.0 project on GitHub. The step viewmodels are all functions though and I'm trying to understand why.
Here is what the dfiddle is using for the index.js of the wizard:
define(['durandal/activator', './step1', './step2', './step3', 'knockout'], function( activator, Step1, Step2, Step3, ko ) {
var steps = [new Step1(), new Step2(), new Step3()];
var step = ko.observable(0);
var activeStep = activator.create();
var stepsLength = steps.length;
var hasPrevious = ko.computed(function() {
return step() > 0;
});
var hasNext = ko.computed(function() {
return (step() < stepsLength - 1);
});
// Start with first step
activeStep(steps[step()]);
return {
showCodeUrl: true,
steps: steps,
step: step,
activeStep: activeStep,
next: next,
previous: previous,
hasPrevious: hasPrevious,
hasNext: hasNext
};
function next () {
if ( step() < stepsLength ) {
step(step() + 1);
activeStep(steps[step()]);
}
}
function previous () {
if ( step() > 0 ) {
step(step() - 1);
activeStep(steps[step()]);
}
}
});
And here is what it's using for step1.js
define(function() {
return function() {
this.name = 'Step 1';
this.s1one = 'Unique to' + this.name;
this.s1two = 'Another property unique to' + this.name;
};
});
Here is what I'm currently using for index.js.
define(['knockout'],
function (ko) {
var rootPath = "viewmodels/wizards/steps/";
var steps = ["step1", "step2", "step3"];
var step = ko.observable(0);
var activeStep = ko.observable();
var stepLength = steps.length;
var hasPrevious = ko.computed(function () { return step() > 0 });
var hasNext = ko.computed(function () { return step() < stepLength - 1 });
var activate = function () {
return activeStep(rootPath + steps[step()]);
};
return {
steps: steps,
step: step,
activeStep: activeStep,
next: next,
previous: previous,
hasPrevious: hasPrevious,
hasNext: hasNext,
activate: activate
}
function next() {
if (hasNext()) {
step(step() + 1);
activeStep(rootPath + steps[step()]);
}
}
function previous() {
if (hasPrevious()) {
step(step() - 1);
activeStep(rootPath + steps[step()]);
}
}
});
And my step1.js
define(function () {
var name = ko.observable("Step 1");
var s1one = ko.observable("Unique to " + name());
var s1two = ko.observable("Another property unique to " + name());
var returnVm = {
name: name,
s1one: s1one,
s1two: s1two
};
return returnVm;
});
The bindings are the same so how are these two approaches different? What am I losing by just returning an object instead of using functions?
The difference is subtle, but important. Modules that return an object are singletons. The same object will be shared among all other modules that depend on it. Modules that return a function are termed constructor functions. Dependant modules will instantiate this constructor function with the new keyword. Therefore, each instance is unique.
Here's some more information gleaned from the Durandal documentation:
A module's define is only exeucted once, at the time the module is first required. As a result, if you return an object instance, you have created a singleton which will stay in memory for the lifetime of your application. If this is not desired, return a constructor function to retain greater control of the lifetime of your objects by allowing consumers to create/release them as needed.
In your example, you aren't losing anything. Either approach works. Which is more correct depends on a number of things. If you do not require unique instances of your module each time it is required, then a singleton is the best choice. However, if say you need multiple instances of the same dialog module, but each with their own data, a constructor function is the way to go.
I hope this helps.

privately encapsulate its object data

Please Advise. Completing http://testfirst.org/learn_javascript - stuck on 07_temperature last spec:
Here is my code:
function Temperature(value){
this.fahrenheit = function(){
if(this.fahrenheit_value===undefined){
this.fahrenheit_value = Math.fround(this.celsius_value*1.8 + 32,2);
}
return this.fahrenheit_value;
};
this.setFahrenheit = function(value){
this.fahrenheit_value = value;
};
this.celcius = function(){
if(this.celsius_value===undefined){
this.celsius_value = Math.round((this.fahrenheit_value-32)/1.8);
}
return this.celsius_value;
};
this.setCelcius= function(value){
this.celsius_value = value;
};
}
And the spec I am trying to pass is as follows, I am not sure how to pass the last one "privately encapsulates its data".
describe("the Temperature object", function() {
it("privately encapsulates its data", function() {
var temperature;
temperature = new Temperature(32);
for (var property in temperature) {
// This assures that there are no data values at all on the object, just methods
console.log(temperature[property]);
expect(typeof(temperature[property])).toEqual('function');
}
});
});

backbone.js this.[method] is not a function

I'm having an issue with a helper function inside my Backbon.js View. When it's run, it dies with the following error message about the first line of the "addCalc" function:
TypeError: this.getCalcValue is not a function
It's really puzzling because in the "initialize" function defined just above, all the functions seem to be defined. It feels like I'm calling the sibling method wrong, and the "initialize" method is an exception where "this" can be used to reference the object.
Is there something wrong/missing with the following code, or something I missed with the backbone documentation?
CalcView = Backbone.View.extend({
el: $("#calcView"),
initialize: function () {
this.resetCalc();
},
addCalc: function (model) {
var cost = this.getCalcValue(model.get('currentCost'));
var custom = this.getCalcValue(model.get('customProgram'));
var variables = { id: model.get('id'),
category: model.get('category'),
shortDesc: model.get('shortDescription'),
description: model.get('description'),
currentCost: cost,
customProgram: custom,
};
var template = _.template($('#calc_template').html(), variables);
$("#calc_payload").append(template);
},
resetCalc: function(models) {
$("#calc_payload tr").remove();
},
removeCalc: function(model){
$("#calc_payload #" + model.get('id')).remove();
},
updateCalcs: function(model) {
var cost = model.get('currentCost');
var custom = model.get('customProgram');
$("#" + model.get("id") + " .currentCost").text(this.getCalcValue(cost));
$("#" + model.get("id") + " .customProgram").text(this.getCalcValue(custom));
/*var currentCostSum = 0;
var customProgramSum = 0;
$("#calc_payload .currentCost").each(function() {
var temp = Number(($(this).text()).replace(/[^0-9\.]+/g, ""));
if (!isNaN(temp))
currentCostSum += temp;
});
$("#calc_payload .customProgram").each(function() {
var temp = Number(($(this).text()).replace(/[^0-9\.]+/g, ""));
if (!isNaN(temp))
customProgramSum += temp;
});
$("#calc_footer .currentCost").text("$" + ((currentCostSum == 0) ? " -- " : CurrencyFormatted(currentCostSum.toFixed(2))));
$("#calc_footer .customProgram").text("$" + ((customProgramSum == 0) ? " -- " : CurrencyFormatted(customProgramSum.toFixed(2))));*/
},
getCalcValue: function(value) {
if (typeof value == 'string' || value instanceof String)
return value.toString();
else if (isNaN(value))
return "$ -- ";
else
return "$" + value.toFixed(2);
},
});
The code that executes the "addCalc" function is driven by a backbone collection. Basically, when the collection is added to, the CalcView.addCalc is called
Calculations = Backbone.Collection.extend({
model: Calculation,
//This is our Friends collection and holds our Friend models
initialize: function (models, options) {
this.on("add", options.onAdd);
this.on("remove", options.onRemove);
this.on("reset", options.onReset);
//Listen for new additions to the collection and call a view function if so
}
});
//This is where addCalc is used.
var calcview = new CalcView();
var calc_collection = new Calculations( null, {
onAdd: calcview.addCalc,
onRemove: calcview.removeCalc,
onReset: calcview.resetCalc
});
In your initialize function add this line of code:
_.bindAll(this,'addCalc');
This will bind this to be your CalcView for the addCalc function. You can put multiple comma separated method names in there if you need to bind more than one function...
See Underscore's documentation on it here.
When you bind events on collection you can send the context as third argument. Try sending one more option property as your calcview and pass it as context.
this.on("add", options.onAdd, options.calcview);
this.on("remove", options.onRemove, options.calcview);
this.on("reset", options.onReset, options.calcview);

Angular, call service function from current service

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.

Deep nesting functions in JavaScript

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.

Categories