Using Knockout with repeated user controls - javascript

I have a web page that contains a list of games. Each game is presented by a user control, that contains a few labels that hold the properties of the game (time, scores, players, etc.). So the same user control is repeated a few times on the page.
The data changes every minute to support live covarage of the game.
I was hoping to use knockout to update all labels in the user control, but since every user control should bind to a different game data, and a user control cannot have its own view model, I dont know what is the best approach to this scenario.
I need something like a dynamic ViewModel and a dynamic data-bind attributes, but I couldnt find any information on the subject.

Here is a demonstration of the template binding using both data and foreach with the same template. You can see in the JS that the data is the type, a game, but we are dislpaying them separately in the HTML.
HTML
<!-- ko if: favoriteGame -->
<h1>Favorite Game</h1>
<div data-bind="template: { name: 'gameTemplate', data: favoriteGame }"></div>
<!-- /ko -->
<h1>All Games</h1>
<div data-bind="template: { name: 'gameTemplate', foreach: games }"></div>
<script type="text/ko" id="gameTemplate">
<div>
<span class="gameName" data-bind="text: name"></span>
<span data-bind="text: publisher"></span>
<input data-bind="value: score" />
<button data-bind="click: $parent.favoriteGame">Favorite</button>
</div>
</script>
Javascript
var Game = function(data) {
this.name = ko.observable(data.name || "");
this.publisher = ko.observable(data.publisher || "");
this.score = ko.observable(data.score || 0);
};
var ViewModel = function(init) {
var self = this;
self.favoriteGame = ko.observable();
self.games = ko.observableArray(ko.utils.arrayMap(init, function(g) {
return new Game(g);
}));
};
Note that the click: $parent.favoriteGame binding selects the favorite game directly. Knockout passes the current context as the first parameter to function bindings, and since observables are functions, this updates the observable directly, without needing a wrapper function.
You can take a look at this in this fiddle. Its not perfectly clear what you where after, you don't have any code in your question. I hope this isn't too far off.

Related

KnockoutJS Observable being rebound appropriately, but calling gives an empty string?

