Ember.js get attributes from template using {{render}} helper - javascript

This question is related to this one: Ember.js {{render}} helper model not correctly set
But I think that I ask the wrong question.
Router
App.Router.map(function () {
this.resource('article', {path: '/article/:id'});
this.resource('article.new', {path: "/article/new"});
});
I have not defined a route or resource for categorynew because it is rendered as a popup within both Article and Article.new.
Template
<script type="text/x-handlebars" data-template-name="article">
{{render "category/new"}}
</script>
<!-- popups -->
<script type="text/x-handlebars" data-template-name="category/new">
Name: {{input type="text" value=name}}
Image: {{view App.UploadFile name="image" file=image }}
Category-parent: {{input value=categoryRelation}}
<button {{action 'saveCategory'}}>Save</button>
</script>
Controller
App.CategoryNewController = Ember.ObjectController.extend({
actions: {
saveCategory: function () {
var newCategory = this.store.createRecord('category', {
name: this.get('name'),
image: this.get('image'),
category_parent:this.get('category_parent')
});
newCategory.save();
console.log(this.get('naam')); // undefinded
}
}
});
When I fill the form that gets rendered with {{render category/new}} I get these errors:
Assertion failed: Cannot delegate set('name', a) to the 'content' property of object proxy <App.CategoryNewController:ember387>: its 'content' is undefined. ember-1.1.2.js:417
Uncaught Error: Object in path nam could not be found or was destroyed.
I think there must be a model in the controller. But if I do a this.get('model') it is always the wrong model. Even if I define it in App.CategoryNewRoute.

