I have a view model:
function ViewModel() {
this.applications = ko.observableArray();
this.templateView = ko.observable("application-grid");
this.templateToUse = function () {
return this.templateView();
}.bind(this);
};
var viewModel = new ViewModel();
ko.applyBindings(viewModel);
I have a list that is binded to the viewModel
<ul data-bind="template: { name: templateToUse, foreach: applications }"></ul>
When the page loads, it firs selects the "application-grid" template id.
When i change it first time, viewModel.templateView('application-list');, the template changes.
Then, if i change it back, viewModel.templateView('application-grid');, the template doesn't change anymore.
I am doing something wrong?
If you want to use knockout templates then you can specify that in bindings:
<ul data-bind="template: { name: templateToUse, foreach: applications, templateEngine: new ko.nativeTemplateEngine() }"></ul>
by default knockout will use jquery templates if it is referenced on page. You could also use other templates, for more info see documentation.
Related
I am beyond confused...
I am creating a list using Knockout.js components, templates, and custom elements. For some reason, the steps I create in my Viewmodel are being initialized in random order within the custom element definition! And it is completely randomized so that it is different each time!
To help better illustrate this, it is best to look at the JSFiddle. I put alert("break") after each step initialization. Load it once, and then click "run" again to see the demo properly. Look in the output window and you can see that other than step 1 being written first, the steps always appear randomly (though they maintain their order in the end).
https://jsfiddle.net/uu4hzc41/8/
I need to have these in the correct order because I will add certain attributes from my model into an array. When they are random I can't access the array elements properly.
HTML:
<ul>
<sidebar-step params="vm: sidebarStepModel0"></sidebar-step>
<sidebar-step params="vm: sidebarStepModel1"></sidebar-step>
<sidebar-step params="vm: sidebarStepModel2"></sidebar-step>
<sidebar-step params="vm: sidebarStepModel3"></sidebar-step>
<sidebar-step params="vm: sidebarStepModel4"></sidebar-step>
</ul>
JS/Knockout:
//custom element <sidebar-step>
ko.components.register("sidebar-step", {
viewModel: function (params) {
this.vm = params.vm;
alert("break");
},
template: "<li data-bind='text: vm.message'>vm.onChangeElement</li>"
});
// model
var SidebarStepModel = function () {
this.message = ko.observable("step description");
};
// viewmodel
var OrderGuideViewModel = function () {
this.sidebarStepModel0 = new SidebarStepModel();
this.sidebarStepModel0.message("step 1");
this.sidebarStepModel1 = new SidebarStepModel();
this.sidebarStepModel1.message("step 2");
this.sidebarStepModel2 = new SidebarStepModel();
this.sidebarStepModel2.message("step 3");
this.sidebarStepModel3 = new SidebarStepModel();
this.sidebarStepModel3.message("step 4");
this.sidebarStepModel4 = new SidebarStepModel();
this.sidebarStepModel4.message("step 5");
};
ko.applyBindings(new OrderGuideViewModel());
By default knockout components load asynchronously. In version 3.3 an option was added to allow the component to load synchronously.
Add synchronous:true when registering to get the behavior you want.
Example:
ko.components.register("sidebar-step", {
viewModel: function (params) {
this.vm = params.vm;
alert("break");
},
template: "<li data-bind='text: vm.message'>vm.onChangeElement</li>",
synchronous: true
});
I am using the reveal module pattern for my javascript and I am having the darndest time getting the css binding to work correctly with knockout.
My JS
my.js.module = (function ($) {
"use strict";
var my = {
testUrl: null
},
testModel= {
stuff: [{
"testOne": null
}]
},
testViewModel = null,
testId: null;
my.bindStuff = function () {
testViewModel = ko.mapping.fromJS(testModel);
ko.applyBindings(testViewModel, $(my.testId).get(0));
$.getJSON(my.testUrl,
{},
function (data) {
var testModelData = {
stuff: data
};
ko.mapping.fromJS(testModelData, testViewModel);
});
};
return my;
}(jQuery));
and in my cshtml I have
<tbody data-bind="foreach: stuff">
<tr>
<td data-bind="text: testOne"></td>
</tr>
</tbody>
Now I want to use the css binding via knock out to get a css value based on the value of what testOne is, it can be one of three things. I know it will be a ko,computed function but i cant quite figure out how to get each particular instance of stuff to look at testOne and get the correct value to determine what to return via the ko.computed function.
if someone could help me out i would be greatly appreciative.
This fiddle shows how to use mapping with an array and set the class of an element via knockout, its not an exact match to your code but should help:
http://jsfiddle.net/davidoleary/UHaVV/
var newData = {
test:"this is a test",
stuff:[
{"testOne":"Event1"},
{"testOne":"Event2"},
{"testOne":"Event3"}
]
};
var viewModel = ko.mapping.fromJS( newData );
ko.applyBindings(viewModel);
<div data-bind="text:test"></div>
<ul data-bind="foreach: stuff">
<li><span data-bind="text: testOne, attr:{class: testOne}"></span></li>
</ul>
Also have a look at this question to see two ways of setting the class:
Knockout binding css class to an observed model property
You can use attr or the new css binding both are refereed to in the question above.
I'm trying to create some tabs, one per profile the user chooses to save. Each profile is a ViewModel. So I thought I'd just create another ViewModel that contains an observableArray of objects of type: {name: profile_name, model: model_converted_to_json}.
I followed this example to create my code - but I get nothing bound, for some reason.
Here's my code:
-ViewModel (I use Requirejs, that explains the external wrapper):
"use strict";
// profiles viewmodel class
define(["knockout"], function(ko) {
return function() {
var self = this;
this.profilesArray = ko.observableArray();
this.selected = ko.observable();
this.addProfile = function(profile) {
var found = -1;
for(var i = 0; i < self.profilesArray().length; i++) {
if(self.profilesArray()[i].name == profile.name) {
self.profilesArray()[i].model = profile.model;
found = i;
}
}
if(found == -1) {
self.profilesArray().push(profile);
}
};
};
});
-The JS code (excerpt of larger file):
var profiles = new profilesViewMode();
ko.applyBindings(profiles, $("#profileTabs")[0]);
$("#keepProfile").on("click", function() {
var profile = {
name: $("#firstName").text(),
model: ko.toJSON(model)
};
profiles.addProfile(profile);
profiles.selected(profile);
console.log(profile);
$("#profileTabs").show();
});
-The HTML (Thanks Sara for correcting my HTML markup)
<section id="profileTabs">
<div>
<ul class="nav nav-tabs" data-bind="foreach: profilesArray">
<li data-bind="css: { active: $root.selected() === $data }">
</li>
</ul>
</div>
</section>
I have verified that the observableArray does get new, correct value on button click - it just doesn't get rendered. I hope it's a small thing that I'm missing in my Knockout data-bind syntax.
Thanks for your time!
You will want to call push directly on the observableArray, which will both push to the underlying array and notify any subscribers. So:
self.profilesArray.push(profile);
You are setting name using name: $('#firstName').text(); you may need to change that to .val() if this is referencing an input field (which I assumed here).
You are using .push() on the underlying array which bypasses ko's subscribers (the binding in this case)
Here is a working jsfiddle based on your code. I took some liberties with model since that wasn't included.
i'm building a paged list on the client side with knockout.js and im trying to output the page index with the below code so i get clickable links with numbers so people can switch page.
<ul data-bind="foreach:Paging">
<li>
</li>
</ul>
In my viewmodel
this.Paging = ko.computed(function ()
{
return ko.utils.range(1, this.TotalPages);
});
Everything works, tried just outputtung text:test and it writes test for each page but i want numbers. So the easiest way is of course to access current index in the foreach and + 1.
How would i be able to do this?
The problem could be with your computed ko. You have not bound it to this. So instead of:
this.Paging = ko.computed(function ()
{
return ko.utils.range(1, this.TotalPages);
});
.. try ...
this.Paging = ko.computed(function ()
{
return ko.utils.range(1, this.TotalPages);
}, this);
You can then try ColinE suggestion of text: this
When you use this in bindings, it will be referencing the window object. You ought to be using $data like this:
I tested it using this markup and it worked as expected:
<!-- returns 12345678910 -->
<div data-bind="foreach: ko.utils.range(1,10)"><span data-bind="text: $data"></span></div>
I am currently using the ExternalTemplate extension to have my templates loaded via ajax at runtime. However I am looking to extend this functionality slightly so I can supply more than one template directory.
I know it seems a bit bizarre, but I have a couple of places where templates could come from, and it is not possible to have them all coming out of one big template folder unfortunately.
I was hoping to do something like:
<script type="text/javascript">
var templateEngineSettings = {
templatesLocations: {
"default":"/view-templates-1"
"other1":"/view-templates-2"
"other2":"/somewhere-else/view-templates"
},
templateSuffix: ".template.html"
};
ko.externaljQueryTemplateEngine.setOptions(templateEngineSettings);
</script>
<div data-bind="template: {name: 'some-template', location:'default'}"></div>
<div data-bind="template: {name: 'some-other-template', location:'other1'}"></div>
<div data-bind="template: {name: 'some-new-template', location:'other3'}"></div>
However I cannot find any solid documentation on how to do this, so any help would be great!
The external template engine pulls its url base from:
ko.ExternalTemplateEngine.templateUrl
One choice would be to create a wrapper to the template binding that would swap this value from your template locations. Something like:
//custom binding
ko.bindingHandlers.templatex = {
update: function(element, valueAccessor, allBindingsAccessor, viewModel) {
var options = valueAccessor(),
location = options.location,
current = koExternalTemplateEngine.templateUrl;
//set to our new location
ko.ExternalTemplateEngine.templateUrl = ko.bindingHandlers.templatex.templateLocations[location];
//call the real template binding
ko.bindingHandlers.update.tempate(element, valueAccessor, allBindingsAccessor, viewModel);
//reset the location back to the default
ko.ExternalTemplateEngine.templateUrl = current;
},
templateLocations: {}
};
//set in your app code
ko.bindingHandlers.templatex.templateLocations = {
"default":"/view-templates-1",
"other1":"/view-templates-2",
"other2":"/somewhere-else/view-templates"
};