So I've got a section that updates based on a ko.observable called toClicked, see below:
<div id="longInfoWindow">
<div id="longInfo_goBack"><span class="fa fa-arrow-left"></span> Go Back</div>
<div id="location_mainInfo">
<h1 id="location_title" data-bind="text: toClicked.title">Title</h1>
<h2 id="location_address" data-bind="text: toClicked.address">Address</h2>
<h6 class="location_latlng">LAT: <span data-bind="text: toClicked.lat">LATITUDE</span></h6>
<h6 class="location_latlng">LNG: <span data-bind="text: toClicked.lng">LONGITUDE</span></h6>
<p id="location_description" data-bind="text: toClicked.content">
Empty
</p>
</div>
</div>
toClicked is data-bound via a for-each ul that passes in bits of information from an observableArray, so this data is constantly changing - here's what the click function looks like in the viewmodel.
var viewModel = {
// Nav open and close via knockoutjs
self : this,
userQuery : ko.observable(''),
toClicked : ko.observable({}),
logClick : function(clicked){
var toClicked = ko.observable({});
toClicked().title = clicked.title;
toClicked().lat = clicked.lat;
toClicked().lng = clicked.lng;
toClicked().placeID = clicked.placeID;
toClicked().address = clicked.address;
toClicked().content = clicked.content;
return toClicked();
}
};
// at the end of the document...
ko.applyBindings(viewModel);
For some reason, I can call any toClicked parameter, like toClicked.title, and I get the proper output. But I can't get anything to bind in the longInfoWindow bit of code, it removes the filler text with empty strings. Is there something I'm missing here with Knockout that's keeping it from updating appropriately?
As a side note, I've tried setting the databinds to viewModel.toClicked.title with no joy. Have also tried $root, $parent. either comes back as not defined or gives the same result.
You need to change the way toClicked is accessed. Given that it is an observable, it must access the properties using syntax like toClicked().property. Another problem is that you should specify toClicked as an object, before setting properties on it.
Also, since clicked is an array, it should be accessed by index, like clicked[0].title.
Note the use of self.toClicked.valueHasMutated(); in function logClick. It tells the view model that observable variable toClicked has some non-observable properties that might have changed. As a result the view model is updated. You should avoid using it excessively.
var viewModel = function() {
// Nav open and close via knockoutjs
var self = this;
self.test = ko.observable('text');
self.userQuery = ko.observable('');
self.toClicked = ko.observable({});
self.markers = ko.observableArray([
{ title: 'Eagle River Airport', lat: 45.934099, lng: -89.261834, placeID: 'ChIJdSZITVA2VE0RDqqRxn-Kjgw', content: 'This is the Eagle River Airport. Visit us for fly-ins!' }
]);
self.logClick = function(clicked) {
// var toClicked = ko.observable({});
self.toClicked().title = clicked[0].title;
self.toClicked().lat = clicked[0].lat;
self.toClicked().lng = clicked[0].lng;
self.toClicked().placeID = clicked[0].placeID;
self.toClicked().address = clicked[0].address;
self.toClicked().content = clicked[0].content;
self.toClicked.valueHasMutated();
return self.toClicked();
};
};
// at the end of the document...
var vm = new viewModel();
ko.applyBindings(vm);
var markers = vm.markers();
vm.logClick(markers);
Your view model must also change slightly. Note the () brackets after toClicked, they are used to access the properties of an observable.
<div id="longInfoWindow">
<div id="longInfo_goBack"><span class="fa fa-arrow-left"></span> Go Back</div>
<div id="location_mainInfo">
<h1 id="location_title" data-bind="text: toClicked().title">Title</h1>
<h2 id="location_address" data-bind="text: toClicked().address">Address</h2>
<h6 class="location_latlng">LAT: <span data-bind="text: toClicked().lat">LATITUDE</span></h6>
<h6 class="location_latlng">LNG: <span data-bind="text: toClicked().lng">LONGITUDE</span></h6>
<p id="location_description" data-bind="text: toClicked().content">
Empty
</p>
</div>
I'd also suggest that instead of accessing toClicked directly within logClick you use something like self.toClicked to avoid ambiguity. See this.
Here's the working fiddle: https://jsfiddle.net/Nisarg0/hx0q6tt6/13/
The more obvious way without having to use valueHasMutated would be to assign directly to the observable:
self.logClick = function(clicked) {
self.toClicked({
lat: clicked[0].lat,
lng: clicked[0].lng,
placeID: clicked[0].placeID,
adress: clicked[0].address,
content: clicked[0].content
});
};
You normally do not need to use valueHasMutated when using knockout. Also there is no need to return the observable from the click handler. In your bindings you need then to access the properties as already stated like this:
<h1 id="location_title" data-bind="text: toClicked().title">Title</h1>
Knockout will automatically update the heading, whenever toClicked changed.

How to exchange value between two viewmodels in knockout js

Hi i have two view models and i want to transfer a value between them.
here is js fiddle
`http://jsfiddle.net/sohimohit/e84u8L85/`
i want when a user click on show another div button then corresponding item name should be display on another div . means i want to show one viewmodel value into another.
secondly when a user click on show another div button another div appears i want an option of cancel so that user can go back to firstdiv. how can i achieve this.
You can use global variables container1VM and container2VM for it.
E.g. call of
container1VM.isVisible(!container1VM.isVisible());
container2VM.isVisible(!container2VM.isVisible());
will make visible container - invisible.
JSFiddle DEMO
Code:
HTML:
<div id="container1">
<div data-bind="visible: !isVisible()">
<ul >
<li >Container1 item</li>
<!-- ko foreach: myItems -->
<li>Item <span data-bind="text: $data"></span></li>
<button data-bind="click:$root.showDiv">show another div</button>
<!-- /ko -->
</ul>
</div>
</div>
<div id="container2" data-bind="visible:isVisible">
<ul>
<li >Container2 item</li>
<!-- ko foreach: myItems -->
<li>Item <span data-bind="text: $data"></span></li>
<!-- /ko -->
</ul>
<button data-bind="click:$root.cancel">cancel</button>
</div>
Javascript:
function Container1ViewModel()
{
var self = this;
self.isVisible = ko.observable(false);
self.showDiv = changeVisibility;
self.myItems = ko.observableArray();
self.myItems.push("ABC");
self.myItems.push("CDE");
}
function Container2ViewModel() {
var self = this;
self.isVisible = ko.observable();
self.myItems = ko.observableArray();
self.myItems.push("XYZ");
self.myItems.push("PQR");
self.cancel = changeVisibility;
}
function changeVisibility()
{
container1VM.isVisible(!container1VM.isVisible());
container2VM.isVisible(!container2VM.isVisible());
}
var container1VM = new Container1ViewModel();;
var container2VM = new Container2ViewModel();;
ko.applyBindings(container1VM, document.getElementById("container1"));
ko.applyBindings(container2VM, document.getElementById("container2"));
Also consider using Knockout Postbox by the excellent Ryan Niemeyer:
https://github.com/rniemeyer/knockout-postbox
You could use syncWith to update an observable controlling visibility. That way both viewmodels don't need to know about each other and you're not hard coding references to your viewmodels.
syncWith - syncWith(topic, [initializeWithLatestValue], [skipInitialPublish], [equalityComparer])
The syncWith function tells an observable to both subscribe and publish on a topic. This allows observables in two different view models to stay in sync with each other without having direct knowledge of its counterpart.
//subscribe to and publish on a topic
this.value = ko.observable(value).syncWith("mytopic");
//subscribe to and publish on a topic and use the last published value to initialize the observable
this.value = ko.observable().syncWith("mytopic", true);
//subscribe to and publish on a topic, but do not publish out the observable's value initially
this.value = ko.observable(value).syncWith("mytopic", false, true);
//subscribe to and publish on a topic, but only publish when the comparer function returns false
var comparer = function(newValue, oldValue) {
return newValue < oldValue;
};
this.value = ko.observable(value).syncWith("mytopic", false, false, comparer);

