Ember - Object has no method 'map' - javascript

I am going through the ember.js guides to learn Ember, and I am using the latest builds of ember.js, ember-data.js, and handlebars.js. I am able to successfully set up a basic nav that switches between views. However, upon trying to integrate a model with a {{#each model}}, I get an error message: Uncaught TypeError: Object # has no method 'map'
Many people seem to have asked a related question, but the solution has always been to update the version of ember, which I have already done.
I believe have followed the tutorial precisely and my code so far is as follows.
App = Ember.Application.create();
App.Store = DS.Store.extend({
revision: 12,
// Says we are specifying all models in js
adapter: 'DS.FixtureAdapter'
});
App.Router.map(function() {
this.resource('posts');
this.resource('about');
});
App.PostsRoute = Ember.Route.extend({
model: function() {
return App.Post.find();
}
});
App.Post = DS.Model.extend({
title: DS.attr('string'),
author: DS.attr('string'),
intro: DS.attr('string'),
extended: DS.attr('string'),
publishedAt: DS.attr('date')
});
App.Post.FIXTURES = ({
id: 1,
title: 'Book Title',
author: 'Dave',
publishedAt: new Date('12-27-2012'),
intro: 'This is an introduction to the book',
extended: 'This is an even longer introduction to the book'
}, {
id: 2,
title: 'Book Title 2',
author: 'James',
publishedAt: new Date('08-13-2012'),
intro: 'This is an introduction to another book',
extended: 'This is an even longer introduction to another book'
});
And the relevant markup:
<script type="text/x-handlebars">
<div class="navbar">
<div class="navbar-inner">
{{#linkTo 'index' classNames='brand'}}Brand{{/linkTo}}
<ul class="nav">
<li>{{#linkTo 'about'}}About{{/linkTo}}</li>
<li>{{#linkTo 'posts'}}Posts{{/linkTo}}</li>
</ul>
</div>
</div>
{{outlet}}
</script>
<script type="text/x-handlebars" id="about">
<div class="about">
<p>Here is some text about the page</p>
</div>
</script>
<script type="text/x-handlebars" id="posts">
<div class="container-fluid">
<div class="row-fluid">
<div class="span3">
<table class='table'>
<thead>
<tr><th>Recent Posts</th></tr>
</thead>
{{#each model}}
<tr>
<td>{{title}} <small class='muted'>by {{author}}</small></td>
</tr>
{{/each}}
</table>
</div>
<div class="span9">
</div>
</div>
</div>
</script>
All help is much appreciated, and sorry if the formatting of this question is awful - it's my first question! Cheers!

The main issue here is that your {{#each}} statement is a little malformed, you should be iterating over the controller object like so:
{{#each controller}}
<tr>
<td>{{title}} <small class='muted'>by {{author}}</small></td>
</tr>
{{/each}}
This is because Ember controllers act a a proxy to their object or—in the case of Ember.ArrayController—to their array.

Try using controller instead of model to iterate over the models in an array controller.
{{#each controller}}
<tr>
<td>{{title}} <small class='muted'>by {{author}}</small></td>
</tr>
{{/each}}

Related

MEAN Stack: Page not rendering at all

I was working on developing a comment page based on https://thinkster.io/mean-stack-tutorial. But the page does not appear at all. Here is the code:
In index.ejs in views directory:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous"/>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.10/angular-ui-router.js"></script>
<script src="/javascripts/ang.js"></script>
</head>
<body ng-app="peopleComments">
<script type="text/ng-template" id="/home.html">
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="page-header">
<h2>Learn. Share. Popularize.</h2>
</div>
<p>Share here to let the world know.</p>
<hr/>
<div ng-repeat="comment in comments|orderBy:'-upvotes'" style="line-height:25px">
{{comment.username}} - {{comment.contents}}
<br>
{{comment.upvotes}}
<span class="glyphicon glyphicon-triangle-top" ng-click="increaseUpvotes(comment)" style="color:green"></span>
{{comment.downvotes}}
<span class="glyphicon glyphicon-triangle-bottom" ng-click="increaseDownvotes(comment)" style="color:red"></span>
<hr/>
</div>
<form ng-submit="addComment()">
<div class="col-xs-2">
<div class="form-group">
<input type="text" class="form-control" placeholder="Your Name" ng-model="username"></input>
</div>
</div>
<div class="col-xs-8">
<div class="form-group">
<input type="text" class="form-control" placeholder="What would you like to share?" ng-model="contents"></input>
</div>
</div>
<button class="btn btn-info" type="submit">Add My Entry</button>
</form>
</div>
</div>
</div>
</script>
</body>
</html>
In comments.js in models directory:
var mongoose = require('mongoose');
var CommentSchema = new mongoose.Schema({
username: String,
contents: String,
upvotes: {type: Number, default: 0},
downvotes:{type:Number, default:0}
});
CommentSchema.methods.upvote=function(cb){
this.upvotes+=1;
this.save(cb);
};
mongoose.model('Comment', CommentSchema);
In ang.js in public/javascripts directory:
var app=angular.module('peopleComments',['ui.router']);
app.factory('comments',['$http', function($http){
var c={
comments:[]
};
//loading all existing comments with getAll()
c.getAll=function(){
return $http.get('/comments').success(function(data){
angular.copy(data, c.comments);
});
};
//function which creates the new comments for updating in the database
c.create = function(comment) {
return $http.post('/comments', comment).success(function(data){
c.comments.push(data);
});
};
app.config([
'$stateProvider',
'$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
//setting up a home state
$stateProvider
.state('home', {
url: '/home',
templateUrl: '/home.html',
controller: 'Base',
resolve: {
comment: ['comments', function(comments){ //using resolve property of ui-router - not rendering???
return comments.getAll();
}]
}
});
$urlRouterProvider.otherwise('home');
}]);
app.controller('Base',[
'$scope','comments',function($scope,comments){
$scope.addComment=function(){ //add new comments to the server/display existing ones
$scope.comments=comments.comments;
if(!$scope.username||$scope.username=='') {$scope.username='Anonymous';}
if(!$scope.contents||$scope.contents==''){return;}
comments.create({
username: $scope.username,
contents: $scope.contents,
}); $scope.comments.push({username:$scope.username,contents:$scope.contents,upvotes:0,downvotes:0});
$scope.username='';
$scope.contents='';
}
$scope.comments = [
{username: 'Diana', contents:'In either a quantum world or in a higher dimension, the past, present and future co-exist!', upvotes: 5, downvotes:0},
{username: 'Cindy', contents:'Never wash strawberries or any berry unless you intend to eat them right away or they will mold.', upvotes: 7, downvotes:0}
];
}]);
The comments given above should appear.. But they aren't.. Why??
I think your problem is that you've created a template, but you're not using the template.
I'm not 100% sure you need a template but try:
<div ng-include src="home.html"></div>
See this example of switching templates dynamically JSFiddle
It looks like the Controller will not wait for comment to load because it doesn't depend on it. Making the controller depend on the comment promise as well as the comments service should make the dependency clear to Angular.
app.controller('Base',[
'$scope','comments', 'comment', function($scope,comments,_comment){
// ...
}]);
The mistake was that I had to add
<ui-view></ui-view>
where I needed my template to load as per the ui-router syntax.

Emberjs: Self-referencing relationship not loading children when in a controller variable

I am having an issue with nested components and there data.
I have a couple models like the following:
Category:
App.Category = DS.Model.extend({
name: DS.attr('string'),
...,
choices: DS.hasMany('categoryChoices')
});
CategoryChoice:
App.CategoryChoices = DS.Model.extend({
name: DS.attr('string'),
...,
children: DS.hasMany('categoryChoices')
});
Then I have a view template like this:
<div>
{{#each category}}
{{category-selector category=this}}
{{/each}}
</div>
Now the component templates:
Category Selector:
<div class='header'>
{{category.name}}
</div>
<div class='choices'>
{{#each category.choices}}
{{category-choice-selector choice=this}}
{{/each}}
</div>
Category Choice Selector:
<div>
{{choice.name}}
</div>
<div class='children'>
{{#each choice.children}}
{{category-choice-selector choice=this}}
{{/each}}
</div>
This is just a rough example, but the problem is that the children of the choices never get loaded. So if I have a category like the following:
Food
Meat
Steak
Chicken
Fruit
Pear
Apple
The choices under Meat and Fruit never appear. Not sure what I am doing wrong here.

codeschool ember js 5.3

I have been going through the emberjs course on codeschool and I came across this interesting problem. In 5.3, the question I am stuck on is:
Provide a model for the ContactsIndex Route which will provide just
one model – the contact for Anostagia. You can look this contact up by
ID.
No matter what I try it will not accept this app.js file?
I am nearly 90% sure I have this setup correctly... any thoughts?
var App = Ember.Application.create({
LOG_TRANSITIONS: true
});
App.Router.map(function() {
this.route('credits', { path: '/thanks' });
this.resource('products', function() {
this.resource('product', { path: '/:product_id' });
});
this.resource('contacts', function() {
this.resource('contact', { path: '/:contact_id' });
});
});
App.IndexController = Ember.ArrayController.extend({
productsCount: Ember.computed.alias('length'),
logo: 'images/logo-small.png',
time: function() {
return (new Date()).toDateString();
}.property()
});
App.ContactsIndexController = Ember.ObjectController.extend({
contactName: Ember.computed.alias('name'),
avatar: 'images/avatar.png',
open: function() {
return ((new Date()).getDay() === 0) ? "Closed" : "Open";
}.property()
});
App.ProductsController = Ember.ArrayController.extend({
sortProperties: ['title']
});
App.ContactsController = Ember.ArrayController.extend({
sortProperties: ['name']
});
App.ContactsIndexRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('contact', params.contact_id);
}
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return this.store.findAll('contact');
}
});
App.ProductsRoute = Ember.Route.extend({
model: function() {
return this.store.findAll('product');
}
});
App.ContactsRoute = Ember.Route.extend({
model: function() {
return this.store.findAll('contact');
}
});
App.IndexRoute = Ember.Route.extend({
model: function(){
return this.store.findAll('product');
}
});
App.ApplicationAdapter = DS.FixtureAdapter.extend();
App.Product = DS.Model.extend({
title: DS.attr('string'),
price: DS.attr('number'),
description: DS.attr('string'),
isOnSale: DS.attr('boolean'),
image: DS.attr('string'),
reviews: DS.hasMany('review', { async: true }),
crafter: DS.belongsTo('contact', { async: true })
});
App.Product.FIXTURES = [
{ id: 1,
title: 'Flint',
price: 99,
description: 'Flint is a hard, sedimentary cryptocrystalline form of the mineral quartz, categorized as a variety of chert.',
isOnSale: true,
image: 'images/products/flint.png',
reviews: [100,101],
crafter: 200
},
{
id: 2,
title: 'Kindling',
price: 249,
description: 'Easily combustible small sticks or twigs used for starting a fire.',
isOnSale: false,
image: 'images/products/kindling.png',
reviews: [],
crafter: 201
},
{
id: 3,
title: 'Matches',
price: 499,
description: 'One end is coated with a material that can be ignited by frictional heat generated by striking the match against a suitable surface.',
isOnSale: true,
reviews: [103],
image: 'images/products/matches.png',
crafter: 201
},
{
id: 4,
title: 'Bow Drill',
price: 999,
description: 'The bow drill is an ancient tool. While it was usually used to make fire, it was also used for primitive woodworking and dentistry.',
isOnSale: false,
reviews: [104],
image: 'images/products/bow-drill.png',
crafter: 200
},
{
id: 5,
title: 'Tinder',
price: 499,
description: 'Tinder is easily combustible material used to ignite fires by rudimentary methods.',
isOnSale: true,
reviews: [],
image: 'images/products/tinder.png',
crafter: 201
},
{
id: 6,
title: 'Birch Bark Shaving',
price: 999,
description: 'Fresh and easily combustable',
isOnSale: true,
reviews: [],
image: 'images/products/birch.png',
crafter: 200
}
];
App.Contact = DS.Model.extend({
name: DS.attr('string'),
about: DS.attr('string'),
avatar: DS.attr('string'),
products: DS.hasMany('product', { async: true })
});
App.Contact.FIXTURES = [
{
id: 200,
name: 'Giamia',
about: 'Although Giamia came from a humble spark of lightning, he quickly grew to be a great craftsman, providing all the warming instruments needed by those close to him.',
avatar: 'images/contacts/giamia.png',
products: [1]
},
{
id: 201,
name: 'Anostagia',
about: 'Knowing there was a need for it, Anostagia drew on her experience and spearheaded the Flint & Flame storefront. In addition to coding the site, she also creates a few products available in the store.',
avatar: 'images/contacts/anostagia.png',
products: [2]
}
];
App.Review = DS.Model.extend({
text: DS.attr('string'),
reviewedAt: DS.attr('date'),
product: DS.belongsTo('product')
});
App.Review.FIXTURES = [
{
id: 100,
text: "Started a fire in no time!"
},
{
id: 101,
text: "Not the brightest flame, but warm!"
}
];
The index.html file (if needed):
<!DOCTYPE html>
<html>
<head>
<base href='http://courseware.codeschool.com/ember/' />
<link href='bootstrap.css' rel='stylesheet' />
<link href='application.css' rel='stylesheet' />
<script src='jquery.js'></script>
<script src='handlebars.js'></script>
<script src='ember.js'></script>
<script src='ember-data.js'></script>
<script src='app.js'></script>
</head>
<body>
<script type='text/x-handlebars' data-template-name='application'>
<div class='navbar navbar-default'>
<div class='container'>
{{#link-to 'index' class='navbar-brand'}}<img src='images/logo.png' alt='logo' height='34' width='224' />{{/link-to}}
<ul class='nav navbar-nav navbar-right'>
{{#link-to 'index' tagName='li'}}Home{{/link-to}}
{{#link-to 'products' tagName='li'}}Products{{/link-to}}
{{#link-to 'contacts' tagName='li'}}Contacts{{/link-to}}
</ul>
</div>
</div>
<div class="container">
{{outlet}}
</div>
<footer class='container'>
<hr />
<p class='pull-left'>© 2013 The Flint & Flame</p>
<p class='pull-right'>{{#link-to 'credits'}}Credits{{/link-to}}</p>
</footer>
</script>
<script type='text/x-handlebars' data-template-name='index'>
<div class="jumbotron">
<h1>Welcome to The Flint & Flame!</h1>
<p class="tagline">
<img {{bind-attr src='logo'}} alt='Logo' />
Everything you need to make it through the winter.
</p>
<p>
{{#link-to 'products' class='btn btn-primary btn-lg'}}
Browse All {{productsCount}} Items »
{{/link-to}}
</p>
</div>
<p class='pull-right text-muted'>Rendered on {{time}}</p>
</script>
<script type='text/x-handlebars' data-template-name='contacts/index'>
<div class='row'>
<img {{bind-attr src='avatar'}} alt='Avatar' class='img-thumbnail col-sm-4'/>
<div class='col-sm-8'>
<h1>About The Fire Sprites</h1>
<p>Contact {{contactName}} for more info!</p>
<p>Current Status: {{open}}.</p>
</div>
</div>
</script>
<script type='text/x-handlebars' data-template-name='credits'>
<h1>Thanks for the Help!</h1>
<p>This site would not be possible without the hardworking Ember Core Team!</p>
</script>
<script type="text/x-handlebars" data-template-name='products'>
<div class='row'>
<div class='col-sm-3'>
<div class='list-group'>
{{#each}}
{{#link-to 'product' this classNames='list-group-item'}}
{{title}}
{{/link-to}}
{{/each}}
</div>
</div>
<div class='col-sm-9'>
{{outlet}}
</div>
</div>
</script>
<script type='text/x-handlebars' data-template-name='product'>
<div class='row'>
<div class='col-sm-7'>
<h2>{{title}}</h2>
<h3 class="text-success">${{price}}</h3>
<p class="text-muted">{{description}}</p>
<p>Finely crafted by {{#link-to 'contact' crafter}}{{crafter.name}}{{/link-to}}.</p>
<h3>Reviews</h3>
<ul>
{{#each reviews}}
<li><p>{{text}}</p></li>
{{else}}
<li><p class='text-muted'><em>No reviews yet. Be the first to write one!</em></p></li>
{{/each}}
</ul>
</div>
<div class='col-sm-5'>
<img {{bind-attr src='image'}} class='img-thumbnail img-rounded'/>
</div>
</div>
</script>
<script type='text/x-handlebars' data-template-name='products/index'>
<p class='text-muted'>Choose a product from those on the left!</p>
</script>
<script type="text/x-handlebars" data-template-name='contacts'>
<div class='row'>
<div class='col-sm-9'>
{{outlet}}
</div>
<div class='col-sm-3'>
<div class='list-group'>
{{#each}}
{{#link-to 'contact' this classNames='list-group-item'}}
{{name}}
{{/link-to}}
{{/each}}
</div>
</div>
</div>
</script>
<script type='text/x-handlebars' data-template-name='contact'>
<div class='row'>
<div class='col-sm-5'>
<img {{bind-attr src='avatar' alt='name'}} class='img-thumbnail img-rounded'/>
</div>
<div class='col-sm-7'>
<h2>{{name}}</h2>
<p>{{about}}</p>
<h3>Products</h3>
<ul>
{{#each products}}
<li>{{#link-to 'product' this}}{{title}}{{/link-to}}</li>
{{/each}}
</ul>
</div>
</div>
</script>
</body>
</html>
It looks like I was being too robust for this question. The answer was the following adjustment:
App.ContactsIndexRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('contact', 201);
}
});

Ember.js - index on nested route duplicating/not destroying on exit

Having an issue with ember not destroying the index on exit.
Controllers/Routes:
App.AccountRoute = Ember.Route.extend({
activate: function () {
//Doing some stuff with login state here. Not important.
}
});
App.AccountController = Ember.Controller.extend({
needs: ['application'], //dependency
account: Ember.computed.alias('controllers.application.accountData'),
states: Ember.computed.alias('controllers.application.states'),
userToken: Ember.computed.alias('controllers.application.userToken'),
tabs: [{'pinned': true, 'name': { 'nestedLink': 'account.index', long: 'Account Overview' }}, {'name': { 'nestedLink': 'account.edit-profile', long: 'Edit Your Company Profile' }}, {'name': { 'nestedLink': 'account.edit-listings', long: 'Edit Your Company Listings' }}, {'name': { 'nestedLink': 'account.edit-payment-methods', long: 'Edit Your Saved Payment Methods' }}, {'name': { 'nestedLink': 'account.view-orders', long: 'View Orders' }}],
});
App.AccountIndexController = Ember.Controller.extend({
needs: ['account']
});
And here's the router:
App.Router.map(function () {
//...
this.resource('account', function() {
this.route('edit-profile');
this.route('edit-listings');
this.route('edit-payment-methods');
this.route('view-orders');
});
});
And the account template is set up like the following with the links to each nested route like: {{#linkTo account.index}}{{/linkTo}} {{#linkTo account.view-orders}}{{/linkTo}}
<script type="text/x-handlebars" data-template-name="account">
<h2>Account for {{account.name.company}}</h2>
<hr />
<div class="row">
<div class="col-md-2 account-sidebar">
<ul class="list-group">
{{#each tabs}}
{{#if pinned}}
{{#linkTo name.nestedLink class="list-group-item pinned-item"}}
{{name.long}}
{{/linkTo}}
{{else}}
{{#linkTo name.nestedLink class="list-group-item"}}
{{name.long}}
{{/linkTo}}
{{/if}}
{{else}}
<p class="text-danger">There are no options for your account.</p>
{{/each}}
</ul>
</div>
<div class="col-md-10 account-content">
{{outlet}}
</div>
</div>
</script>
<script type="text/x-handlebars" data-template-name="account/index">
<h3>Account Overview</h3>
</script>
Switching back and forth between the overview (index) tab and a nested route results in:
You're missing a closing </div> in your "account/index" template. It should be
<script type="text/x-handlebars" data-template-name="account/index">
<h3>Account Overview</h3>
<hr />
<div class="row">
<div class="col-md-6">
<h4>Account Created:</h4>
<p class="text-muted"></p>
</div>
<div class="col-md-6">
<h4>Account Address:</h4>
</p>
</div>
</div>
</script>
See http://emberjs.jsbin.com/novib/3/

emberjs append works but raises Assertion Failed error

I'm new to ember I am trying to append a template to another and it seems to work but it raises an error, can you please explain why?
The error:
Assertion failed: You cannot append to an existing Ember.View. Consider using Ember.ContainerView instead
This is the code in app.js
App.NewStickie = Ember.View.extend({
click: function(evt){
var stickie = Ember.View.create({
templateName: 'stickie',
content: 'write your notes here'
});
stickie.appendTo('#stickies');
}
});
These are the contents of index.html
<script type="text/x-handlebars">
{{#view App.NewStickie}}
<button type="button" class="btn btn-success">
New
</button>
{{/view}}
{{outlet}}
</script>
<script type="text/x-handlebars" id="index">
<div id="stickies">
{{#each item in model}}
<div class="stickie" contenteditable="true">
{{#view App.DeleteStickie}}
<span class="glyphicon glyphicon-trash"></span>
{{/view}}
<div contenteditable="true">
{{item.content}}
</div>
</div>
{{/each}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="stickie">
<div class="stickie">
{{#view App.DeleteStickie}}
<span class="glyphicon glyphicon-trash"></span>
{{/view}}
<div contenteditable="true">
{{view.content}}
</div>
</div>
</script>
Each view in ember have a template, for example:
foo_view.js
App.FooView = Ember.View.extend({
templateName: 'foo'
})
foo template
<script type="text/x-handlebars" data-template-name="index">
<div id=myFooView>Foo</div>
</script>
You are trying to insert a view inside of other in that way:
App.BarView.create().appendTo('#myFooView')
This isn't allowed. You can use the {{view}} handlebars helper to render a view inside other like that:
foo template
<script type="text/x-handlebars" data-template-name="index">
<div id=myFooView>
Foo
{{view App.BarView}}
</div>
</script>
But I think that you want this working dynamically. So you can use the ContainerView, like described by the error message:
App.StickiesView = Ember.ContainerView.extend({
click: function() {
var stickie = Ember.View.create({
templateName: 'stickie',
content: 'write your notes here'
});
this.pushObject(stickie);
}
})
I see in your code a lot of views with the click event, ember encourage you to use actions, this give more flexibility, error/loading handling etc. I think is a good idea to use it.
I hope it helps
You should probably read this guide that explains that ContainerView is. Also, I don't think it's necessary to create another View to append a template to another template.

Categories

Resources