I have just started experimenting with JavaScript, JQuery and Knockout.js (I'm normally working on the server-side in Java) and it's beyond brilliant - however I have hit a wall, and I'm extremely doubtful that I'm doing, whatever I'm trying to do, 'by the book'.
I have created a fiddle for it here: http://jsfiddle.net/kcyhw/
My mission:
My problem:
I tried the following:
Using optionValue in the data-bind - it works, and it binds to the "value", however, I can see that it "takes over" my binding, and kills the text I retrieve from the object. I removed it, and continued...
Computed values, however it didn't work out since the template wants (after my knowledge) a literal object, and functions can't reference other properties in such an object.
Created my own binding, so I could get a reference to both the element (the select box) and all the other binding values. In this function I tried, using jQuery, to set the attribute 'value' on the element which was passed in, however, it doesn't work. I can also see, that the binding gets called 4 times (that probably because it calls init and then update for each template I created which contains the select box).
To me, it looks like I have created a friggin' mess, and I would really appreciate if some smart people could point me into the right direction of how to solve this. Resources, code-snippets, advice... whatever you got.
The code:
<html>
<head>
<script src="javascript/jquery-1.10.2/jquery-1.10.2.js"></script>
<script src="javascript/knockout-2.3.0/knockout-2.3.0.js"></script>
<script src="javascript/knockout.mapping-master-2.0/knockout.mapping-latest.js"></script>
<script type="text/javascript" src="javascript/json2-2.0/json2.js"></script>
<title>A Knockout Demo</title>
<script>
/**
* JQuery Function
*/
$(document).ready(function() {
// Domain Object
var Person = function(id, fullname) {
var self = this;
self.id = id;
self.fullname = fullname;
};
// Knockout Model
var KoModel = function() {
var self = this;
// Declare observables
self.persons = ko.observableArray();
// Allows observables to share an array without observing each other
self.createPersonSelector = function(namevalue) {
var person = new Object();
person.selectedPerson = ko.observable();
person.name = namevalue;
return person;
}
// Prints a serialized string which could be sent to the server
self.printFormElements = function(formElements) {
alert($(formElements).serialize());
}
// Will change the person select value, to a real value
self.changePersonSelectValue = function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var value = valueAccessor(), allBindings = allBindingsAccessor();
// Next, whether or not the supplied model property is observable, get its current value
var valueUnwrapped = ko.unwrap(value);
// Now manipulate the DOM element
var $eleme = $(element);
if ($eleme == null) {
return;
}
// Change to item number two in the list *doesn't work*.
$eleme.val(2);
};
// Person selectbox value binding
ko.bindingHandlers.personSelect = {
init : function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
self.changePersonSelectValue(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);
},
update : function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
self.changePersonSelectValue(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);
}
};
// Put some test-data into the array
self.persons.push(new Person(1, 'Martin Smith'));
self.persons.push(new Person(2, 'Andy Gregersen'));
self.persons.push(new Person(3, 'Thomas Peep'));
};
// Apply bindings
ko.applyBindings(new KoModel());
});
</script>
<script type="text/html" id="person-template">
<span>Choose ID: </span><select data-bind="options: $root.persons, optionsText: 'id', personSelect: true, value:selectedPerson, attr: {'name': name, 'id': name}"></select></br>
<span>ID:</span> <span data-bind="text: selectedPerson().id"></span></br>
<span>Full Name: </span> <span data-bind="text: selectedPerson().fullname"></span></br>
</script>
<body>
<h1>Person Select One</h1>
<form data-bind="submit: printFormElements">
<div
data-bind="template: { name: 'person-template', data:createPersonSelector('personselect')}"></div>
<button type="submit">Submit</button>
</br>
</form>
<h1>Person Select Two</h1>
<form data-bind="submit: printFormElements">
<div
data-bind="template: { name: 'person-template', data:createPersonSelector('personselecttwo')}"></div>
<button type="submit">Submit</button>
</br>
</form>
</body>
</html>
The easiest way for me to answer is by changing quite a few things that I might do differently. The main issue that's holding you back is that you're using jQuery for things that can be handled by KO in a much easier way.
Below are the things I'd change, to see the full result have a look at this fiddle (which doesn't use jQuery at all).
Simplify your model to something like this:
var KoModel = function() {
var self = this;
// Declare observables
self.persons = ko.observableArray();
// Prints a serialized string which could be sent to the server
self.printFormElements = function() {
alert(ko.mapping.toJSON(self));
}
// Hold selected person
self.selectedPersons = ko.observableArray();
};
A few things to note:
selectedPersons
is now an observable, and an array at that because the view could potentially be a multi-select; This corresponds to the following View for starting off a template:
<div data-bind="template: { name: 'person-template' }"></div>
I've removed the data
bit for now. This means each instance of this code would bind to (the same) $root
view model. If you don't want that I'd suggest creating a container view model to hold several KoModel
s.
The template looks like this:
<span>Choose ID: </span>
<select data-bind="options: persons, optionsText: 'fullname', selectedOptions: selectedPersons"></select><br />
<!-- ko foreach: selectedPersons -->
<span>ID:</span> <span data-bind="text: id"></span><br />
<span>Full Name: </span> <span data-bind="text: fullname"></span><br />
<!-- /ko -->
Here's the jist:
data-bind
is much simpler. You don't need to fiddle with value
attributes because Knockout will bind each option
to a specific item in your array; fullname
for the text; selectedOptions
bit tells Knockout where to store selected items in your view model; foreach
because the select
could potentially be multiple
select. Now the ko.mapping.toJSON(self)
call in the view model will generate something like this:
{
"persons": [{
"id": 1,
"fullname": "Martin Smith"
}, {
"id": 2,
"fullname": "Andy Gregersen"
}, {
"id": 3,
"fullname": "Thomas Peep"
}],
"selectedPersons": [{
"id": 2,
"fullname": "Andy Gregersen"
}]
}
As you can see the list of selected persons is there, to be sent to the server. The rest is there by default, but the mapping plugin can be configured to great detail.
Hope this helps and solves your problem!
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.