What is the best way to identify the updater of an observable - javascript

Given any knockout example, I want to be able to identify the source of an observable update.
Consider for example this simple code:
HTML
<input type='text' data-bind='value: someValue' />
<span data-bind='text: someValue'></span>
<button data-bind='click: updateValue'>Update</button>
js
var vm = function () {
var self = this;
this.someValue = ko.observable('random value');
this.updateValue = function () {
self.someValue('random value ' + Math.round(Math.random()*10));
}
}
var vmi = new vm();
vmi.someValue('other random value');
ko.applyBindings(vmi);
Fiddle
I want to know if someValuewas last updated via the input tag, the button, or via code.
What do you think would be the best way to do so? (extender, custom binding handler, ...)

Create separate "writable computed observables" for each of those things to modify. See http://knockoutjs.com/documentation/computedObservables.html#writeable_computed_observables. In your write methods for each of these, you can handle differentiation/coordination/syncing between them as appropriate. There might be a better solution though if you described your actual scenario.

Related

$watch or ng-model binding issue?

I basically have a very complicated set of tabs with various input controls but lets not worry about this for now. For now let consider simple input wtching issue I am baffled by.
For example:
<input type="text" placeholder="Associate some tags please" data-ng-model="tag" data-ng-maxlength="250">
I am try to detect if user has typed something into my input:
$scope.$watch('tag', function () {
//$scope.$watchCollection('tag', function () {
console.log('tag changed');
});
But I seem to get zero response. However, when I perform my save operation I always seem to get the value user typed in.
Is a case of ng-model not binding correctly or is it that I am not $watching it correctly?
Also after I've performed my save operation I try to clear what user typed in for the tag with:
$scope.tag = '';
But that doesn't seem to work for some reason as though $scope.tag doesn't exist.
PS: When I say save operation I am actually performing a array.push() into an object which later gets saved by a service.
For example:
$scope.checkSaveTag = function (tag) {
...
// checked for duplicate tag beforehand
$scope.myForm.Tags.push(tagObj); // complicated form object
$scope.tag = ''; // tag input control
...
};
Any chance that the tag is an object or an array? If that is the case, you'll need to do a deep $watch, e.g:
$scope.$watch('tag', function () {
console.log('tag changed');
}, true);
Try like this
Controller
$scope.form={
tag:''
}
$scope.$watch("form.tag",function(newVal,oldVal){
console.log(newVal);
})
Html
<input type="text" placeholder="Associate some tags please" data-ng-model="form.tag" data-ng-maxlength="250">

Not able to push values into an observavblearray

I cannot understand why this small code is not working properly.Please help!
All i try is to push textbox values into an array and then then display it back using a binding.
HTML code is :
<div>
Add Task:<input type="text" placeholder="abcd" data-bind="value:viewModel.newTask"/>
<input type="button" value="add" data-bind="click:viewModel.addTask" />
</div>
<div data-bind="foreach:viewModel.tasks" ></div>
js script is:
var viewModel = function (items) {
var self = this;
self.newTask = ko.observable();
self.tasks = ko.observableArray(items);
self.addTask = function () {
self.tasks().push(self.newTask());
self.newTask(" ");
}
ko.applyBindings(viewModel(["alpha","beta","gamma"]));
}
i tried it in JSFiddle also:
http://jsfiddle.net/Rakz_1221/m3rwupmz/1/
First off, you haven't included KnockoutJS in your JSFiddle demo, so your demo will not work anyway.
The problem you have is that you're calling tasks.push. tasks itself isn't an array. tasks is a function - a Knockout Observable. In order to push values into it, we need to execute this function by calling tasks().push() instead.
As James Thrope has commented, you can in fact call tasks.push, sorry about that!
Finally, your ko.applyBindings(...) is never being called as it's contained within your viewModel function - which you're not calling at any point.
I strongly advise you to go through KnockoutJS's very own interactive tutorial available at http://learn.knockoutjs.com.

Getting Slider Value - d3/dc

