Can knockout.js use templates to generate templates? - javascript

I'm trying to use knockout to templates to generate templates.
Along the lines of
Html:
<script id="searchField-template" type="text/html">
<li data-bind="text: name"></li>
</script>
<script id="template-template" type="text/html">
<ul data-bind="template: { name: 'searchField-template', foreach: ${name} }" ></ul>
</script>
JS:
var viewModel = {
Title: [{
name: "Title1"},
{
name: "Title2"},
{
name: "Title3"}],
Manager: [{
name: "Manager1"},
{
name: "Manager2"},
{
name: "Manager3"}],
Defn: [{
name: "Title"},
{
name: "Manager"}]
};
ko.applyBindings(viewModel);
runnable sample here: http://jsfiddle.net/scottwww/yQZUE/2/
It seems that the problem is with how curly braces are interpreted.
Any suggestions?

Here you go >
http://jsfiddle.net/vwP3w/2/

Not sure this is the right way, but a reference to the vm helps.
http://jsfiddle.net/scottwww/vwP3w/1/
HTML:
<div data-bind="template: { name: 'template-template', foreach: Defn }"></div>
<script id="searchField-template" type="text/html">
<li data-bind="text: name"></li>
</script>
<script id="template-template" type="text/html">
<ul data-bind="template: { name: 'searchField-template', foreach: vm[$data.name] }" ></ul>
</script>
JS:
var viewModel = {
Title: [{
name: "Title1"},
{
name: "Title2"},
{
name: "Title3"}],
Manager: [{
name: "Manager1"},
{
name: "Manager2"},
{
name: "Manager3"}],
Defn: [{
name: "Title"},
{
name: "Manager"}]
};
window.vm = viewModel;
ko.applyBindings(viewModel);

use the $data variable inside the nested template.
http://jsfiddle.net/dwick/yQZUE/3/

Related

KnockoutJS - How to hide certain elements inside foreach using Observable Arrays?

I have a list of WebsiteOwners. I'm trying to build a UI which will display more information about the owners when I click on them.
this.toExpand = ko.observableArray(); //initialize an observable array
this.invertExpand = ko.observable("");
this.invertExpand = function (index) {
if (self.invertExpand[index] == false) {
self.invertExpand[index] = true;
alert(self.invertExpand[index]); //testing whether the value changed
}
else {
self.invertExpand[index] = false;
alert(self.invertExpand[index]); //testing whether the value changed
}
};
Here's the HTML code :
<div data-bind="foreach: WebsiteOwners">
<div>
<button data-bind="click: $root.invertExpand.bind(this,$index())" class="label label-default">>Click to Expand</button>
</div>
<div data-bind="visible: $root.toExpand()[$index]">
Primary Owner: <span data-bind="text:primaryOwner"></span>
Website Name : <span data-bind="text:websiteName"></span>
//...additional information
</div>
</div>
You can store one of your WebsiteOwner items directly in your observable. No need to use an index.
Don't forget you read an observable by calling it without arguments (e.g. self.invertExpand()) and you write to it by calling with a value (e.g. self.invertExpand(true))
I've included 3 examples in this answer:
One that allows only a single detail to be opened using knockout
One that allows all details to be opened and closed independently using knockout
One that does not use knockout but uses plain HTML instead 🙂
1. Accordion
Here's an example for a list that supports a single expanded element:
const websiteOwners = [
{ name: "Jane", role: "Admin" },
{ name: "Sarah", role: "Employee" },
{ name: "Hank", role: "Employee" }
];
const selectedOwner = ko.observable(null);
const isSelected = owner => selectedOwner() === owner;
const toggleSelect = owner => {
selectedOwner(
isSelected(owner) ? null : owner
);
}
ko.applyBindings({ websiteOwners, isSelected, toggleSelect });
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<ul data-bind="foreach: { data: websiteOwners, as: 'owner' }">
<li>
<span data-bind="text: name"></span>
<button data-bind="
click: toggleSelect,
text: isSelected(owner) ? 'collapse' : 'expand'"></button>
<div data-bind="
visible: isSelected(owner),
text: role"></div>
</li>
</ul>
2. Independent
If you want each of them to be able to expand/collapse independently, I suggest adding that state to an owner viewmodel:
const websiteOwners = [
{ name: "Jane", role: "Admin" },
{ name: "Sarah", role: "Employee" },
{ name: "Hank", role: "Employee" }
];
const OwnerVM = owner => ({
...owner,
isSelected: ko.observable(null),
toggleSelect: self => self.isSelected(!self.isSelected())
});
ko.applyBindings({ websiteOwners: websiteOwners.map(OwnerVM) });
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<ul data-bind="foreach: websiteOwners">
<li>
<span data-bind="text: name"></span>
<button data-bind="
click: toggleSelect,
text: isSelected() ? 'collapse' : 'expand'"></button>
<div data-bind="
visible: isSelected,
text: role"></div>
</li>
</ul>
3. Using <details>
This one leverages the power of the <details> element. It's probably more accessible and by far easier to implement!
const websiteOwners = [
{ name: "Jane", role: "Admin" },
{ name: "Sarah", role: "Employee" },
{ name: "Hank", role: "Employee" }
];
ko.applyBindings({ websiteOwners });
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<ul data-bind="foreach: websiteOwners">
<li>
<details>
<summary data-bind="text: name"></summary>
<div data-bind="text: role"></div>
</details>
</li>
</ul>