Data binding while manipulating the DOM not working with Angular, Knockout

I am looking to implement Angular, Knockout or another to data-bind a wizard-style application form proof-of-concept (no server-side code). However I appear to be breaking the data bindings when I clone the data-bound div.
The first few steps of the application form capture data, while the later steps present the data back to the user to allow them confirm what was entered. I am manipulating the DOM by inserting the appropriate step when 'next' is pressed and taking out the last step when 'previous' is pressed. I do this using detatch, clone and remove.
Can anyone give advise on the approach they would take to make this work, and what frameworks I should look at?
Below is pseudocode to give an idea of the structure. The pseudo-data-binding-code is just how I thought it would work, I'm not bedded to any framework.
HTML View
<div id="wizard">
<div id="step1">Enter your name: <input type="text" id="name" /></div>
</div>
<div id="actions"><input type="button" value="Previous" /><input type="button" value="Next" onClick="goNext();" /></div>
<div id="steps">
<div id="stepA">Enter your age: <input type="text" id="age" databind="theAge" /></div>
<div id="stepB">The age you entered - {{ theAge }} is too young!</div>
<div id="stepC">Enter your favourite colour: <input type="text" id="faveColour" databind="faveCol" /></div>
<div id="stepD">Hi {{ name }}. You are {{ age }} years old and your favourite colour is {{ faveCol }}</div>
</div>
JavaScript
<script>
function goNext() {
// figure out which step is next
insertStepIntoWizard(step, index, title);
}
function insertStepIntoWizard(step, index, title) {
var element = step.detach();
wizard.steps('insert', index, {
title: title,
content: element.clone()
});
console.log('insertStepIntoWizard - just inserted ' + step.attr('id') + ' into wizard position ' + index);
}
</script>
I think you're thinking in terms of having your view reflect your entity model, which is basically templating. If you're looking to use Knockout/Angular, consider using it's view model to manage page state / flow / actions, in addition to controlling the entity. (Writing jQuery code to poke about with the DOM and clone, show/hide, etc is no fun). #sabithpocker makes a similar point.
Working example: I'm familiar with Knockout, and have created an example jsFiddle of your scenario: http://jsfiddle.net/overflew/BfRq8/5/
Notes:
I've used the template tag to house each section of the wizard, and all steps point to the same model/entity within the viewmodel.
To emphasise what's happening with the publish/subscribe nature of the bindings:
The user input is also relayed at the bottom of the page.
The form title is dynamic, as well as the 'step'
ko.computed is used to 'compute' the full name and if there are any 'steps' left to go
Knockout-specific: Notice the occurrence of brackets popping up around the place. This is one thing that may catch you out occasionally if you choose to learn Knockout. It just means that you're evaluating the binding container to get the value.
View model
<div>
<h3 data-bind="text: currentStep().name"></h3>
<div data-bind="template: { name: 'wizard-step1' }, visible: currentStep().id === 0"></div>
<div data-bind="template: { name: 'wizard-step2' }, visible: currentStep().id === 1"></div>
<div data-bind="template: { name: 'wizard-step3' }, visible: currentStep().id === 2"></div>
<input type="button" value="Next step" data-bind="click: onNext, visible: hasNextSteps" />
<input type="button" value="Submit" data-bind="click: onSubmit,visible: !hasNextSteps()" />
<div data-bind="visible: submitResultMessage, text: submitResultMessage"></div>
</div>
<div>
<h3>Your inputs</h3>
<div data-bind="visible: questions.fullName">Full name: <span data-bind="text: questions.fullName"></span></div>
<div data-bind="visible: questions.age">Age: <span data-bind="text: questions.age"></span>
</div>
<div data-bind="visible: questions.favouriteColour">Favourite colour: <span data-bind="text: questions.favouriteColour"></span>
</div>
</div>
<script type="text/html" id="wizard-step1">
<div>
First name: <input data-bind="value: questions.firstName, valueUpdate:'afterkeydown'" />
</div>
<div>
Last name: <input data-bind="value: questions.lastName, valueUpdate:'afterkeydown'" />
</div>
</script>
<script type="text/html" id="wizard-step2">
<div>
Age: <input data-bind="value: questions.age, valueUpdate:'afterkeydown'" />
</div>
</script>
<script type="text/html" id="wizard-step3">
<div>
Favourite colour: <input data-bind="value: questions.favouriteColour, valueUpdate:'afterkeydown'" />
</div>
</script>
View
// Entity for holding form data.
var FormData = function() {
var self = this;
self.firstName = ko.observable("");
self.lastName = ko.observable("");
self.age = ko.observable("");
self.favouriteColour = ko.observable("");
self.fullName = ko.computed(function() {
if (!self.firstName() && !self.lastName()) {
return "";
}
return self.firstName() + " " + self.lastName();
});
}
// Quick handling for managing steps of the wizard
var wizardSteps = [
{ id: 0, name: "Wizard step 1"},
{ id: 1, name: "More questions"},
{ id: 2, name: "Last step"}
];
var ViewModel = function() {
var self = this;
// Properties
self.questions = new FormData();
self.currentStep = ko.observable(wizardSteps[0]);
self.submitResultMessage = ko.observable();
// Actions
self.onNext = function() {
var currentIndex = self.currentStep().id;
if (self.hasNextSteps()) {
// Move forward one step on the wizard
self.currentStep(wizardSteps[currentIndex + 1]);
}
};
self.onSubmit = function() {
self.submitResultMessage("Data is now submitted ");
};
// Page control
self.hasNextSteps = ko.computed(function() {
var currentIndex = self.currentStep().id;
return currentIndex < wizardSteps.length - 1;
});
};
ko.applyBindings(new ViewModel());
What most of the bleeding edge javascript frameworks is trying to do is attempting to manage the huge amount of code in current applications where large amount of business logic is implemented with javascript in client side. So they are mostly trying to provide you with some architecture to organize your code to make it easy to manage, read and scale.
In your case you are trying to neglect the architecture like MVC or MVWhatever they are providing and use the framework to do some templating, If I understand you correctly. For that you can better get the help of some templating engine in javascript like handlebars and use it to manually render your data stored in your current javascript app.
see it here http://handlebarsjs.com/
Simply re-initialize your bindings.
This solves my problem when using knockout.
When you change the main DOM element which is actually being referenced for MVVM, it turn-out that any change or re-structuring that element hampers the dom relationship.
Eventually, though the placement of the item looks same but technically, in java script engine it is not. So the problem occurs.
In order to fix something like this, simply have a constructor which will initialize or re-initialize the mapping for a particular element in DOM.
Hope that solves your problem.
Example:
function ReInitializeVM()
{
ko.cleanNode(document.getElementById("userDetails"));
ko.applyBindings(viewModel, document.getElementById("userDetails"));
}
The function can be called everytime where you feel the need of re-initializing the bindings. Calling the function will remove any binding on the element with id "userDetails" and then the applyBindings can be called to initialize the binding. This will invoke bindings for any new/changed element.

