Rendering one mustache partial multiple times with different data - javascript

I have two objects that I want to render side by side. There is never a case where I will want to render more, or less than two. My model is setup like so:
{
obj1: {...},
obj2: {...}
}
Using mustache templates, I want to render each object using the same partial:
<div>
<h1>Object 1</h1>
{{>objPartial}}
</div>
<div>
<h1>Object 2</h1>
{{>objPartial}}
</div>
However, mustache doesn't seem to support passing a context to the partial. Doing something like {{>objPartial obj1}} seems like it should be supported, but I can't find any documentation on setting a context for a partial.
Is this sort of thing supported? If not, how can I accomplish the same effect without duplicating the partial (objPartial1 and objPartial2)?

The syntax I think you are looking for is not {{>objPartial obj1}}, but rather it should be
{{#obj1}}
{{>objPartial}}
{{/obj1}}
The syntax for {{#}} isn't only for arrays - for non array objects the object becomes part of the current scope.
I've forked maxbeatty's example and modified it to show this syntax:
<script type="template/text" id="partial">
<ul>
{{#name}}
<li>{{.}}</li>
{{/name}}
</ul>
</script>
<script type="template/text" id="main">
<div>
<h1>Stooges</h1>
{{#object1}}
{{>objPartial}}
{{/object1}}
</div>
<div>
<h1>Musketeers</h1>
{{#object2}}
{{>objPartial}}
{{/object2}}
</div>
</script>​
<script type="text/javascript">
var partial = $('#partial').html(),
main = $('#main').html(),
data = {
object1: {
name: ["Curly", "Moe", "Larry"]},
object2: {
name: ["Athos", "Porthos", "Aramis", "D'Artagnan"]}
},
html = Mustache.to_html(main,data, {
"objPartial": partial
});
document.write(html);
</script>
Link to jsfiddle: http://jsfiddle.net/YW5zF/3/

You could adjust your model to include the h1 and div so you could loop over a list sending different data to objPartial each time
<script type="template/text" id="partial">
<ul>
{{#name}}
<li>{{.}}</li>
{{/name}}
</ul>
</script>
<script type="template/text" id="main">
{{#section}}
<div>
<h1>{{title}}</h1>
{{>objPartial}}
</div>
{{/section}}
</script>
<script type="text/javascript">
var partial = $('#partial').html(),
main = $('#main').html(),
data = {
section: [
{
title: "Object 1",
name: ["Curly", "Moe", "Larry"]},
{
title: "Object 2",
name: ["Athos", "Porthos", "Aramis", "D'Artagnan"]}
]
},
html = Mustache.to_html(main,data, {
"objPartial": partial
});
document.write(html);
</script>
See it on jsFiddle

Related

Data context on JsViews' include tag

I made a snippet to show the issue I'm facing:
<html>
<head>
<title>Demo 1 JsViews</title>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha256-k2WSCIexGzOj3Euiig+TlR8gA0EmPjuc79OEeY5L45g=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsviews/0.9.90/jsviews.min.js"></script>
</head>
<body>
<script id="template" type="text/x-jsrender">
<p>{{:property}}</p>
{^{on ~root.testFunction}}PUSH ME!{{/on}}
{{for nestedObjects}}
{{include #data tmpl="#template-2"/}}
{{/for}}
</script>
<div id="container"></div>
<script id="template-2" type="text/x-jsrender">
<p>{{:~root.property}}</p>
{^{on ~root.testFunction}}PUSH ME!{{/on}}
</script>
<div id="container-2"></div>
<script>
data = {
property : "PARENT",
testFunction : function(){ alert(this.property); },
nestedObjects : [
{
id: 0,
property: "CHILD1",
testFunction : function(){ alert(this.property);}
},
{
id: 1,
property: "CHILD2",
testFunction : function(){ alert(this.property);}
}]
};
/**
* Funciones
*/
$(document).ready(function(){
var tmpl = $.templates("#template");
tmpl.link("#container", data);
});
</script>
</body>
</html>
As you can see, in '#template-2' it's taking the properties from main data object, not from each nestedObjects' item. I know this would be the normal behaviour.
Is there a way to make include tag to take each nestedObjects' item and not the whole data object as context?
I know that if I remove the '~root' modifiers in '#template-2' it will work as expected, but I need it to work with '~root' modififer if possible.
Thanks in advance :)
After trying many things, I managed to get it working in another way using a helper object, if someone has a similar issue feel free to contact me :).
~root is a built-in helper that points to the top-level data that you passed to the link() or render() method. See
http://www.jsviews.com/#contextualparams#root.
So you can't change it to have it point somewhere else. But you can create your own helpers (not using the reserved root name), such as:
{{include #data ~myroot=#data tmpl="#template-2"/}}
and in template-2 write
{{:~myroot.property}}

How to create html dynamically using jquery

I want to create a html page dynamically according to a JSON object and I implemented a method and its working fine my current code is given below
$(function(){
var result =JSON.parse(response);
var markup="";
for (var i=0;i<result.length;i++) {
markup+="<div id='nameDiv'>"+result[i].name+"</div>";
}
$('.divLoadData:last').after(markup);
});
But my actual markup is like this
<div class="divLoadData">
<div class="name" id="nameDiv">
Name
</div>
<div class="numberb">
<div id="age">
Age
</div>
</div>
<div class="numberb dob">
<div id="dob">
DOB
</div>
</div>
</div>
and it will grow eventually so my current method is not capable for creating this kind markup so is there any other way to do the same.
I think jQuery Tmpl (jQuery Template) is what you need.
https://github.com/BorisMoore/jquery-tmpl
You can just setup the template then bind it with JSON data. It will build the html for you.
for simple example
$.tmpl( "<li>${Name}</li>", { "Name" : "John Doe" }).appendTo( "#target" );
suit your case example
var movies = [
{ Name: "The Red Violin", ReleaseYear: "1998" },
{ Name: "Eyes Wide Shut", ReleaseYear: "1999" },
{ Name: "The Inheritance", ReleaseYear: "1976" }
];
var markup = "<li><b>${Name}</b> (${ReleaseYear})</li>";
// Compile the markup as a named template
$.template( "movieTemplate", markup );
// Render the template with the movies data and insert
// the rendered HTML under the "movieList" element
$.tmpl( "movieTemplate", movies )
.appendTo( "#movieList" );
Hope this help.

Master Details in Knockout JS

What am I doing wrong? I am trying to create a simple master details view a la the 'canonical MVVM' example.
Here's a simplified example in JSfiddle that doesn't work: http://jsfiddle.net/UJYXg/2/
I would expect to see the name of the selected 'item' in the textbox but instead it says 'observable'?
Here's my offending code:
var list = [ { name: "item 1"} , { name: "Item 2" }];
var viewModel = {
items : ko.observableArray(list),
selectedItem : ko.observable(),
}
viewModel.setItem = function(item) {
viewModel.selectedItem(item);
}
ko.applyBindings(viewModel);
And the HTML
<ul data-bind="foreach: items">
<li>
<button data-bind="click: $root.setItem, text:name"></button>
</li>
</ul>
<p>
<input data-bind="value:selectedItem.name" />
</p>
You are really close. Just need to do value: selectedItem().name or better use the with binding to change your scope. Also, the script that you are referencing is slightly out-of-date (in 2.0 click passes the data as the first arg).
Sample here: http://jsfiddle.net/rniemeyer/acUDH/

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.

How do I reuse HTML User Interface elements?

I have the following HTML:
<div id="addFieldUI" style="display: none">
<div>
Field Name:
<input type="text" />
</div>
<div>
Field Value
</div>
<div>
Data Type:
<select>
<option>Price</option>
</select>
</div>
I would like to reuse the HTML in at least two other jQuery modals. If I use $('#otherElem').html($('#addFieldUI').html()) to insert the HTML into an HTML element I have the problem of duplicate elements if I use input ids for the fields. Should I rather use input names? Should I use a 'script' code block instead of a div? How do I create reusable HTML?
EDIT
I am aware of jQuery data templates but in this case I just want to reuse HTML within MODAL dialogs. E.g. if I used the same form for creating and editing data.
Something like this, perhaps:
var counter = 0;
$('#otherElem').html(
$('#addFieldUI')
.clone()
.attr('id', 'addFieldUI-' + counter++)
.html()
);
You can use a class name as a hook, and than before appending to the other div use Js to dynamically add IDs
There is a library called jQuery Templates and you can always give that a go, I used often in ASP.NET MVC, but works everywhere.
in Git you have some demos
example:
<script id="movieTemplate" type="text/x-jquery-tmpl">
<li>
<b>${Name}</b> (${ReleaseYear})
</li>
</script>
<ul id="movieList"></ul>
<script type="text/javascript">
var movies = [
{ Name: "The Red Violin", ReleaseYear: "1998" },
{ Name: "Eyes Wide Shut", ReleaseYear: "1999" },
{ Name: "The Inheritance", ReleaseYear: "1976" }
];
$( "#movieTemplate" ).tmpl( movies ).appendTo( "#movieList" );
</script>

Categories