Best way to loop Obj data in HTML document with JS?

I'm developing a simple SPA framework. I have a problem. I want to render my Obj data in HTML. Below is my code and my online example
var data = {
for: {
animal: [{
name: 'dog',
alive: 'false'
},
{
name: 'cat',
alive: 'true'
}
],
human: [{
name: 'bob',
sex: 'male'
},
{
name: 'alice',
sex: 'female'
}
]
}
};
<html>
<div id="app">
<ol>
<li np-for="animal">
<np tag="text-for" data="name"></np><br>
<np tag="text-for" data="alive"></np>
</li>
</ol>
</div>
</html>
best way to solve it?
If you are not using a framework, then I suggest you using jQuery, so the full code looks like this
let data = {
animals : [
{ name: 'dog', alive : 'false'},
{ name: 'cat', alive : 'true'}
]
};
let animalList = $('#animals');
data.animals.forEach(function(data) {
animalList.append(`<li>
<b>${data.name}</b>
<i>${data.alive}</i>
</li>`);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<html>
<div id="app">
<ul id="animals">
<!-- data will automatically generated by jQuery -->
</ul>
</div>
</html>
I suggest you to using let or const for static variable instead of var, read more var vs let vs const in JavaScript
Read more about jQuery

Kendo ui MVVM - How to bind an observable array and array inside array using kendo templates

Here is the my working DEMO.
I have an observable array named persons and each array element contains an another array named hobbies.
I have successfully bound the persons array using kendo template, but does anyone know how should I bind the hobbies array using an another template. Below is the code from my DEMO.
Code:
var persons = new kendo.data.ObservableArray(
[
{
name: "John Doe",
age: 28,
hobbies: [
{ id: 1, description: "Baseball", rank: 1 },
{id: 2, description: "music", rank: 3 },
{ id: 3, description: "Surfing the web", rank: 2}
]
},
{
name: "Jane Doe",
age: 24,
hobbies: [
{ id: 1, description: "Volley Ball", rank: 1 },
{id: 2, description: "Cricket", rank: 3 },
{ id: 3, description: "Hockey", rank: 2}
]
}
]
);
var viewModel = kendo.observable({
array: persons
});
kendo.bind($("#example"), viewModel);
<h2>Persons Array</h2><br/>
<div id="example" data-template="template" data-bind="source: array">
</div>
<script id="template" type="text/x-kendo-template">
<div>
Name: #=name# || Age: #=age# <br>
<ul>Hobbies (below, I want to display hobbies)</ul>
<br/>
</div>
</script>
You need to use a for loop inside the ul tag, something like:
# for (var i = 0; i < hobbies.length; i++) { #
<li>#= hobbies[i].description#</li>
# } #
Here it is the updated fiddle
Use another nested template named "hobby-template" and bind "hobbies" as source to that
<h2>Persons Array</h2><br/>
<div id="example" data-template="template" data-bind="source: array">
</div>
<script id="template" type="text/x-kendo-template">
<div>
<span data-bind="text:name"></span>
<span data-bind="text:age"></span>
<ul data-template="hobby-template" data-bind="source: hobbies"></ul>
</div>
</script>
<script id="hobby-template" type="text/x-kendo-template">
<li>
<span data-bind="text:description"></span>
<span data-bind="text:rank"></span>
<li>
</script>

How to create a tree structure in Knockout using a Knockout template?

I am working with Knockout support and now creating tree structured UI component. Here i will create the elements dynamically and there i want to bind the data to newly created elements.
Please check with the below code
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script type="text/html" id="tree">
<li menuid="data bind with attr binding">
<span> </span>
<span><a href="#" name="endnode"></span>
<ul data-bind="template: { name: 'tree', foreach: childNodes }">
</ul>
</li>
below is my script
var viewModel = {
Mytree: ko.observable({
childNodes: [
{
id: 1,name:"node1",
childNodes: [ {id: 2, name:"node2", childNodes: [{id: 3,name:"node3", childNodes: [] }] } ]
},
{
id: 4,name:"node4",
childNodes: [ {id: 5,name:"node5", childNodes: [] } ]
}
]
})
};
ko.applyBindings(new viewModel.Mytree());
Now i want to append the bindable node names to to tree like below:
<span><a href="#" name="endnode" data-bind:"text:childNodes.name"/></span>
<ul data-bind="template: { name: 'tree', foreach: childNodes }">
Can you please any one suggest me to achieve this
If you pass it the view model (you can use the $root syntax for this) you can just use "name" in the binding. The context will change as it goes down the tree ... there's a couple of other bits wrong - for one it's data-bind= (equals, not colon)
Try this template ...
<script type="text/html" id="tree">
<li menuid="data bind with attr binding">
<span> </span>
<ul data-bind="template: { name: 'tree', foreach: childNodes }" />
</li>
</script>
and use this to kick things off (note $root)
<ul data-bind="template: { name: 'tree', data: $root }" />
this will give you ...

Knockout "with" binding

I am trying to display descendant elements in array using "with" binding.
But it displays only last items in "exercises" and I want to see all of them. How is it possible to fix this?
And after that, how can I make each item in array editable?
My ViewModel:
function AppViewModel() {
var self = this;
self.workouts = ko.observableArray([
{name: "Workout1", exercises:{
name: "Exercise1.1",
name: "Exercise1.2",
name: "Exercise1.3"
}},
{name: "Workout2", exercises:{
name: "Exercise2.1",
name: "Exercise2.2",
name: "Exercise2.3"
}},
{name: "Workout3", exercises:{
name: "Exercise3.1",
name: "Exercise3.2",
name: "Exercise3.3"
}},
{name: "Workout4", exercises:{
name: "Exercise3.1",
name: "Exercise3.2",
name: "Exercise3.3"
}},
]);
self.removeWorkout = function() {
self.workouts.remove(this);
};
}
ko.applyBindings(new AppViewModel());
The View:
<div class="content">
<ul data-bind="foreach: workouts">
<li>
<span data-bind="text: name"> </span>
Remove
<ul data-bind="with: exercises">
<li data-bind="text: name"></li>
</ul>
</li>
</ul>
</div>
Here's this code at jsfiddle:
http://jsfiddle.net/9TrbE/
Thanks!
The exercises property you declared as an object should be an array.
self.workouts = ko.observableArray([
{name: "Workout1", exercises:[
{ name: "Exercise1.1" },
{ name: "Exercise1.2" },
{ name: "Exercise1.3" }
]},
]);
So you can use this view :
<div class="content">
<ul data-bind="foreach: workouts">
<li>
<span data-bind="text: name"> </span>
Remove
<ul data-bind="foreach: exercises">
<li data-bind="text: name"></li>
</ul>
</li>
</ul>
</div>
Declaring :
var exercises = {
name: "Exercise1.1",
name: "Exercise1.2",
name: "Exercise1.3"
};
Is like doing that :
var exercises = {
name: "Exercise1.1",
};
exercises.name: "Exercise1.2";
exercises.name: "Exercise1.3";
Get it with this
Here's this code at jsfiddle
self.workouts = ko.observableArray([
{name: "Workout1", exercises:[
{ name: "Exercise1.1" },
{ name: "Exercise1.2" },
{ name: "Exercise1.3" }
]},
]);
`http://jsfiddle.net/9TrbE/8/

Categories

Resources