Passing in an observableArray as a function parameter

What I'm trying to achieve:
A form with one text input field(Name), two select fields with some options(Type & Column) with a submit button that creates a Widget with it's title set to Name, data-type set to Type and Column - being it's position in the page.
Type in this case has a couple of different options defined in the view, it works just dandy so I won't go into how I got that working, for now.
As of now I have a page with three columns, each as it's own observableArray - like so:
self.col0 = ko.observableArray([]);
self.col0.id = 'col0'
The same goes for col1 and col2. My goal here is to get the select field to point to these arrays. Atleast that's what I think I need to do.
I have a createWidget function, that looks at the Name and Type values in the DOM (Correct me if I'm wrong here, this is the very first thing I'm creating with KnockOut) and creates a new Widget from that data - like so:
var Widget = function(name, type) {
var self = this;
self.name = name;
self.type = type;
};
var ViewModel = function() {
var self = this;
self.newWidgetName = ko.observable();
self.newType = ko.observable();
// Unrelated code in between
self.createWidget = function(target) {
newName = self.newWidgetName();
newType = self.newType();
widget = new Widget(newName, newType);
target.unshift(widget)
self.newWidgetName("");
};
The input/select elements in the DOM
input.widget-name type="text" placeholder="wName" data-bind="value: newWidgetName"
select data-bind="value: newType"
option value="calendar" Calendar
option value="article" Article
option value="document" Document
select.colPick data-bind="value: colPick"
option value="col0" Column 1
option value="col1" Column 2
option value="col2" Column 3
And my click handler for the createWidget function - like so:
a.btn.create-widget data-bind="click: function(){ createWidget(col0); }"
Shazam, it works!
However, this will only ever output the new Widget into the first col (col0), it won't take the value of the column select field into account and unshift the new widget into the correct column. The errors I'm getting after much trial and error pretty much boils down to:
1) "Cannot call method unshift of undefined"
2) "Uncaught TypeError: Object function observable() .... has no method 'unshift'
So as of right now the code looks like the example above, It's not ideal by any means but with the different columns connected with knockout-sortable, it's not a huge deal if this functionality has to get scrapped. The user can still move the Widgets around from col0 to col2 and vice versa.
If anyone has a resource that would point me in the right direction, or the answer up their sleeve - feel free to share!
All the best, Kas.
Edit: Followup questions for Tyrsius
With the code you supplied I now have the three columns working in the select box, however I am struggling a bit when it comes to outputting the Widgets into view.
With my previous code, this is what the view looked like:
<div class="cols">
<div class="col col-25">
<ul data-bind="sortable: { template: 'widgetTmpl', data: col0, afterMove: $root.widgetData }">
</ul>
</div>
<div class="col col-50">
<ul data-bind="sortable: { template: 'widgetTmpl', data: col1, afterMove: $root.widgetData }">
</ul>
</div>
<div class="col col-25">
<ul data-bind="sortable: { template: 'widgetTmpl', data: col2, afterMove: $root.widgetData }">
</ul>
</div>
</div>
What I'm working with right now is this:
<div data-bind="foreach: columns">
<div data-bind="foreach: items" class="col">
<ul data-bind="sortable: { template: 'widgetTmpl', data: columns, afterMove: $root.widgetData }"></ul>
</div>
</div>
My first question, I realised I wasn't thinking at the time so skip that one.
Question #2: How would I now get these Widgets bound to the col that I picked in the form?
Would I do this?
<ul data-bind="sortable: { template: 'widgetTmpl', data: entryColumn, afterMove: $root.widgetData }"></ul>
Or am I way off? :)
I would keep a collection of the items on the column as a type, which would look like this:
var Column = function(header) {
this.header = ko.observable(header);
this.items = ko.observableArray([]);
};
The trick would be to bind the column select straight to the list of columns on your viewmodel:
<select data-bind="options: columns, optionsText: 'header', value: entryColumn"
Whats happening here is that the actual column object that is selected by the dropdown will be stored in the entryColumn property. Later we can put the item directly into its items list since we will have a reference to it. This also allows us to support any number of columns without changing the logic.
The add code would stay simple:
self.columns = ko.observableArray(columns);
self.entryName = ko.observable('');
self.entryType = ko.observable('');
self.entryColumn = ko.observable('');
self.types = ko.observableArray(['Small', 'Medium', 'Large']);
self.addWidget = function() {
var widget = new Widget(self.entryName(), self.entryType());
//Here is where we can directly add to the selected columns item list
self.entryColumn().items.push(widget);
self.resetForm();
};
Here is the fiddle demonstrating these.
FollowUp Question
You're close, but the data is the items not the EntryColumn
<div data-bind="foreach: columns">
<div data-bind="sortable: { template: 'widgetTmpl', data: items}" class="column">
</div>
</div>
<script type="text/html" id="widgetTmpl">
<p data-bind="text: name() + ' - ' + type()"></p>
</script>
Here is the updated fiddle.

