Ember js & handlebars - each not working - javascript

I'm new to Ember.js and I'm trying to get my head around it.
I have the following code (http://jsfiddle.net/6wPmW/ for a working example):
<script type="text/x-handlebars">
<div id="ViewAddresses" class="location_search_results selected_searches clearfix">
<div>
<div>
{{#each MapSearch.ViewAddressesC}}
<div class="row">ghfdg</div>
{{/each}}
</div>
</div>
</div>
</script>​
JS:
MapSearch = Ember.Application.create();
MapSearch.ListAddress = Ember.Object.extend({
list_info: null,
node_level: null,
node_value: null,
removed_properties: null
});
MapSearch.ViewAddressListC = Ember.ArrayController.create({
content: [],
initialize: function() {
me = this;
var l = MapSearch.ListAddress.create({
node_level: "test",
node_value: "test"
});
me.pushObject(l);
var l = MapSearch.ListAddress.create({
node_level: "test",
node_value: "test"
});
me.pushObject(l);
console.log(me);
}
});
MapSearch.ViewAddressListC.initialize();
console.log(MapSearch.ViewAddressListC.get("content"));​
Surely I should get two <divs> created from the each loop?

The error is in your template:
{{#each MapSearch.ViewAddressesC}}
should be
{{#each MapSearch.ViewAddressListC}}
See this JSFiddle.

Related

How to update hasMany in Ember.js using different controllers?

How to update hasMany in Ember.js using different controllers?
Hi
I have Ruby on Rails 4.0.3 app and I am using Ember.js
DEBUG: Ember : 1.6.0-beta.3 ember.js?body=1:3917
DEBUG: Ember Data : 1.0.0-beta.7+canary.f482da04 ember.js?body=1:3917
DEBUG: Handlebars : 1.3.0 ember.js?body=1:3917
DEBUG: jQuery : 1.11.0
I want to display hasMany in the same view using different controllers.
I have seen some example on StackOverflow but most (if not all) of them are for displaying records.
Ok talk is cheap, I am showing the code:
Models:
-javascripts/models/task.js
EmTasks.Task = DS.Model.extend({
name: DS.attr('string'),
description: DS.attr("string"),
list: DS.belongsTo('list')
});
-javascripts/models/list.js
EmTasks.List = DS.Model.extend({
name: DS.attr('string'),
tasks: DS.hasMany('task')
});
Router:
-javascripts/router.js
EmTasks.Router.map(function(){
return this.route("lists", {
path: '/'
});
});
EmTasks.ListsRoute = Ember.Route.extend({
model: function() {
return this.store.find('list');
}
});
Controllers:
-javascripts/controllers/lists_controller.js
EmTasks.ListsController = Em.ArrayController.extend({
addList: function() {
this.store.createRecord('list', {
name: this.get('newListName')
}).save();
return this.set('newListName', '');
},
destroyList: function(id) {
if (confirm("Are you sure?")) {
this.get('store').find('list', id).then( function(record) {
record.destroyRecord();
});
}
},
});
-javascripts/controllers/list_controller.js
EmTasks.ListController = Em.ObjectController.extend({
actions: {
editList: function() {
this.set('isEditingList', true);
var model = this.get('model')
},
acceptChanges: function () {
this.set('isEditingList', false);
var name = this.get('model.name');
if (Ember.isEmpty(name)) {
this.send('removeList');
} else {
var list = this.get('model')
list.set('name', name);
list.save()
}
},
removeList: function () {
var list = this.get('model');
list.destroyRecord();
}
},
isEditingList: false
});
-javascripts/controllers/task_controller.js
EmTasks.TaskController = Em.ObjectController.extend({
isEditingTask: false
});
Templates:
-javascripts/templates/lists.handlebars [fragment]
{{#each itemController='list'}}
<div class='col-md-8'>
<h3>
{{#if isEditingList}}
{{edit-input class="form-control" value=name focus-out="acceptChanges" insert-newline="acceptChanges"}}
{{else}}
<div {{action 'editList' on='doubleClick'}}>
{{name}}
</div>
{{/if}}
</h3>
</div>
<div class='col-md-4 down13p'>
<button class="btn btn-danger btn-small pull-right" {{action "destroyList" id}} type="button">
<span class="glyphicon glyphicon-ban-circle"></span>
</button>
</div>
{{#each task in this.tasks }}
<div class="col-md-10">
{{#if task.isEditingTask}}
{{edit-input class="form-control" value=task.name focus-out="acceptChanges" insert-newline="acceptChanges"}}
{{else}}
<div {{action 'editList' on='doubleClick'}}>
{{name}}
</div>
{{/if}}
But is looks like isEditingTask property is not working...
Any idea how to fix that?
OK found a solution, just add itemController to tasks each loop
{{#each task in this.tasks itemController='task' }}
<div class="col-md-10">
{{#if task.isEditingTask}}
{{edit-input class="form-control" value=task.name focus-out="acceptChanges" insert-newline="acceptChanges"}}
{{else}}
<div {{action 'editTask' on='doubleClick'}}>
{{name}}
</div>
{{/if}}
HTH

Uncaught TypeError: Cannot read property 'fn' of undefined

I have a backbone app where I want a view to be displayed when triggered. When i click the anchor the console says: Uncaught TypeError: Cannot read property 'fn' of undefined - I checked other issues of this kind and they say that jQuery is not loaded?? wtf? I dont get it...
My MenuView:
var LeftMenuView = Backbone.View.extend({
template: Handlebars.compile(Template),
events: {
'click li a.artAll': 'artAll',
},
artAll: function(event) {
event.preventDefault();
var artAllRouter = new Backbone.Router();
var route = '/artists/top100/all';
artAllRouter.navigate(route, {trigger: true});
}
....
The console tells me that the error is rooted in handlebars.js?
My HTML template:
<div>
{{#each}}
<p>
{{artist_name}}
</p>
{{/each}}
</div>
and my View:
define(['backbone','handlebars', 'text!templates/TopAllArtists.html'],
function(Backbone,Handlebars, Template) {
var TopAllArtView = Backbone.View.extend({
template: Handlebars.compile(Template),
initialize: function () {
_.bindAll(this, 'render');
},
render: function() {
var self = this;
self.collection.each(function(model){
self.$el.append(self.template({
artist_name:model.get('artist_name')
})
);
});
return this;
}
});
return TopAllArtView;
}
);
Anyone know what might be the issue?
According to the Handlebars documentation the each requires a list to iterate over,
<ul class="people_list">
{{#each people}}
<li>{{this}}</li>
{{/each}}
</ul>
Is artist name something to iterate over?
If model.get('artist_name') is iterable, ['name1', 'name2', ...] than,
<div>
{{#each artist_name}}
<p>
{{this}}
</p>
{{/each}}
</div>
If model.get('artist_name') is a string, 'Johann Sebastian Bach' than,
<div>
<p>
{{artist_name}}
</p>
</div>

Loading a single model with Ember.js

I'm trying to write a pretty simple app: I have some games (say chess, tictactoe... whatever), and there are several boards of each game. I want to show a list of games, and then show a list of boards when you click on a game.
But I'm facing a lot of problems. I'll first describe them, and I'll paste the code after that:
The list of games is correctly shown. No problem here.
When I click a game, I get this error:
Uncaught Error: assertion failed: an Ember.CollectionView's content must implement Ember.Array. You passed <(generated game controller):ember237>
BUT if I press F5 or write the URL manually, everything works fine. And I have no idea why.
UPDATE: I've seen that if I change the games template, changing the {{#linkTo}} with a hand-written link, everything works OK:
This is the non-working linkTo: {{#linkTo 'game' game}}{{game.name}}{{/linkTo}}. It builds the URL correctly, but it fails when I click on it.
This is a hand-written <a> tag: {{game.name}}. It works perfectly.
The Url of each board should follow this format:
/games/1/boards/5
But when I write the {{#linkTo 'board' board}} what I get is:
/games/undefined/boards/5
Here is the code (You can see a "working" copy here in JBin. But it's not functional, because it relays on a local REST app):
The Router:
MGames.Router.map(function () {
this.resource('games', function () {
this.resource ('game', {path: '/:game_id'}, function () {
this.resource('board', {path: '/boards/:board_id'});
});
});
});
MGames.IndexRoute = Ember.Route.extend({
redirect: function () {
this.transitionTo('games');
}
});
MGames.GamesRoute = Ember.Route.extend ({
model: function() {
return MGames.Game.findAll();
}
});
MGames.GameRoute = Ember.Route.extend ({
model: function(params) {
return MGames.Board.findAllByGame(params.game_id);
}
});
MGames.BoardsRoute = Ember.Route.extend ({
model: function(params) {
return this.modelFor('game').then(
function (game) {
return MGames.Board.find(game.get('id'), params.board_id);
}
);
}
});
The models:
MGames.Game = Ember.Object.extend({
id: null,
name: null,
icon: null
});
MGames.Game.reopenClass({
findAll: function() {
var url = [MGames.GAMES_API_URL];
url.push ('games');
url = url.join('/');
var result = Ember.ArrayProxy.create({ content: [] });
$.getJSON(url).then (
function (response) {
response.forEach(function (child) {
result.pushObject (MGames.Game.create(child));
});
}
);
return result;
},
find: function (id) {
var url = [MGames.GAMES_API_URL];
url.push ('games');
url.push (id);
url = url.join('/');
var game = MGames.Game.create({ isLoaded: false });
$.getJSON(url).then (
function(response) {
game.setProperties(response);
game.set('isLoaded', true);
}
);
return game;
}
});
MGames.Board = Ember.Object.extend({
id: null,
name: null,
owner: null,
game: null,
is_public: null,
created_at: null
});
MGames.Board.reopenClass({
findAllByGame: function (game) {
var url = [MGames.GAMES_API_URL];
url.push ('games');
url.push (game);
url.push ('boards');
url = url.join('/');
var result = Ember.ArrayProxy.create({ content: [] });
$.getJSON(url).then (
function (response) {
console.log (response);
response.forEach(function (child) {
result.pushObject (MGames.Board.create(child));
});
}
);
return result;
},
find: function (game, board) {
url = [MGames.GAMES_API_URL];
url.push ('games');
url.push (game);
url.push ('boards');
url.push (board);
url = url.join('/');
var result = MGames.Board.create();
$.getJSON(url).then (
function(response) {
result.setProperties(response);
}
);
return result;
}
});
And the template:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>MGames</title>
<link rel="stylesheet" href="css/bootstrap.css">
<link rel="stylesheet" href="css/main.css">
</head>
<body>
<script type="text/x-handlebars">
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<div class="nav-collapse collapse">
<ul class="nav">
<li class="active">{{#linkTo 'index' class="brand"}}MGames{{/linkTo}}</li>
</ul>
</div>
</div>
</div>
</div>
<div class="container">
{{outlet}}
</div>
</script>
<script type="text/x-handlebars" id="games">
<div class="row">
<header id="header">
<h1>Games</h1>
</header>
<ul>
{{#each game in controller}}
<li>
{{#linkTo 'game' game}}{{game.name}}{{/linkTo}}
</li>
{{/each}}
</ul>
{{outlet}}
</div>
</script>
<script type="text/x-handlebars" id="game">
<div class="row">
<div class="span3" id="boards">
<header id="header">
<h1>Boards</h1>
</header>
<ul id="board-list">
{{#each board in controller}}
<li>
{{#linkTo 'board' board}}{{board.name}}{{/linkTo}}
</li>
{{/each}}
</ul>
</div>
<div class="span9">
{{outlet}}
</div>
</div>
</script>
<script type="text/x-handlebars" id="board">
<header id="header">
<h1>{{game.name}} - {{name}} <small>{{owner.nickname}}</small></h1>
</header>
</script>
<script src="js/libs/jquery.js"></script>
<script src="js/libs/bootstrap.js"></script>
<script src="js/libs/handlebars.js"></script>
<script src="js/libs/ember.js"></script>
<script src="js/application.js"></script>
<script src="js/router.js"></script>
<script src="js/models/game.js"></script>
<script src="js/models/user.js"></script>
</body>
</html>
Ok, I finally figured out the problem. In my gameRoute I wrote this:
MGames.GameRoute = Ember.Route.extend ({
model: function(params) {
return MGames.Board.findAllByGame(params.game_id);
}
});
It worked when I write the URL directly in the browser bar, because Ember calls the model function, but when following a {{#linkTo}} the model is the one passed as parameter, so the modelfunction isn't called.
So the working code is this one (a little bit simplified):
MGames.GameRoute = Ember.Route.extend ({
model: function (params) {
// This is only called when loading the URL directly,
// not following a link. We load the game, and in the
// setupController we'll load the boards.
return MGames.Game.find(params.game_id);
},
setupController: function(controller, game) {
// This is *always* called, so we load the boards
model = MGames.Board.findAllByGame(game.id);
controller.set('model', model);
}
});
The board route that you are linking to has 2 dynamic segments, but you are providing only one. You need to change to,
{{#linkTo 'board' game board}}The board{{/linkTo}}
The undefined error is probably due to the ArrayController not getting a board as the corresponding game id is being passed as undefined. The above change should fix that too.

Ember Transition with query param

I'm creating an ember.js app. The first page is single field, with a button. On button click, I'd like it to go to the path #/deals/:api_key. However, when I click the button, I'm not clear on the best way to go about it.
Here's what i have so far:
App = Ember.Application.create();
App.Store = DS.Store.extend({
revision: 12,
adapter: 'DS.FixtureAdapter'
});
App.Deal = DS.Model.extend({
name: DS.attr('string')
});
App.Router.map(function() {
this.resource('start', { path: '/' });
this.resource('deals', { path: '/deals/:api_key' });
});
App.DealsRoute = Ember.Route.extend({
model: function(params) {
return App.Deal.find();
}
});
App.StartController = Ember.ObjectController.extend({
apiKey: "",
getDeals: function (model) {
this.transitionToRoute('deals');
}
});
App.DealsView = Ember.View.extend({
didInsertElement: function() {
// Add active class to first item
this.$().find('.item').first().addClass('active');
this.$().find('.carousel').carousel({interval: 1000});
}
});
<script type="text/x-handlebars" data-template-name="start">
{{view Em.TextField valueBinding="apiKey" placeholder="API Key"}}
<br />
<button {{action 'getDeals'}} class="btn btn-large">Get Won Deals!</button>
</script>
<script type="text/x-handlebars" data-template-name="deals">
<div id="carousel" class="carousel slide">
<div class="carousel-inner">
{{#each model}}
<div class="item">
{{name}}
</div>
{{/each}}
</div>
</div>
</script>
Any suggestions on the right way to pass data from a text input into the next transition as a query param?
you need to pass the parameter in the view in a linkTo helper, e.g.
{{#linkTo 'deals' api_key}}go to deals{{/linkTo}}
this generates a link with the dynamic section you need.
go to deals
check the docs about linkTo for more info: http://emberjs.com/guides/templates/links/

Unable to get a simple sample of Handlebars to work

I have a simple example with Handlebars, and I cannot get it to work! Please help. I am trying to get the google app engine guestbook to work with handlebars instead of jinja2
HTML/Template code is as follows:
Hello {{user.nickname}}
<h2>Top 10 Most Recent Guestbook Entries</h2>
{{#each greetings}}
<br>
<small>[<i>{{date.ctime}}</i>]</small>
<b>
{{#if author}}
<code>{{author.nickname}}</code>
{{else}}
<i>anonymous</i>
{{/if}}
</b>
wrote:
{{comment}}
{{/each}}
</script>
<div id="comment_content"> </div>
<form id="comments" action="">
<div><textarea id="comment" name="content" rows="3" cols="60"></textarea></div>
<div><input id="comment_button" type="button" value="submit"></div>
</form>
{{ url_linktext }}
and here is the JS code:
<script>
$(document).ready(function() {
var source = $("#entry-template");
var srcHTML =source.html();
var template = Handlebars.compile(srcHTML);
// put all your jQuery goodness in here.
$("#comment_button").click(function(event) {
var data = JSON.stringify({
"login": "/_ah/login?continue=http%3A//localhost%3A8080/show",
"logout": "/_ah/login?continue=http%3A//localhost%3A8080/show&action=Logout",
"user": {
"nickname": "dummy",
"email": "dummy#me.com"
},
"greetings": [
{
"comment": "COMMENT1",
"date": {
"ctime": "Sun Feb 10 23:20:21 2013"
},
"author": {
"nickname": "dummy",
"email": "dummy#me.com"
}
}
]
})
var html = template(data);
console.log(html);
$("#comment_content").html(html);
});
});
</script>
Here is how I am referencing Handlebar source
Can someone please help?
Thanks a mil
Your big problem is that you're using JSON.stringify to prepare your data for the compiled template function:
var data = JSON.stringify({ ... });
var html = template(data);
That will end up handing the template function a string (which is a JSON representation of the data you want) when it wants just the object. Drop the JSON.stringify and things will start behaving better:
var data = { ... };
var html = template(data);
Demo: http://jsfiddle.net/ambiguous/Axmbd/
Also, this part of your HTML:
{{ url_linktext }}
doesn't appear to be inside the template's <script> so the {{url}} and {{url_linktext}} won't be filled in. You can fix that by moving that <a> to the right place.

Categories

Resources