I have a data fixture adapter say..
App.Person.reopenClass({
FIXTURES: [
{
id: 1,
name: 'Name1',
},
{
id:2,
name:'Name2'
}
]
});
In my template i want to bind this model with checkboxes..like there are two names in the model so there should be two checkboxes with name as their labels
This is my route
App.IndexRoute=Ember.Route.extend({
model:function(){
return this.store.findAll('person');
}
});
and this is Controller-on click of a button I want to retrieve info about the checkboxes
App.IndexController=Ember.ArrayController.extend({
actions:{
buttonHandler:function(){
//Get Names which are checked/unchecked
}}
});
Is there any way to bind the model with checkboxes and retrieve which checkboxes have been selected in the controller?
I recommend using ObjectProxy like described in this blog post: http://www.poeticsystems.com/blog/ember-checkboxes-and-you
You can avoid putting a "checked" property on the model and must not take care of the serializer trying to save the checked property and so forth.
From the blog post
Proxy Model:
proxiedModel: Ember.computed.map('model', function(model) {
return Ember.ObjectProxy.create({
content: model,
checked: false
});
}
Template:
<ul>
{{#each proxiedModel}}
<li>
{{input type="checkbox" value=checked}}
{{name}}
</li>
{{/each}}
</ul>
App.Person.reopenClass({
FIXTURES: [
{
id: 1,
name: 'Name1',
checked: false
},
{
id:2,
name:'Name2',
checked: false
}
]);
your template:
{{#each controller.model as |obj|}}
{{input type="checkbox" name=obj.name checked=obj.checked}}
{{/each}}
<button {{action 'buttonHandler'}}>Get checked</button>
your controller:
App.IndexController=Ember.ArrayController.extend({
actions:{
buttonHandler:function(){
var checked_only = this.get('model').filterBy('checked', true);
}}
});
Hope this helps...
Related
I have a few questions. The first is why can't I display data without putting 'in model' when using the each helper? Also I'm having issues showing products specific to the category I'm in?
View an image of my inspect here:
Currently when I click on a category, it shows all of my products.
I want to show all of my categories, then list my products associated to the category I clicked ( I need these available at all times). Shouldn't I be able to just do the following?
<ul class="off-canvas-list">
<li><label>Solution Category</label></li>
{{#each category}}
<li class="has-submenu">
{{name}}
<ul class="left-submenu">
<li class="back">Back</li>
{{#each products}}
{{name}}
{{/each}}
</ul>
</li>
{{/each}}
</ul>
Router.js
App.Router.map(function () {
this.route('index', { path: '/' });
this.route('line-of-one');
this.route('operating-room');
this.route('sterile-processing');
this.route('solution');
this.route('department');
this.route('category');
this.resource('products');
this.resource('presentations', { path: '/presentations' }, function () {
// this one is nested and dynamic, we need it to see one user at a time with its id
this.resource('presentation', { path: '/:presentation_id' }, function () {
// another nested one for editing the current user
this.route('edit');
});
// and a last one to create users
this.route('create');
});
// this is our 404 error route - see MissingRoute just bellow
this.route('missing', { path: '/*path' });
});
// this handles wrong routes - you could use it to redirect to a 404 route or like here to redirect to the index page
App.MissingRoute = Em.Route.extend({
redirect: function () {
this.transitionTo('index');
}
});
Application Route
App.ApplicationRoute = Ember.Route.extend({
model: function () {
return this.store.findAll('category')
},
setupController: function (controller, model) {
this._super(controller, model);
controller.set('product', this.store.find('product'));
}
})
Products Route
App.ProductsRoute = Ember.Route.extend({
model: function () { return this.store.find('product') }
})
Category Model
App.Category = DS.Model.extend({
name: DS.attr('string'),
room: DS.attr('string'),
subroom: DS.attr('string'),
products: DS.hasMany('product')
});
App.Category.FIXTURES = [
{
id: 0,
room: "Operating Room",
subroom: "Operating Room",
image: "Content/Images/Products/Case-Goods.png",
name: "Case Goods",
rendertemplate: "sliderTemplate",
sort: 10,
products: [0]
}
]
Product Model
App.Product = DS.Model.extend({
name: DS.attr('string'),
room: DS.attr('string'),
subroom: DS.attr('string'),
image: DS.attr('string'),
category: DS.belongsTo('category')
});
App.Product.FIXTURES = [
{
id: 0,
room: "Operating Room",
subroom: "Operating Room",
category: "Case Goods",
image: "Content/Images/Products/Case-Goods.png",
name: "Case Goods Solutions",
content: "Innovations to reduce procedural delays and keep everything at your fingertips.",
bullets: [
{ content: "Instant access to supplies and equipment to help minimize procedural delays" },
{ content: "Protection of your sterile supplies behind closed doors with durable, easy-to-clean surfaces and either stainless steel or tempered glass door fronts" },
{ content: "Select freestanding or recessed consoles to fit your space" }
],
sellsheet: "Content/Sellsheets/casegoods.html",
rendertemplate: "valuePropTemplate",
sort: 0
}
]
My Navigation
<ul class="off-canvas-list">
<li><label>Solution Category</label></li>
{{#each category in model}}
<li class="has-submenu">
{{category.name}}
<ul class="left-submenu">
<li class="back">Back</li>
{{#each product in model}}
{{{product.name}}}
{{/each}}
</ul>
</li>
{{/each}}
</ul>
1.The context of your the handlebars is the controller, if you used the snippet below it would work
{{#with model}}
{{each product}}
...
{{/each}}
{{/with}}
i see you have not implemented a custom route handler for /products so by default ember will assume that you are looking for all of the products. which makes sense as the url doesnt have any information about the selected category. you could nest product within category, or you could filter the results on the /products route using Query Paramenters
I'm not sure exactly how to explain why this worked but: I was passing my model data to the ApplicationRoute so I didn't need to specify a model to each through. I understand that if your in a view associated with a route and you just use {{#each}} the context will be the data your passing through? The code below ended up working. If someone good provide an explanation that would be greatly appreciated!
<ul class="off-canvas-list">
<li><label>Solution Category</label></li>
{{#each}}
<li class="has-submenu">
{{name}}
<ul class="left-submenu">
<li class="back">Back</li>
{{#each products}}
{{{name}}}
{{/each}}
</ul>
</li>
{{/each}}
</ul>
How can you filter a data-list to render into multiple outlets in emberjs.
What I have now in not really working, but may help you understand what I want to achieve.
I can solve this by making multiple file-list.hbs template-files ( where I change file in the each to fileList1 or fileList2, ...), but that doesn't seem right.
What I want to achieve
I have a documents page where I want to list all of the document in the file list (see fixtures file). But instead of printing out one files-list, I want to split the lists so I have multiple lists according to the filter.
Please look at the code to understand it better ^^
Can anyone help? :)
File.FIXTURES
App.File.FIXTURES = [
{
id: 1,
showHomepage: false,
filter: 'filter1',
url: '/file1.pdf',
description: 'file1'
},
{
id: 2,
showHomepage: false,
filter: 'filter2',
url: '/file2.pdf',
description: 'file2'
},
{
id: 3,
showHomepage: true,
filter: 'filter2',
url: '/file3.pdf',
description: 'file3'
},
{
id: 4,
showHomepage: true,
filter: 'filter3',
url: '/file4.pdf',
description: 'file4'
}
];
Route
App.InfoDocumentenRoute = Ember.Route.extend({
model: function() {
var store = this.store;
return Ember.RSVP.hash({
fileList1: store.find('file' , { filter: "filter1" }),
fileList2: store.find('file' , { filter: "filter2" }),
fileList3: store.find('file' , { filter: "filter3" })
});
},
renderTemplate: function() {
this.render('file-list', { // the template to render
into:'info.documenten', // the route to render into
outlet: 'file-list-filter1', // the name of the outlet in the route's template
controller: 'file' // the controller to use for the template
});
this.render('file-list', { // the template to render
into:'info.documenten', // the route to render into
outlet: 'file-list-filter2', // the name of the outlet in the route's template
controller: 'file' // the controller to use for the template
});
this.render('file-list', { // the template to render
into:'info.documenten', // the route to render into
outlet: 'file-list-filter3', // the name of the outlet in the route's template
controller: 'file' // the controller to use for the template
});
}
});
info/documents.hbs
{{ outlet file-list-filter1 }}
{{ outlet file-list-filter2 }}
{{ outlet file-list-filter3 }}
file-list.hbs
<ul class="download-list">
{{#each file in file}}
<li class="download-list__item">
<a {{bind-attr href=file.url}} target="_blank" class="download-list__link">
<i class="icon-download download-list__link__icon"></i>
{{file.description}}
</a>
</li>
{{else}}
<li>
Geen documenten beschikbaar.
</li>
{{/each}}
I think the best way to go about this would be to declare your file-list.hbs as a partial and include it within your other templates where needed as: {{partial "file-list"}}. In your showHomepage where you only want to use it a single time, merely include the {{partial "file-list"}} within your showHomepage.hbs.
Then, for your InfoDocumentRoute, put the following to declare your model as an array of filelists:
App.InfoDocumentenRoute = Ember.Route.extend({
model: function() {
var store = this.store;
return [
store.find('file' , { filter: "filter1" }),
store.find('file' , { filter: "filter2" }),
store.find('file' , { filter: "filter3" })
];
}
});
And your InfoDocument.hbs as:
{{#each file in model}}
{{partial "file-list"}}
{{/each}}
Which will then render the file-list template for each item in the model array.
More info about partials
So from what i gather about your question you want to filter your model on your filter property on the model. I am sure there are a few ways to accomplish this but here is another possible solution that could spark another solution.
So in the route I returned the models. Then in the controller I created properties that are filtering the array of models from the route. Then in the template I loop over the array that filter property gives me in the controller and output in the template.
Heres JSBin. http://emberjs.jsbin.com/vunugida/5/edit
App.IndexRoute = Ember.Route.extend({
model: function() {
return this.store.findAll('File');
}
});
App.IndexController = Ember.ArrayController.extend({
filter1: function() {
return this.filter(function(item) {
return item.get('filter') === "filter1";
});
}.property(),
filter2: function() {
return this.filter(function(item) {
return item.get('filter') === "filter2";
});
}.property(),
filter3: function() {
return this.filter(function(item){
return item.get('filter') === "filter3";
});
}.property()
});
TEMPLATE:
<script type="text/x-handlebars" data-template-name="index">
<h1>Index Template</h1>
<ul>
{{#each}}
<li>{{url}}</li>
{{/each}}
</ul>
<p>Filter 1</p>
{{#each filter1}}
<li>{{url}}</li>
{{/each}}
<p>Filter 2</p>
{{#each filter2}}
<li>{{url}}</li>
{{/each}}
<p>Filter 3</p>
{{#each filter3}}
<li>{{url}}</li>
{{/each}}
</script>
I have an ember application with a model called users.js with associated controllers and routing. In my usersController.js, I have a function which counts the number of users in the system. I can then display this figure in my users template. However, I want to display that figure in my index template instead, is this possible? How would I go about it- right now the figure doesn't seem to be available for use outside of my users model.
Here's my usersController-
App.UsersController = Ember.ArrayController.extend({
sortProperties: ['name'],
sortAscending: true,
numUsers: function() {
return this.get('model.length');
}.property('model.[]')
});
And my html-
<script type = "text/x-handlebars" id = "index">
<h2>Homepage</h2>
//This is where I would like the figure to be
<h3>There are {{numUsers}} users </h3>
</script>
<script type = "text/x-handlebars" id = "users">
<div class="col-md-2">
{{#link-to "users.create"}}<button type="button" class="btn btn-default btn-lg"><span class="glyphicon glyphicon-plus"></button> {{/link-to}}
//This works fine
<div>Users: {{numUsers}}</div>
</div>
<div class="col-md-10">
<ul class="list-group">
{{#each user in controller}}
<li class="list-group-item">
{{#link-to "user" user}}
{{user.name}}
{{/link-to}}
</li>
{{/each}}
</ul>
{{outlet}}
</div>
</script>
You can just load all users in the IndexRoute, something like this:
App.IndexRoute = Ember.Route.extend({
model: function() {
return this.store.find('user');
}
});
And extract the shared logic, in that case user count, to a mixin, and use where needed:
App.UsersCountMixin = Ember.Mixin.create({
numUsers: function() {
return this.get('model.length');
}.property('model.[]')
});
App.IndexController = Ember.ArrayController.extend(App.UsersCountMixin, {
});
App.UsersController = Ember.ArrayController.extend(App.UsersCountMixin, {
sortProperties: ['name'],
sortAscending: true
});
So {{numUsers}} will be avaliable in your index template.
To share logic with more than one model, you will need to create some alias for model property to avoid ambiguity:
App.IndexRoute = Ember.Route.extend({
model: function() {
return Ember.RSVP.hash({
users: this.store.find('user'),
subjects: this.store.find('subject'),
})
}
});
App.UsersCountMixin = Ember.Mixin.create({
users: Ember.required(),
numUsers: function() {
return this.get('users.length');
}.property('users.[]')
});
App.SubjectsCountMixin = Ember.Mixin.create({
subjects: Ember.required(),
numSubjects: function() {
return this.get('subjects.length');
}.property('subjects.[]')
});
App.UsersController = Ember.ArrayController.extend(App.UsersCountMixin, {
users: Ember.computed.alias('model'),
sortProperties: ['name'],
sortAscending: true
});
App.SubjectsController = Ember.ArrayController.extend(App.SubjectsCountMixin, {
subjects: Ember.computed.alias('model'),
sortProperties: ['name'],
sortAscending: true
});
App.IndexController = Ember.ArrayController.extend(App.UsersCountMixin, App.SubjectsCountMixin, {});
Of course this is a lot of code to just show the data length, since you can just use:
<h3>There are {{users.length}} users </h3>
<h3>There are {{subjects.length}} subjecst </h3>
But I think you will have more complex computed properties to share. In that cases, mixins is a good way to achieve it.
I have the following two models:
App.Child = DS.Model.extend({
name: DS.attr('string')
});
And:
App.Activity = DS.Model.extend({
children: DS.hasMany('child',{async:true}),
name: DS.attr('string')
});
I want to use checkboxes to choose between the existing children, for the hasMany relation.
For example, I have these three children:
App.Child.FIXTURES = [
{ id: 1, name: 'Brian' },
{ id: 2, name: 'Michael' },
{ id: 3, name: 'James' }
];
The user should be able to use checkboxes, while creating or editing an activity, for choosing which children, to add to the hasMany relation.
I've created a JSFiddle to illustrate my question: http://jsfiddle.net/Dd6Wh/. Click 'Create a new activity' to see what I'm trying to do.
Basically it's the same as Ember.Select [ ... ] multiple="true", but for checkboxes.
What's the correct approach for something like this with Ember.js?
You can use an itemController in your each view helper to manage the selection. In the code below I created one called ChildController:
App.ChildController = Ember.ObjectController.extend({
selected: function() {
var activity = this.get('content');
var children = this.get('parentController.children');
return children.contains(activity);
}.property(),
selectedChanged: function() {
var activity = this.get('content');
var children = this.get('parentController.children');
if (this.get('selected')) {
children.pushObject(activity);
} else {
children.removeObject(activity);
}
}.observes('selected')
});
With a itemController you can expose some properties and logics, without add it directlly to your models. In that case the selected computed property and the selectedChanged observer.
In your template, you can bind the selection using checkedBinding="selected". Because the itemController proxy each model, the selected property of the itemcontroller will be used, and the {{name}} binding, will lookup the name property of the model:
<script type="text/x-handlebars" data-template-name="activities/new">
<h1>Create a new activity</h1>
{{#each childList itemController="child"}}
<label>
{{view Ember.Checkbox checkedBinding="selected"}}
{{name}}
</label><br />
{{/each}}
{{view Ember.TextField valueBinding="name"}}
<button {{action create}}>Create</button>
</script>
The same aproach in edit template:
<script type="text/x-handlebars" data-template-name="activities/edit">
<h1>Edit an activity</h1>
{{#each childList itemController="child"}}
<label>
{{view Ember.Checkbox checkedBinding="selected"}}
{{name}}
</label><br />
{{/each}}
{{view Ember.TextField valueBinding="name"}}
<button {{action update}}>Update</button>
</script>
This is a fiddle with this working http://jsfiddle.net/marciojunior/8EjRk/
Component version
Template
<script type="text/x-handlebars" data-template-name="components/checkbox-select">
{{#each elements itemController="checkboxItem"}}
<label>
{{view Ember.Checkbox checkedBinding="selected"}}
{{label}}
</label><br />
{{/each}}
</script>
Javascript
App.CheckboxSelectComponent = Ember.Component.extend({
/* The property to be used as label */
labelPath: null,
/* The model */
model: null,
/* The has many property from the model */
propertyPath: null,
/* All possible elements, to be selected */
elements: null,
elementsOfProperty: function() {
return this.get('model.' + this.get('propertyPath'));
}.property()
});
App.CheckboxItemController = Ember.ObjectController.extend({
selected: function() {
var activity = this.get('content');
var children = this.get('parentController.elementsOfProperty');
return children.contains(activity);
}.property(),
label: function() {
return this.get('model.' + this.get('parentController.labelPath'));
}.property(),
selectedChanged: function() {
var activity = this.get('content');
var children = this.get('parentController.elementsOfProperty');
if (this.get('selected')) {
children.pushObject(activity);
} else {
children.removeObject(activity);
}
}.observes('selected')
});
Updated fiddle http://jsfiddle.net/mgLr8/14/
I hope it helps
Given the following code, I thought the person.index and nested person.finish routes would use the PersonController content/model property since theirs was empty/undefined? What am I doing wrong? http://jsfiddle.net/EasyCo/MMfSf/5/
To be more concise: When you click on the id, the {{id}} and {{name}} are blank? How do I fix that?
Functionality
// Create Ember App
App = Ember.Application.create();
// Create Ember Data Store
App.Store = DS.Store.extend({
revision: 11,
adapter: 'DS.FixtureAdapter'
});
// Create parent model with hasMany relationship
App.Person = DS.Model.extend({
name: DS.attr( 'string' ),
belts: DS.hasMany( 'App.Belt' )
});
// Create child model with belongsTo relationship
App.Belt = DS.Model.extend({
type: DS.attr( 'string' ),
parent: DS.belongsTo( 'App.Person' )
});
// Add Person fixtures
App.Person.FIXTURES = [{
"id" : 1,
"name" : "Trevor",
"belts" : [1, 2, 3]
}];
// Add Belt fixtures
App.Belt.FIXTURES = [{
"id" : 1,
"type" : "leather"
}, {
"id" : 2,
"type" : "rock"
}, {
"id" : 3,
"type" : "party-time"
}];
App.Router.map( function() {
this.resource( 'person', { path: '/:person_id' }, function() {
this.route( 'finish' );
});
});
// Set route behaviour
App.IndexRoute = Ember.Route.extend({
model: function() {
return App.Person.find();
},
renderTemplate: function() {
this.render('people');
}
});
Templates
<script type="text/x-handlebars">
<h1>Application</h1>
{{outlet}}
</script>
<script type="text/x-handlebars" id="people">
<h2>People</h2>
<ul>
{{#each controller}}
<li>
<div class="debug">
Is the person record dirty: {{this.isDirty}}
</div>
</li>
<li>Id: {{#linkTo person this}}{{id}}{{/linkTo}}</li>
<li>Name: {{name}}</li>
<li>Belt types:
<ul>
{{#each belts}}
<li>{{type}}</li>
{{/each}}
</ul>
</li>
{{/each}}
</ul>
</script>
<script type="text/x-handlebars" id="person">
<h2>Person</h2>
Id from within person template: {{id}}<br><br>
{{outlet}}
</script>
<script type="text/x-handlebars" id="person/index">
Id: {{id}}<br>
Name: <a href="#" {{action "changeName"}}>{{name}}</a><br><br>
{{#linkTo index}}Go back{{/linkTo}}<br>
{{#linkTo person.finish}}Go to finish{{/linkTo}}
</script>
<script type="text/x-handlebars" id="person/finish">
<h2>Finish</h2>
{{id}}
</script>
You can use this in your router:
model: function() {
return this.modelFor("person");
}
Instead of your's:
controller.set('content', this.controllerFor('person'));
Your views were served through different controllers, either Ember's generated one or the one you defined PersonIndexController and that contributed to the issue you were facing. Instead of patching your original example to make it work, i instead reworked it to show you how you should structure your views/routes to leverage Emberjs capabilities.
You should design your application/example as a series of states working and communicating with each other and captured in a Router map. In your example, you should have a people, person resource and a finish route with corresponding views and controllers, either you explicitly create them or let Ember do that for you, providing you're following its convention.
Here's a working exemple and below I highlighted some of the most important parts of the example
<script type="text/x-handlebars" data-template-name="people">
<h2>People</h2>
<ul>
{{#each person in controller}}
<li>
<div class="debug">
Is the person record dirty: {{this.isDirty}}
</div>
</li>
<li>Id: {{#linkTo 'person' person}}{{person.id}}{{/linkTo}}</li>
<li>Name: {{person.name}}</li>
<li>Belt types:
<ul>
{{#each person.belts}}
<li>{{type}}</li>
{{/each}}
</ul>
</li>
{{/each}}
</ul>
</script>
<script type="text/x-handlebars" data-template-name="person">
<h2>Person</h2>
Id from within person template: {{id}}<br><br>
Id: {{id}}<br>
Name: <a href="#" {{action "changeName"}}>{{name}}</a><br><br>
{{#linkTo index}}Go back{{/linkTo}}<br>
{{#linkTo person.finish}}Go to finish{{/linkTo}}
{{outlet}}
</script>
Models, Views, Controllers and Route definitions
DS.RESTAdapter.configure("plurals", { person: "people" });
App.Router.map( function() {
this.resource('people',function() {
this.resource('person', { path: ':person_id' }, function() {
this.route( 'finish');
});
})
});
App.PeopleController = Ember.ArrayController.extend();
App.PeopleRoute = Ember.Route.extend({
model: function() {
return App.Person.find();
}
})
App.IndexRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('people');
}
});
App.PersonRoute = Ember.Route.extend({
model: function(params) {
debugger;
return App.Person.find(params.client_id);
},
renderTemplate: function() {
this.render('person',{
into:'application'
})
}
})
App.PersonFinishRoute = Ember.Route.extend({
renderTemplate: function() {
this.render('finish',{
into:'application'
})
}
})