When you call render you can supply it a model, but you aren't supplying it a model. Your controller on the other hand extends ObjectController, which tells ember it's backed by a model. So either you can supply it a model, or you can change it to extend Controller (and everything will live on the controller instead of on a non-existent model).
App.CategoryNewController = Ember.Controller.extend({
name is spelled wrong in the console.log, but I'm pretty sure that's just a typo while putting it on SO.
http://emberjs.jsbin.com/EtafEFUr/1/edit

Related

Iron router with Meteor display data to template

I am using iron router with Meteor (latest versions). I have a template:
<template name="home">
{{#each products}}
<p>{{name}}</p>
{{/each}}
JJJJJJJJJJJJ
</template>
and in lib/router.js:
Router.configure({
layoutTemplate:'layout'
});
Router.map(function () {
this.route('home', {
path:'/',
template:'home',
data : function () {
return Products.find();
}
});
});
When I run the page, I see empty page with this JJJJJJJJJJJJ, added for test to see is it template loads.
In the Products collection are 2 items with name. I can read (select), add, remove items for this collection via WEB browser console, but the collection is not rendered in the template.
What can be the error ?
The data function of Iron Router sets the data context for the template. In your case, you are setting the data context to be a cursor but attempting to access the cursor with products, which does not exist as a helper, either registered globally or on the template itself.
There's a few ways you can fix this, but I would suggest letting the router simply determine which template to render and let the template fetch it's own data.
Adjusted router:
Router.map(function () {
this.route('home', {
path: '/',
template: 'home'
}
);
Template helper:
Template.home.helpers({
products() {
return Products.find();
}
});
Alternatively, you can use the this keyword within the template to access the current data context:
<template name="home">
{{#each this}}
<p>{{name}}</p>
{{/each}}
JJJJJJJJJJJJ
</template>
However, it is not easy to discern what is being put into this unless you can follow the flow of the data through the route to the template. This also couples this template very tightly with this route, as the route itself is determining the context of your template.
The solution is in lib/router.js, need to set attribute data like this :
data : function () {
templateData = {
products: Products.find()
};
return templateData;
}
, NOT like before - return Products.find(); .

Ember-CLI Route model data not affecting template

I have an ember route webtest contained within the file routes/webtest.js:
export default Ember.Route.extend({
model: function() {
return {
title: "Does not appear"
};
}
});
I have the matching template contained within templates/webtest.hbs
<div class="container">
<h2>Title: {{title}}</h2>
</div>
when I navigate to the page /webtest in my web browser with ember serve
the resulting page has the title: text, but not the text does not appear
I have been to multiple ember pages, and I have the same code working here:
http://jsbin.com/neheru/6/edit?html,js,output
The goal is to be able to have a variable accessible from within the template webtest that can be accessed by a route.
FYI I'm trying to get it to the template so I can pass variables to a component
The variable should be {{model.title}}
You can log out variables from your template with
{{log title}}
{{log controller.title}}
{{log model.title}}
This might help you to debug faster.
You should then be able to pass data in to our component with something like:
{{my-component title=model.title}}

Ember.js Dynamic segment not working

I'm using Ember 1.0.0 and the latest build of Ember Data (beta), and I have a route with a dynamic segment that isn't working.
I have defined the routes below:
PwdMgr.Router.map(function() {
this.resource("passwords", function(){
this.resource("password", {path: "/:password_id"}, function(){
this.route("edit");
});
});
}
In the template passwords.index I display a list of models like this:
{{#each}}
<tr>
<td>{{id}}</td>
<td>{{name}}</td>
<td>{{client.name}}</td>
<td>{{service.name}}</td>
<td>{{localisation.name}}</td>
<td>{{status.name}}</td>
<td>{{login}}</td>
<td>{{password}}</td>
<td>
{{#link-to 'password.index' this}}<span class="glyphicon glyphicon-search"></span>{{/link-to}}
{{#link-to 'password.edit' this}}<span class="glyphicon glyphicon-pencil"></span>{{/link-to}}
<span class="glyphicon glyphicon-remove" {{action 'edit' password}}></span>
</td>
</tr>
{{/each}}
I have two links, one that goes to the route password.index and one to the route passwword.edit. I provide the model for the dynamic segment and the handlebars creates the URL correctly (/passwords/1 and /passwords/1/edit).
My problem is that when I get to the URL /password/1 (or /password/1/edit), the model is not a single object but an array of objects.
Since I'm using the default pattern, as explained in the guides, I didn't setup Route objects. But for debugging purposes I created a route object for the password.index route. Here's what it looks like:
PwdMgr.PasswordIndexRoute = Ember.Route.extend({
model: function(params){
console.log(params);
return this.get('store').find('password',params.password_id);
},
setupController: function(controller, model){
console.log(model);
}
});
And here's my console log:
Object {} app.js (line 31)
<DS.RecordArray:ember435> { content=[3], store=<DS.Store:ember506>, isLoaded=true, more...} app.js (line 35)
The empty object explains why I get an array of object but is there a reason why the params variable is an empty object?
Thanks a lot
[EDIT]
I have changed my Router.map like so:
PwdMgr.Router.map(function() {
this.resource("passwords", function(){
this.route("detail", {path: "/:password_id"});
this.route("edit", {path: "/:password_id/edit"});
});
}):
And the dynamic segment for both "detail" and "edit" routes works fine. I think the problem comes from the fact that the dynamic segment is in the nested resource, which is strange because the Emberjs guides' examples are with dynamic segments in nested resources.
password/1 doesn't appear to be a real route, I'd try passwords/1, and get rid of the slash on the path.
PwdMgr.Router.map(function() {
this.resource("passwords", function(){
this.resource("password", {path: ":password_id"}, function(){
this.route("edit");
});
});
}
Or change it to
PwdMgr.Router.map(function() {
this.resource("passwords", function(){});
this.resource("password", {path: "password/:password_id"}, function(){
this.route("edit");
});
}
I found my mistake thanks to Daniel's comment.
My routes were setup like this (before my edit):
passwords
password
password.detail
password.edit
password.index
I was using the route PwdMgr.PasswordsRoute to setup my models and its corresponding template passwords.
The problem was that I was in the passwords route and going directly to the password.detail route. I think there was a problem going from passwords to password.detail (or password.edit) with the model parameter.
Anyway, once I changed my route to PwdMgr.PasswordsIndexRoute and the corresponding template to passwords/index, everything was going as expected. Models were passed correctly through the dynamic segment.
Thanks a lot Daniel for pointing out my error.

text/x-handlebars never showing up

I am trying to develop my first application, and I can't get the browser to display my handlebars scripts
Here is my html :
<!doctype html>
<html>
<head>
<title>Random Presents</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<script src="lib/jquery.min.js"></script>
<script src="lib/handlebars.js"> </script>
<script src="lib/ember.js"></script>
<script src ="js/app.js"></script>
</head>
<body>
<script type="text/x-handlebars">
{{#view App.AView}}{{firstName}}{{/view}}
</script>
<script type="text/x-handlebars">
{{#view App.AView}}{{surname}}{{/view}}
</script>
</body>
</html>
and my app.js file :
App = Ember.Application.create();
App.AView = Ember.View.extend({
tagName: 'span',
firstName: 'Joe',
surname: 'Bloggs'
});
When I load the file; the page is empty, even though the source corresponds to my html file.
I don't see any error in the chrome javascript console.
Is there something really obvious that I miss?
I tested the libraries, they directly come from the website and are on last version.
Worse, I actually even tried with a script containing only html and he won't load either.
Because you created the properties in the View class, you should use the view property which is somewhat a pointer like the this keyword in some cases.
Change your template to:
<script type="text/x-handlebars">
{{#view App.AView}}
{{view.firstName}}
{{view.surname}}
{{/view}}
</script>
Because of Ember conventions, both handlebars templates from your code represent the same thing. Ember assumes the template name to be "application" when there is no name. This means that even if you fix the property to be {{view.propertyName}} on both templates, the later one will override the first (or all predecessors with the same name), because Ember will compile the templates (with Handlebars) into template functions and the name will be used as a key to a collection of templates (path Ember.TEMPLATES), so that's why you'd have move those expressions to a single template like in the code above.
But you should avoid using views like this.
Views should display data, but shouldn't keep data. Your data should live in a model (in the store) and a view should ask the controller for data, and it should get it from the sore. The controller should be populated with data from the store through the router (it knows what to do and when to do it).
I'm not saying this in a bad way at all; just trying to save you from driving on the wrong side of the street since you're starting on Ember.
It's well known that there are a lot of outdated tutorials and this causes a lot of confusion sometimes (there's a repo with outdated tutorials/articles which should be receiving notifications to update or add a disclaimar). But in general, I would suggest you to follow the guides, watch some videos about Ember, check other resources available on the internet.
Here's a commented code of a very basic sample application just to show some of the features you could and should be using:
Handlebars:
<!--
when a template doesn't have a data-template-name, Ember assumes this is the
application main template. This is usually where you'd render the layout structure
and also where you'd put the main outlet
-->
<script type="text/x-handlebars">
<h1>Example</h1>
{{outlet}}
</script>
<!--
As per convention, a named template should match with its route name
There are ways around using "partial", "render", or even defining
a View class and setting the templateName property to a different name, or
using the route's renderTemplate hook
Another thing. You can have nested views when using nested routes
This view template has another outlet to display a person from the collection
-->
<script type="text/x-handlebars" data-template-name="people">
{{#each person in controller}}
{{#linkTo people.person person}}
{{person.fullName}}
{{/linkTo}}<br />
{{/each}}
<hr />
{{outlet}}
</script>
<!--
Unlike the very first code piece in this answer, when you have a view or
template connected to a controller, you can access the data from the controller
using handlebars expressions.
-->
<script type="text/x-handlebars" data-template-name="people/person">
First name: {{view Ember.TextField valueBinding="firstName"}}<br />
Last name: {{view Ember.TextField valueBinding="lastName"}}<br />
Full Name: {{fullName}}
</script>
JavaScript:
window.App = Ember.Application.create();
// defining routes which are somewhat like states (think of a state machine)
// they also provide the ability to have hash urls
// the router is a very important piece of ember due to conventions
App.Router.map(function() {
// sample url ~/#/people
this.resource('people', function() {
// sample url ~/#/people/1
this.route('person', { path: ':person_id' });
});
});
// In this route we provide the data to the list view in "people" template
// the data will actually go to the controller 'content' property which can
// be a type of array for arraycontroller or a single object for object controller
// this should allow the view to call data from the controller
App.PeopleRoute = Em.Route.extend({
model: function() {
return App.Person.find()
}
});
// in this route we provide data for the "people/person" template
// In this case we are using the person id from the parameters to query our
// application store.
App.PeoplePersonRoute = Em.Route.extend({
model: function(params) {
return App.Person.find(params.person_id)
}
});
// This is the very first route of the application
// Most of the time, you'll simply redirect from your index to a resource
// in this example, from ~/#/ to ~/#/people
App.IndexRoute = Em.Route.extend({
redirect: function() {
this.transitionTo('people');
}
});
// The store manages your application data. Normally you only have to define
// the revision since it's not 1.0 yet (https://github.com/emberjs/data/blob/master/BREAKING_CHANGES.md)
// for this sample, I'm using the Fixture Adapter so I can add mock up data to the
// app while testing/coding front end
App.Store = DS.Store.extend({
revision: 11,
adapter: 'DS.FixtureAdapter'
});
// Using Ember-Data, you can define a Model object which uses application
// semantics to describe your data, and does many operations which you'd
// normally expect to see in a ORM. Ember-Data is no ORM, but it gets pretty close
// and in certain scenarios it goes beyond
App.Person = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
fullName: function() {
return '%# %#'.fmt(
this.get('firstName'),
this.get('lastName')
);
}.property('firstName', 'lastName')
});
// Using the FixtureAdapter you can add mockup data to your data store
App.Person.FIXTURES = [
{id: 1, firstName: 'Joe', lastName: 'Bloggs'},
{id: 2, firstName: 'Other', lastName: 'Dude'}
];
// when your controller wants to handle a collection, use ArrayController
App.PeopleController = Em.ArrayController.extend();
// when it handles a single object, use ObjectController
App.PeoplePersonController = Em.ObjectController.extend();
Within a template the default context is the controller, so you need to explicitly reference the view to access its properties: {{view.property}}
In your example:
{{#view App.AView}}{{view.surname}}{{/view}}
Working example JSBin

Ember.js Appropriately reference property of unrelated item

Assuming an ember.js router application with an application controller, application view, a view type that I plug and play with, and an unrelated controller that handles external data. How can that third view have a computed property from the unrelated controller, what do I put into the .property() elipses so that it gets notified of changes?
e.g.
App.ExternalDataController = Em.Controller.extend
stellarProperty: 'super value' #I want OtherView to have a computer property referencing this
App.ApplicationController = Em.ArrayController.extend
content: [] #Assume a bunch of values of some kind here
App.ApplicationView = Em.View.extend
templateName: 'app-view'
App.OtherView = Em.View.extend
templateName: 'other-view'
someComputedProperty: (->
App.router.externalDataController.get('stellarProperty') + ' flying pigs'
).property() #What do I put in that property elipses?
templates
<script type="text/x-handlebars" data-template-name="application">
<div>
{{#each content}}
{{name}} -- {{view App.OtherView}}
{{/each}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="other-view">
<span>Irate goats and {{view.someComputedProperty}}</span>
</script>
The short answer is that you should make the property you need available through the view's controller.
In your example, because OtherView is nested in the ApplicationView's template, it will have a controller property that is set to ApplicationController. In your router, you can call router.applicationController.connectControllers('externalData'). That will set an externalDataController property on applicationController. Then you can expose the property you need:
App.ApplicationController = Em.ArrayController.extend
content: [] #Assume a bunch of values of some kind here
externalDataController: null # set via connectControllers call in router
stellarPropertyBinding: Em.Binding.oneWay('externalDataController.stellarProperty')
And OtherView becomes:
App.OtherView = Em.View.extend
templateName: 'other-view'
someComputedProperty: (->
#get('controller.stellarProperty') + ' flying pigs'
).property('controller.stellarProperty')
Hope that helps!

Categories