Problem Render backbone collection using Mustache template - javascript

I am quite new to backbone js and Mustache. I am trying to load the backbone collection (Object array) on page load from rails json object to save the extra call . I am having troubles rendering the backbone collection using mustache template.
My model & collection are
var Item = Backbone.Model.extend({
});
App.Collections.Items= Backbone.Collection.extend({
model: Item,
url: '/items'
});
and view
App.Views.Index = Backbone.View.extend({
el : '#itemList',
initialize: function() {
this.render();
},
render: function() {
$(this.el).html(Mustache.to_html(JST.item_template(),this.collection ));
//var test = {name:"test",price:100};
//$(this.el).html(Mustache.to_html(JST.item_template(),test ));
}
});
In the above view render, i can able to render the single model (commented test obeject), but not the collections. I am totally struck here, i have tried with both underscore templating & mustache but no luck.
And this is the Template
<li>
<div>
<div style="float: left; width: 70px">
<a href="#">
<img class="thumbnail" src="http://placehold.it/60x60" alt="">
</a>
</div>
<div style="float: right; width: 292px">
<h4> {{name}} <span class="price">Rs {{price}}</span></h4>
</div>
</div>
</li>
and my object array kind of looks like this

Finally it turns out that mustache doesn't handle array of objects sent to the template, so i had to wrap it with other object like this
render: function() {
var item_wrapper = {
items : this.collection
}
$(this.el).html(Mustache.to_html(JST.item_template(),item_wrapper ));
}
and in template just looped the items array to render the html
{{#items}}
<li>
<div>
<div style="float: left; width: 70px">
<a href="#">
<img class="thumbnail" src="http://placehold.it/60x60" alt="">
</a>
</div>
<div style="float: right; width: 292px">
<h4> {{name}} <span class="price">Rs {{price}}</span></h4>
</div>
</div>
</li>
{{/items}}
Hope it helps to someone.

Mustache can handle arrays using {{#.}}{{.}}{{/.}}

This happens because mustache expects a key/value pair -being the value an array- for Non-Empty Lists. From the mustache man page, section "Non-Empty Lists":
Template:
{{#repo}}
<b>{{name}}</b>
{{/repo}}
Hash:
{
"repo": [
{ "name": "resque" },
{ "name": "hub" },
{ "name": "rip" },
]
}
Output:
<b>resque</b>
<b>hub</b>
<b>rip</b>
If you pass only an array, mustache does not have a way to know where to expand it inside the template.

Using Hogan.js implementation.
Given template:
<ul>
{{#produce}}
<li>{{.}}</li>
{{/produce}}
</ul>
And context:
var context = { produce: [ 'Apple', 'Banana', 'Orange' ] );
We get:
<ul>
<li>Apple</li>
<li>Banana</li>
<li>Orange</li>
</ul>

Related

ng-src doesn't render any value

I'm using angular 1.4.5 with django v1.8 app, and trying to implement new layout for my website.
Here is my view:
<div class="page animsition" ng-app="AppUI">
<div class="page-content padding-30 blue-grey-500"
ng-controller="DisplayManageController">
<ul>
<li ng-repeat="feed in items">
<a>{{ feed.type }}</a>
<img ng-src="feed.obj.image"/>
<em ng-bind="feed.obj.text"></em>
</li>
</ul>
</div>
</div>
And the angular controller code:
var AppUI = angular.module('AppUI');
AppUI.controller('DisplayManageController', ['$scope', 'display', function ($scope, display) {
$scope._display = display.items({'id': 71});
$scope.items = [];
$scope._display.$promise.then(function (result) {
angular.forEach(result, function (value) {
$scope.items.push(value);
});
});
}]);
And html result after promise updates
<li ng-repeat="feed in items" class="ng-scope">
<a></a>
<img ng-src="feed.obj.image" src="feed.obj.image">
<em ng-bind="feed.obj.text" class="ng-binding">Blabla #somehashtagfromme</em>
</li>
Here is the content of "items"
[
{$$hashKey:"object:3",group_id: 1,id: "562a1a48942fbd0d9016617e",obj:{image:"https://scontent.cdninstagram.com/hphotos-xfa1/t51.2885-15/s640x640/sh0.08/e35/12080420_855923527854550_1926591603_n.jpg",text:"Nefesler tutuldu"},type:"instagram"},
{$$hashKey:"object:2",group_id: 1,id: "5627a75e942fbd0d7ed19748",obj:{image:"https://pbs.twimg.com/media/CR2VePxUwAAVqtn.jpg", text:"bu zamanların ruhu"},type:"twitter"},
...
]
ng-repeat repeats all items as feed objects, but why curly braces and ng-src doesn't compile or returns empty values ?
Ps: I also tried ng-src={{feed.obj.text}} but result doesn't change ..
It should be ng-src="{{feed.obj.text}}". That's quotes and double curly brackets.
If that doesn't work, paste <pre>{{feed.obj | json}}</pre> after the </a> and before the <img>. That will let you see problems with the object.
Also, since you're using Django make sure your rendering the template raw. Otherwise django will think that the double curly brackets are django templates.
Here is a plunker of your code working:
http://plnkr.co/edit/khgxkLt2FjskrguWP5Js?p=preview
You do need curly braces around items being interpolated.
<ul>
<li ng-repeat="feed in items">
<a>{{ feed.type }}</a>
<img ng-src="{{feed.obj.image}}"/>
<em ng-bind="{{feed.obj.text}}"></em>
</li>
</ul>

How do I make {{#each}} work with these templates?

I have been reading these tutorials on Spacebars.
Understanding Spacebars by David Burles
Meteor's Spacebars README on Github
Discover Meteor's Spacebars secrets
I think I can understand pretty good, but I am having this problem
i have the main.html template like this
<template name="main">
<div class="container">
<section id="bl-work-section">
<div class="bl-box">
<h4 class="sin">Próximo</h4>
<h2 id="titulo1" class="sin"></h2>
<h4 class="sin" id="sub1"></h4>
</div>
<div class="bl-content">
<h2>Lunes 25 de Noviembre del 2014</h2>
{{#each mostrarLunes}}
{{> Lunes}}
{{/each}}
<a></a>
</div>
<span class="bl-icon bl-icon-close"></span>
</section>
<!-- Panel items for the works -->
<div class="bl-panel-items" id="bl-panel-work-items">
{{#each mostrarLunes}}
{{showPromos}}
{{/each}}
</div>
</div>
</div><!-- /container -->
</template>
So like you guys see im calling 2 templates inside main template and that 2 templates look like this
Lunes.html Template
<template name="Lunes">
<ul id="bl-work-items">
<li data-panel="panel">
<div class="oferta">
<h3>Promocion: {{metadata.nombrePromo}} </h3><br><small>Descripcion:{{metadata.descripcionPromo}}</small>
</div></li><br><br>
</ul>
</template>
showPromos.html Template
<template name="showPromos">
<div data-panel="panel">
<div>
<h1>Estoy viendo esto en la segunda pagina </h1>
<h3>Nombre Promo {{metadata.nombrePromo}}</h3>
<p>Descripcion promo.{{metadata.descripcionPromo}}</p>
</div>
</div>
<nav>
<span class="bl-icon bl-icon-close"></span>
</nav>
</template>
So what is the problem? well if we look at the template Lunes and showPromos, i have a Data-Panel="panel", but its seems like that data panel don't work when i wrap that value on the {{each}} tags, because if i use, that {{each}} tags outside the data-panel selector it works, so its seems like its not working seems like they don't have connection.
so im asking if there is a way to connect that value? already try helper attributes like the 3rd link says, and don't work, the attributes helpers look like this
Template.main.helpers({
attributes: function () {
return {
dataPanel: "prueba",
}
}
});
Helper mostrarLunes
Template.main.helpers({
'mostrarLunes' : function(){
return Promociones.find({'metadata.diaOferta' : { $in: ['Lunes'] } });
}
});
Looks like the data context isn't set. Try creating a template helper for your other templates with the data (I don't understand Spanish so I'm assuming this is the MongoDB cursor you need...
Templates.lunes.helpers({
data: function() {
return Promociones.find({'metadata.diaOferta' : { $in: ['Lunes'] } });
}
});
Templates.showPromos.helpers({
data: function() {
return Promociones.find({'metadata.diaOferta' : { $in: ['Lunes'] } });
}
});

How to ng-style one element it's $index created by ng-repeat?

I have 2 directives: wa-hotspots & wa-tooltips.
On ng-mouseover of wa-hotspots it takes that $index of wa-hotspot and sets the visibility and position of wa-tooltip via ng-class:on and ng-style="tooltipCoords" by matching indexes.
Note: Since wa-hotspots & wa-tooltips share the same collection page.hotspots and therefore they share teh same index via ng-repeat
Problem:
When you hover over wa-hotspots it sets the ng-style position to ALL of the elements in wa-tooltips. I only want it ot set the proper matching index. Since the visiblity is controlled by ng-class, This doesn't really matter but it seems like it's extra overhead that could be avoid.
Therefore:
Question:
How can I make sure that my ng-style isn't styling all the wa-tooltips on hover of wa-hotspots? But rather, style only the tooltip that matches the proper shared index?
<ul id="wa-page-{{page.pageindex}}" class="wa-page-inner" ng-mouseleave="pageLeave()">
<li wa-hotspots
<map name="wa-page-hotspot-{{page.pageindex}}">
<area ng-repeat="hotspot in page.hotspots"
class="page-hotspot"
shape="{{hotspot.areashape}}"
coords="{{hotspot.coordinatetag_scaled}}"
ng-mouseover="showTooltip($index, hotspot.coordinatetag_scaled)"
ng-mouseout="hideTooltip()">
</map>
</li>
<li class="tooltip-wrapper">
<ul class="tooltip">
<li wa-tooltips
ng-repeat="hotspot in page.hotspots"
ng-class="{on: hovered.index == $index}"
ng-mouseover="hovered.active == true"
ng-mouseout="hovered.active == false"
ng-style="tooltipCoords" hotspot="hotspot">
</li>
</ul>
</li>
</ul>
tooltip:
You need to make it per item like in your case - hotspot.tooltipCoords then set that variable by index.
you can do the check inside the expression function.
Heres a fiddle
<div ng-controller="MyCtrl">
<div ng-repeat="item in items" ng-style="isChecked($index)">
name: {{item.name}}, {{item.title}}
<input type="checkbox" ng-model="item.checked" />
</div>
</div>
...
$scope.isChecked = function($index){
var color = $scope.items[$index].checked ? 'red' : 'blue';
return {color:color};
}
Instead of
ng-mouseover="hovered.active == true"
ng-mouseout="hovered.active == false"
use
ng-mouseover="hotspot.class== 'active'"
ng-mouseout="hotspot.class== ''"
and after that you can use hotspot.class in ng-class ie:
ng-class="hotspot.class"
Please see demo below:
var app = angular.module('app', []);
app.controller('homeCtrl', function($scope) {
$scope.items = [{
id: 1
}, {
id: 2
}, {
id: 3
}, {
id: 4
}]
});
.red {
background-color: yellow;
}
p {
background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="homeCtrl">
<p ng-repeat="i in items" ng-mouseover="i.class='red'" ng-class="i.class" ng-mouseout="i.class = ''">{{i.id}}</p>
</div>
</div>
Use the below one
<div class="col-md-4 col-xs-12 col-lg-4" ng-repeat="obj in items">
<button type="button" class="btn btn-sm pull-right" ng-class="obj.class" ng-click="obj.class='test'" >
Write a new class "test". Instead of click you can use the same in ngmouseover

Pass parameter to Angular ng-include

I am trying to display a binary tree of elements, which I go through recursively with ng-include.
What is the difference between ng-init="item = item.left" and ng-repeat="item in item.left" ?
In this example it behaves exactly the same, but I use similiar code in a project and there it behaves differently. I suppose it's because of Angular scopes.
Maybe I shouldn't use ng-if, please explain me how to do it better.
The pane.html is:
<div ng-if="!isArray(item.left)">
<div ng-repeat="item in [item.left]" ng-include="'Views/pane.html'">
</div>
</div>
<div ng-if="isArray(item.left)">
{{item.left[0]}}
</div>
<div ng-if="!isArray(item.right)">
<div ng-repeat="item in [item.right]" ng-include="'Views/pane.html'">
</div>
</div>
<div ng-if="isArray(item.right)">
{{item.right[0]}}
</div>
<div ng-if="!isArray(item.left)">
<div ng-init = "item = item.left" ng-include="'Views/pane.html'">
</div>
</div>
<div ng-if="isArray(item.left)">
{{item.left[0]}}
</div>
<div ng-if="!isArray(item.right)">
<div ng-init="item = item.right" ng-include="'Views/pane.html'">
</div>
</div>
<div ng-if="isArray(item.right)">
{{item.right[0]}}
</div>
The controller is:
var app = angular.module('mycontrollers', []);
app.controller('MainCtrl', function ($scope) {
$scope.tree = {
left: {
left: ["leftleft"],
right: {
left: ["leftrightleft"],
right: ["leftrightright"]
}
},
right: {
left: ["rightleft"],
right: ["rightright"]
}
};
$scope.isArray = function (item) {
return Array.isArray(item);
}
});
EDIT:
First I run into the problem that the directive ng-repeat has a greater priority than ng-if. I tried to combine them, which failed. IMO it's strange that ng-repeat dominates ng-if.
It's a little hacky, but I am passing variables to an ng-include with an ng-repeat of an array containing a JSON object :
<div ng-repeat="pass in [{'text':'hello'}]" ng-include="'includepage.html'"></div>
In your include page you can access your variable like this:
<p>{{pass.text}}</p>
Pass parameter to Angular ng-include
You don't need that. all ng-include's sources have the same controller. So each view sees the same data.
What is the difference between ng-init="item = item.left" and ng-repeat="item in item.left"
[1]
ng-init="item = item.left" means - creating new instance named item that equals to item.left. In other words you achieve the same by writing in controller:
$scope.item = $scope.item.left
[2]
ng-repeat="item in item.left" means create list of scopes based on item.left array. You should know that each item in ng-repeat has its private scope
I am trying to display a binary tree of elements, which I go through recursively with ng-include.
I posted in the past answer how to display tree by using ng-include.
It might helpful: how-do-display-a-collapsible-tree
The main part here that you create Node with id wrapped by <scipt> tag and use ng-repeat:
<script type="text/ng-template" id="tree_item_renderer">
<ul class="some" ng-show="data.show">
<li ng-repeat="data in data.nodes" class="parent_li" ng-include="'tree_item_renderer'" tree-node></li>
</ul>
</script>
<ul>
<li ng-repeat="data in displayTree" ng-include="'tree_item_renderer'"></li>
Making a generic directive instead of ng-include is a cleaner solution:
Angular passing scope to ng-include
I am using ng-include with ng-repeat of an array containing string. If you want to send multple data so please see Junus Ergin answer.
See my code Snippet:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="">
<div ng-repeat="name in ['Sanjib Pradhan']" ng-include="'your_template.html'"></div>
<div ng-repeat="name in ['Chinmay Sahu']" ng-include="'your_template.html'"></div>
<script type="text/ng-template" id="your_template.html">
{{name}}
</script>
</div>

KnockoutJS - foreach not working with a single entry in viewmodel

I have the following code that, on a successful AJAX return, displays a popup window with a list of addresses. The knockout version is 2.3.0.
If there is more than 1 address then the html correctly renders with a 'display' string.
The problem is that if there is ONLY 1 address the html list renders but without any text in the span.
In both cases the view model is correctly being populated with data so it looks to me like a problem updating the html.
I have tried pushing the data again and although I can use jQuery to update the html but this doesn't help me understand the problem.
HTML
<div id="reverseGeocodingResults">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Local Addresses</h4>
</div>
<div>
<ul data-bind="foreach: vm.reverseGeocodingViewModel.AddressList" class="locationList">
<li class="locationListItem" data-bind="click: SubmitAddressRequest">
<div>
<span data-bind="text: Display"></span>
</div>
</li>
</ul>
</div>
</div>
</div>
Javascript (from a separate file so only taken what I think is relevant)
var vm;
var masterViewModel = function () {
this.viewModel = { LocationList: ko.observableArray([]), SubQuery: ko.observable() };
this.reverseGeocodingViewModel = { AddressList: ko.observableArray([]) };
};
function SubmitReverseGeocodingRequest(easting, northing, projectId, mouseLocation) {
$.post('url?action=ReverseGeocodingLookup', {
easting: easting,
northing: northing,
pid: projectId
})
.done(function (data) {
spinner.stop();
if (parseInt(data.NumberOfAddressesFound) > 0) {
if (data.AddressList.length == 1) {
alert('just 1 address');
}
// remove all array items before adding new
// Not pretty but gets around an issue the UI seems to have displaying the updated list
if (vm.reverseGeocodingViewModel.AddressList().length > 0) {
vm.reverseGeocodingViewModel.AddressList.splice(0, vm.reverseGeocodingViewModel.AddressList().length);
}
vm.reverseGeocodingViewModel.AddressList(data.AddressList);
}
});
)
$(document).ready(function () {
vm = new masterViewModel();
ko.applyBindings(vm);
})
HTML Result - Multiple Results
<ul class="locationList" data-bind="foreach: vm.reverseGeocodingViewModel.AddressList" style="height: 265px;">
<li data-bind="click: SubmitAddressRequest" class="locationListItem">
<div>
<span data-bind="text: Display">Yates Wine Lodge, SWINDON</span>
</div>
</li>
<li data-bind="click: SubmitAddressRequest" class="locationListItem">
<div>
<span data-bind="text: Display">The Brunel Centre, SWINDON</span>
</div>
</li>
</ul>
HTML Result - Single Result
<ul class="locationList" data-bind="foreach: vm.reverseGeocodingViewModel.AddressList" style="height: 265px;">
<li class="locationListItem" data-bind="click: SubmitAddressRequest">
<div>
<span data-bind="text: Display"></span>
</div>
</li>
</ul>
I have looked at various previous questions on this topic and couldn't come up with an answer hence the question, but please point me to another post if I have missed one.
to remove all before add new use reverseGeocodingViewModel.AddressList.removeAll()
Maybe didn't find it but you have to parse data, $.parseJSON I use for this.
Try to use
$.each($.parsejSON(data), function(i, el){
reverseGeocodingViewModel.AddressList.push(el);
})
I hope it will help you
I've noticed following issues:
function SubmitReverseGeocodingRequest should be closed with } not )
foreach: vm.reverseGeocodingViewModel.AddressList should be foreach: reverseGeocodingViewModel.AddressList, without vm because vm is already binded
vm = new masterViewModel();
ko.applyBindings(vm);
To clean AddressList use vm.reverseGeocodingViewModel.AddressList([]) instead vm.reverseGeocodingViewModel.AddressList.splice(0, vm.reverseGeocodingViewModel.AddressList().length);
Note: Here's possible mistake
<li class="locationListItem" data-bind="click: SubmitAddressRequest">
so AddressList item have to have the SubmitAddressRequest function. I don't think that you get data with the function
I've created test sample based on your code, take a look here

Categories

Resources