How to bind click handlers to templates in knockoutjs without having a global viewModel?

I'm very new to KnockoutJs so I'm hoping that there is a well known best practice for this kind of situation that I just haven't been able to find.
I have a view model that contains an array of items. I want to display these items using a template. I also want each item to to be able to toggle between view and edit modes in place. I think what fits best with Knockout is to create the relevant function on either the main view model or (probably better) on each item in the array and then bind this function in the template. So I have created this code on my page:
<ul data-bind="template: {name: testTemplate, foreach: items}"></ul>
<script id="testTemplate" type="text/x-jquery-tmpl">
<li>
<img src="icon.png" data-bind="click: displayEditView" />
<span data-bind="text: GBPAmount"></span>
<input type="text" data-bind="value: GBPAmount" />
</li>
</script>
<script>
(function() {
var viewModel = new TestViewModel(myItems);
ko.applyBindings(viewModel);
})();
</script>
And this in a separate file:
function TestViewModel(itemsJson) {
this.items = ko.mapping.fromJS(itemsJson);
for(i = 0; i < this.items.length; ++i) {
this.items[i].displayEditView = function () {
alert("payment function called");
}
}
this.displayEditView = function () {
alert("viewmodel function called");
}
};
Due to the environment my JS is running in I can't add anything to the global namespace, hence the annonymous function to create and set up the view model. (There is a namespace that I can add things to if it is necessary.) This restriction seems to break all the examples I've found, which seem to rely on a global viewModel variable.
P.S. If there's an approach that fits better with knockoutJS than what I am trying to do please feel free to suggest it!
When your viewModel is not accessible globally, there are a couple of options.
First, you can pass any relevant methods using the templateOptions parameter to the template binding.
It would look like (also note that a static template name should be in quotes):
data-bind="template: {name: 'testTemplate', foreach: items, templateOptions: { vmMethod: methodFromMainViewModel } }"
Then, inside of the template vmMethod would be available as $item.vmMethod. If you are using templateOptions as the last parameter, then make sure that there is a space between your braces { { or jQuery templates tries to parse it as its own.
So, you can bind to it like:
<img src="icon.png" data-bind="click: $item.vmMethod" />
The other option is to put a method and a reference to anything relevant from the view model on each item. It looks like you were exploring that option.
Finally, in KO 1.3 (hopefully out in September and in beta soon) there will be a nice way to use something like jQuery's live/delegate functionality and connect it with your viewModel (like in this sample: http://jsfiddle.net/rniemeyer/5wAYY/)
Also, the "Avoiding anonymous functions in event bindings" section of this post might be helpful to you as well. If you are looking for a sample of editing in place using a dynamically chosen template, then this post might help.
This is for those asking how to pass variable methods (functions) to Knockout Template. One of the core features of Templating is the consuming of variable data, which can be String or function. In KO these variables can be embedded in data or foreach properties for the Template to render. Objects embedded in data or foreach, be it String, function etc, can be accessed at this context using $data.
You can look at this code and see if it can help you to pass functions to Knockout Template.
function ViewModel() {
this.employees = [
{ fullName: 'Franklin Obi', url: 'employee_Franklin_Obi', action: methodOne },
{ fullName: 'John Amadi', url: 'employee_John_Amadi', action: methodTwo }
],
this.methodOne = function(){ alert('I can see you'); },
this.methodTwo = function(){ alert('I can touch you'); }
}
ko.applyBindings(new ViewModel());
<ul data-bind="template: { name: employeeTemplate, foreach: employees }" ></ul>
<script type="text/html" id="employeeTemplate">
<li><a data-bind="attr: { href: '#/'+url }, text: fullName, click: $data.action"></a></li>
</script>
If you want to serve multiple Template constructs you can introduce a switch method to your ViewModel like this, and use as property to introduce alias for each item (employee). Make sure you add the switch key, linkable, to the item object.
...
this.employees = [
{ fullName: 'Franklin Obi', linkable : false },
{ fullName: 'John Amadi', url: 'employee_John_Amadi', action: methodTwo, linkable : true }
],
this.methodLinkTemplate = function(employee){return employee.linkable ? "link" : "noLink"; } //this is a two way switch, many way switch is applicable.
...
Then the id of the Template forms will be named thus;
<ul data-bind="template: { name: employeeTemplate, foreach: employees, as: 'employee' }" ></ul>
<script type="text/html" id="noLink">
<li data-bind="text: fullName"></li>
</script>
<script type="text/html" id="link">
<li><a data-bind="attr: { href: '#/'+url }, text: fullName, click: $data.action"></a></li>
</script>
I have not ran this codes but I believe the idea can save someones time.

Categories