I am new to Knockoutjs and JavaScript, and need some advice.
In my HTML (View) page, I am trying to show a text property from my Javascript object (Model), which looks something like:
var object = function() {
this.text = "blah blah blah";
}
In my Object's ViewModel, I have this:
var objectViewModel= function (object) {
var content = ko.observable(object); // or is it object.text() ?
self.section = function() {
return content.text; //or is it content ?
}
}
And in my view, I have this:
<span data-bind="text:section"></span>
My main question is how do I make the HTML show a model's property (the text) via viewmodel? I commented in my other questions, and would like some help.
Thanks in advance!
So I'd recommend this post as a good read.
To answer both of the additional commented questions: It all depends on what you passed as the parameter. That important bit of information would be provided when you instantiate your viewmodel, which you left out.
As specified, you'll need to ko.applyBindings(new objectViewModel(new object())).
Secondly, you have self, where did it come from? Make it this or declare var self = this; or provide the rest of the code from which that variable is coming from.
Next, from within the section function you need to "read" your content observable:
return content().text
Finally, in your view you need to execute section:
<span data-bind="text:section()"></span>
As an additional consideration, you could make section a computed observable with a dependency on content.
this.section = ko.computed(function() {
return content().text;
})
which removes the need to execute in the view. Check out this fiddle with two different scenarios depicted.
Related
I have following issue concerning understanding ember properties:
If i have a propertyA:
propertyA: function() {
return this.get("propertyB.someObject.someValue");
}.property("propertyB")
and a propertyB:
propertyB: function() {
return this.get("propertyX.someObject");
}.property("propertyX", "propertyY", "propertyZ")
And i have a binding for propertyA in some template like:
{{propertyA}}
Then in 90% of the cases in my code it happens that propertyA does not get updated properly when i set i.e. propertyX.
If i understand it correctly, then propertyB should become dirty as soon as one of the dependent properties (like propertyX) changes. This should automatically make propertyA dirty and thus update it automatically since it has a binding.
What happens in my code is, that propertyA remains the old cached value even when i called it in the console, but when i call propertyB it revaluates and returns the updated code, since it was dirty.
The question is, why does propertyA not automatically become dirty when propertyB does? Is it because propertyB has no binding in a template? I thought it is not necessary if propertyA has the dependence.
I also figured out that this problem does not occur when propertyB just depends on propertyX, so the multi-dependency must somehow mess things up.
Sorry for this quite complicated explanation but i tried to abstract my actual code as simple as possible.
UPDATE:
Ok here some actual code:
Controller:
styling: function() {
var clipValues = this.get("clip.styling") || {};
var infoValues = this.get("clip.info.styling") || {};
return Ember.Object.create(jQuery.extend({}, clipValues, infos));
}.property("clip.styling", "clip.info.styling"),
showBottombar: function() {
return (!!this.get("bottombarSrc") || !!this.get("styling.bottombar.fancyStuff"));
}.property("styling"),
Somewhere else the clip gets set for this controller. And later its info gets updated in the clip model which is a simple Ember.Object:
getInfo: function(url) {
var self = this;
return App.ajax(url).then(function(response) {
self.set("info", response);
});
}
Now after getInfo gets called, the {{showBottombar}} in the template shows "false" even if "bottombarSrc" and "...fancyStuff" is true. When i call "styling" from the console, it reevaluates the styling code which indicates that it was marked as dirty after clip.getInfo happened (which sets the "info"). But this does not effect the showBottombar. It just does not get called afterwards.
UPDATE 2
There are two strange ways of making it work, but i dont understand why:
First one is adding a styling binding to a template:
{{styling}}
That causes showBottombar to get called after the styling changes.
Second one is removing other dependencies from the styling property:
styling: function() {
var clipValues = this.get("clip.styling") || {};
var infoValues = this.get("clip.info.styling") || {};
return Ember.Object.create(jQuery.extend({}, clipValues, infos));
}.property("clip.info.styling"),
(no more "clip.styling" dependency). Which also causes the showBottombar property to work properly. Both ways work individually.
propertyA: function() {
return this.get("propertyB.someObject.someValue");
}.property("propertyB").volatile()
http://emberjs.com/api/classes/Ember.ComputedProperty.html#method_volatile
It is end of day and my brain is down for the night, but I am working on learning how to use setters when dynamically binding to Html elements. Of the many examples I have read so far, it seems the Urls below are the most helpful on the subject of using setters with knockoutjs bindings but I still do not get the idea or yet understand how it ought to be done.
knockoutjs-data-bind-setter
conditionally-bind-a-function-in-knockoutjs
knockout-data-bind-on-dynamically-generated-elements
easy-two-way-data-binding-in-javascript
For instance my viewmodel below (see fiddle) would like to protect the private variables and probably do so by adding some sort of validation code later on.However, right now, it simply needs to get at the parameter or text value entered into the text box by user. What exactly is the best syntax for this kind of operation?
<!-- related bindings: valueUpdate is a parameter for value -->
Your value: <input data-bind="value: someValue, valueUpdate: 'afterkeydown'"/>
If your trying to achieve two way binding along with the validation, then knockouts computed functions should be the best approach.
In your view model have a private variable and and expose ko.computed function for the binding, then in either read/write part of the computed you can do the validation.
Technically you could do that, but it is not the way Knockout is meant to be used. For example, let's say our viewmodel has one <select> and one text <input> binding. Using the private vars to hold the actual values, means we need a writable computed observable to update it, because Knockout only binds properties to your view, not private vars.
function appWithPrivateVars() {
var selectedItem = ko.observable('Option 3'), //our private vars
textVal = ko.observable('Haha');
this.selected = ko.computed({
read: function() { return selectedItem(); },
write: function(value) { /* add validation code here */ selectedItem(value); }
});
this.textVal = ko.computed({
read: function() { return textVal(); },
write: function(value) { /* add validation code here */ textVal(value); }
});
this.listItems = ['Option 1','Option 2','Option 3'];
this.get = function() { return selectedItem(); }; //getter
}
Now compare with the code needed for the same viewmodel without caring about private vars (also notice you don't need an explicit getter/setter) :
function appWithProperties() {
var self = this;
this.textVal = ko.observable('Haha');
// example of computed
this.textValInput = ko.computed({
read: function() { return self.textVal(); },
write: function(value) { /* add validation code here */ textVal(value); }
this.selected = ko.observable('Option 3');
this.listItems = ['Option 1','Option 2','Option 3'];
}
The thing is you don't need to 'protect' your otherwise accessible model properties because if they are not bound to the view, they will not be able to be modified. Furthermore, you will get yourself in trouble if you use var's at the moment you want to easily serialize your data to JSON with the ko.toJSON function (unless you're willing to rewrite an entire parsing function). Compare the outputs of ko.toJSON for both viewmodels:
Sample data for appWithPrivateVars
// no private vars included, eg. 'selectedItem'
{"selected":"Option 1", // computed prop; not what we need
"textVal":"Haha",
"listItems":["Option 1","Option 2","Option 3"]}
See how the 'actual' values are not included in the mapping (which is logical because ko.toJSON doesn't have access to them). Now check out the JSON output for appWithProperties:
{"textVal":"Haha", // actual value
"textValInput: "Haha", // value filter
"selected":"Option 1",
"listItems":["Option 1","Option 2","Option 3"]
}
Check out the fiddle
So I have two viewModels, one has a document style database in an observable:
var databaseViewModel = function () {
var self = this;
self.database = ko.observableArray([]).publishesTo("database");
}
var calcViewModel = function () {
var self = this;
self.replicatedDatabase = ko.observableArray([]).subscribeTo("database");
}
These get applied thusly:
ko.applyBindings({
databaseViewModel: new databaseViewModel(),
calcViewModel: new calcViewModel()
});
The only problem is, that the drop down box tied to replicatedDatabase doesn't show any items. I know I can force a binding update:
database.valueHasMutated();
But I don't know where and when.
I have tried after the ko.applyBindings however, I'm not sure how to access the newly created databaseViewModel. I've tried inside databaseViewModel after it has been created, but I believe that KO automatically updates the bindings when they've been binded, so not sure this actually makes a difference, it didnt on the dropdowns anyways.
I'm not really sure what I should be doing here.
For reference, I'm using knockout-postbox to do message bus style communications, subscribeTo and publishesTo. So as soon as the database observable is changed it will notify all subscribers, so I thought that maybe replicatedDatabase would have been update in the instance that databaseViewModel was initiated.
So, rather than force knockout to update the values I chose a different approach.
Realistically speaking the page would initially be populated with some data from a server, so with this in mind I proceeded by making a global variable holding the initial data:
var serverData = [{}];
Then just simply populate the observableArray's using Ryan Niemeyer mapping function:
ko.observableArray.fn.map = function ( data, Constructor) {
var mapped = ko.utils.arrayMap(data, function (item) {
return new Constructor(item);
});
this(mapped);
return this;
};
This way both viewModel's start off with the initial data, and when the database viewModel gets updated this permeates through to the other viewModel's
Is it possible to use Knockout ONLY for viewing/using other objects of custom class?
I'm trying to find a way to open knockout with different data but always the same structure.
What I did:
// I have an Event class which looks like that:
function cEvent(id){
this.id = id;
}
// I keep an array of instances of that class in something like:
var arr = [new cEvent(1), new cEvent(2)]
On the html page I have:
Event ID: <span data-bind="text: id"></span>
I created an accessor-like class to get data from a specific event with Knockout
cEvent2 = function cEvent2(baseEvent) {
this.id = ko.computed(function(){
return baseEvent.id;
});
}
When I use ko.applyBindings(arr[0]); it works but what if I want to load another "model" without cleaning nodes and reapplying knockout on the page?
What I want:
I'd like to have something like ko.applyBindings(arr[1]); that would update the interface based on the data I want.
Of course in reality the cEvent class is much more complex, but I am trying to see if we're able to get something done without directly extending these instances of cEvent with knockout.
Maybe I'm just trying to do something wrong and it's not the way knockout want to work? I know that in my case I want knockout to serve as a "simple class reader" even if it could do more.
Any tip would be really appreciated.
Here's what I would do:
function cEvent(id){
this.id = id;
}
function myViewModel() {
var arr = [new cEvent(1), new cEvent(2)]
this.selectedEvent = ko.observable(arr[0]);
}
ko.applyBindings(new myViewModel());
That way, if you bind to selectedEvent.id all you need to do when you want to view a different event is update the selectedEvent property and all of your bindings will be automatically updated.
I am following a backbone.js tutorial and a part of the code isn't working, maybe because Backbone has changed in the meantime or because I'm doing something wrong. This is the render function of my view.
// grab and populate our main template
render: function () {
// once again this is using ICanHaz.js, but you can use whatever
this.el = ich.app(this.model.toJSON());
// store a reference to our movie list
this.movieList = this.$('#movieList');
return this;
},
The element gets appended into the document later in the code. Subsequently, when the code tries to adds elements to this.movieList, Javascript says it's undefined.
I have tried changing this.el = ... to
this.setElement(ich.app(this.model.toJSON()));
and that helps because this.$el is now defined, but if i try this.$el.find(...) it never finds anything, even though through inspection in Chrome it does appear to contain the HTML elements.
I never used ICanHaz but it it works like the other template languages probably returns HTML code. In that case i'd do something like:
render: function(){
this.$el.html(ich.app(this.model.toJSON()));
}
addMovie: function (movie) {
var view = new MovieView({model: movie});
this.$el.find("#movieList").append(view.render().el);
},
Hope this helps
PS: This is the first time I see this.$('something') in a backbone code o_O. Is he storing the reference of JQuery on the view?