I am currently using dc to make a few line graphs. I also just added in a slider(i played with d3.slider, but then found a different solution)
I am hoping to use the slider to filter the other graphs. Does anyone have suggestions on how to get the selected value from the slicer, and then filter?
To filter I was thinking something like:
d3.select("input[type=range]").on("change", function() {
var gendersel = this.value;
});
var data = _.where(data2, {variable: gendersel});
However, this doesn't work...
I'm also thinking about using crossfilter (probably smarter) for filtering, but still having issues getting the slider value:
var bytest = ndx.dimension(function(d) { return d.testvar; });
bytest.filter([gendersel, gendersel + .5])
My Slider:
<div style="font-size:14px;font-family:sans-serif">Test Filter:
<input style="position:relative;top:4px;" type="range" min="1" max="2" value="1"></div>
I was using this as a reference:
https://www.biostat.wisc.edu/~kbroman/D3/slide_scatter.html
var gendersel isn't in the global scope. You need to define it globally, to use it globally, and not inside the function:
var gendersel;
$("input[type=range]").on("change", function() { //I'm not sure what the select() thing is, I just always use $() for selectors, so if you know d3.select works, then use it.
gendersel = this.value;
});
var data = _.where(data2, {variable: gendersel});
A second potential problem:
If the slider is built by Javascript, then it's possible that the element doesn't exist when this selector is created for your onchange event. If this is the case you will have to create your change event like so:
$(".parentOfInputs").on("change", "input[type=range]", function() {
gendersel = $(this).value; //also not sure if in your case it should be $(this) or this. Try both.
});
The parentOfInputs need to be some element that already existed before the onchange event is created.

Binding multiple events to elements stored in variable

I know that puting reference of HTML element into the variable is a good practice if I need to reference to this element many times. But I run into the problem with this while making my project. How can I bind multiple and the same events to the elements which are stored into the variable?
For now I deal with it this way:
var producerEl = $("#js-producer");
var brandEl = $("#js-brand");
var seriesEl = $("#js-series");
bind(seriesEl);
bind(brandEl);
bind(seriesEl);
function bind($el) {
$el.on("keypress", function () {
// some code..
});
}
I need something like $(producerEl, brandEl, seriesEl).on...
var producerEl = $("#js-producer");
var brandEl = $("#js-brand");
var seriesEl = $("#js-series");
producerEl.add(brandEl).add(seriesEl).on("click", function () {
alert('hello');
});
If you are trying to keep your code readable, might I suggest this approach?
$("#js-producer, #js-brand, #js-series").on('keypress', function () { });
Hmm. If you're using these selectors only one, don't care about "I know it is good to". The best solution is the one provided by David Smith.
Anyway, jQuery is using the sizzle selector engine, who has it's own cache. You can ask for
$("#js-producer, #js-brand, #js-series")
the result would be cached and reused.

How to organize javascript code per view?

I'm working on a project that uses the mvc 4 api combined with vanilla mvc 4 for routing and views. Because we're using the web api\json all the data calls are client driven so we're creating a lot of javascript code.
To help with this we decided to create one global prototype javascript object for handling the stuff that is shared and include one javascript file per view to handle all client side code for that specific view. ( I know this isn't optimal )
My problem is with the per view javascript files. Should we create a prototype object per view, or use closures?
Without an object per view we see a trend, we usually end up with a $(document).ready, event wire ups like $(document).on('click', ..., view models, and a bunch of old school functions. It seems like there should be a way to organize these areas into something better. I've seen some suggestions on SO on how to do so but they don't show how to incorporate that jQuery load, event wire ups, and view models. Does anyone have any suggestions, or possible a simple example?
Here are the areas we normally end up with
$(document).ready(function () {....
$(document).on('click', '.button', function(e) {.....
function FooBar(){.....
I don't think there are any hard and fast rules for how to best accomplish this - lots of ways to skin this cat.
But here's what I do: I tend to create one (or more if necessary) closure objects for the page. It'll have an initialize method contains the $().ready() handler which then does all the event handler hookups, and any other page global initialization that needs to happen.
Something like this:
(function() {
function Page() {
var me = this;
// plain properties
this.prop = "value";
// compiled templates
this.template = HandleBars.compile($("#template1").html());
this.intialize = function () {
$().ready(function () {
$("#button1").click( me.button1Click );
$("#field1").change( me.field1Change );
});
};
this.button1Click = function () {
alert("click");
me.doSomething();
};
this.field1Change = function () {
alert("change");
},
this.doSomething = function (parm) {
alert("doSomething");
}
}
var page = new Page();
page.intialize();
})();
You should use one view model per view, and bind it to your view (html) with KnockoutJs, or any other resembling javascript library, which gives something like that (taken from there first example) :
view (html) :
​<html>
<body>
<p>First name: <input data-bind="value: firstName" /></p>
<p>Last name: <input data-bind="value: lastName" /></p>
<h2>Hello, <span data-bind="text: fullName"> </span>!</h2>
</body>
</html>​​​​​​​​​​​​​​
view model (javascript) :
var ViewModel = function(first, last) {
this.firstName = ko.observable(first);
this.lastName = ko.observable(last);
this.fullName = ko.computed(function() {
return this.firstName() + " " + this.lastName();
}, this);
};
get data from server, and populate view model (and view, consequently)
$.get('/info/path/', function(data) {
ko.applyBindings(new ViewModel(data.Planet, data.Earth));
});
Of course, if you're last part isn't at the end of the page, you can put it in a document.ready, or any similar method.

Categories