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>
Related
When I try to render a list of view models contained in a chart model using the each handlebars helper, the promise array for the view models doesn't resolve before the each helper renders, leaving blank lis:
template:
<script type="text/x-handlebars" id="chart-container">
{{views}}
<ul>
{{#each view in views}}
<li>{{view}}</li>
{{/each}}
</ul>
</script>
What's odd is that if I change the each helper to {{#each views}} it works fine.
How can I make the view render once the promised hasMany relationship has been resolved using view in views for the each helper? Below are the relevant models and fixtures:
displayItem model:
var DisplayItem = DS.Model.extend({
name: DS.attr("name"),
display: DS.belongsTo("display", {async: true})
});
chart model:
var Chart = DisplayItem.extend({
views: DS.hasMany("view", {async: true})
});
view model:
var View = DS.Model.extend({
name: DS.attr("string"),
chart: DS.belongsTo("chart", {async: true})
});
relevant fixture data:
Chart.FIXTURES = [
{
id: 1,
name: "Derp",
display: 1,
views: [1, 2],
defaultView: 1
}
];
View.FIXTURES = [
{
id: 1,
name: "Test 1",
chart: 1
},
{
id: 2,
name: "Test 2",
chart: 1
}
];
To answer your question, the route is simplest place to do it. Using nested promises Ember won't setup the controller etc, until the deepest promise has resolved.
App.ChartContainerRoute = Em.Route.extend({
model: function(){
return this.store.find('chart').then(function(charts){
return Em.RSVP.all(charts.getEach('views')).then(function(){
return charts;
});
});
}re
});
Generally I'd recommend against waiting on all of the async calls (since Ember will asynchronously inject them into the page). If you are trying to modify the view after it's been inserted, there are other patterns that can solve this while giving a more responsive feeling app.
The real problem you're seeing
You aren't specifying anything to show in the li, and you're using a key word view
<li>{{view.name}}</li>
http://emberjs.jsbin.com/OxIDiVU/790/edit
view usually refers to the view associated with the current template etc.
{{#each item in views}}
<li>{{item}}</li>
{{/each}}
http://emberjs.jsbin.com/OxIDiVU/791/edit
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>
After endless trying I hope someone find the clue in what I am trying. I know there are many questions about this specific topic on stackoverflow. However I think I do not ask the same question. As I do not find the answer to my specific challenge.
Here is my Router:
App.Router.map(function () {
this.resource('article', {path: '/article/:id'});
this.resource('article.new', {path: "/article/new"});
});
Routes
App.ArticleRoute = Ember.Route.extend({
model: function (params) {
return this.store.find('article', params.id);
}
});
App.ArticleNewRoute = Ember.Route.extend({
renderTemplate: function () {
this.render('article', {
controller: 'article.new'
});
},
model: function () {
return this.store.createRecord('article');
}
});
The model
App.Category = DS.Model.extend({
name: DS.attr('string'),
image: DS.attr('string'),
categoryRelation: DS.belongsTo('category')
});
App.Article = DS.Model.extend({
name: DS.attr('string'),
category: DS.hasMany('category')
)};
The returned JSON from server:
{
"articles":[
{
"id":1,
"name":"Car 1",
"category":[1,2],
{
"id":2,
"name":"Car 2",
"category":2,
],
"categorys":[ // note the added 's' when returning multiple items as per EmberJS convention
{
"id":1,
"name":"Oldtimers"
},
{
"id":2,
"name":"Classic"
}
],
}
And now the question, because I try in my template the following:
<script type="text/x-handlebars" data-template-name="article">
<div>
{{#each category in model}}
{{category.name}}<br>
{{name}}<br>
{{/each}}
</div>
</script>
I have tried multiple variations in the template, this is my last code which seems correct. Note: as for article with id 2, the template must also render if there is just one article.
Edit: I translated some code for you guys. If there are misspellings, they are probably not in the original code.
Your article template will receive just one article so this {{#each category in model}} don't work, you need to use {{#each category in model.category}}:
<div>
Article {{name}}<br/>
{{#each category in model.category}}
Category {{category.name}}<br/>
{{/each}}
</div>
This is a fiddle with this in action http://jsfiddle.net/marciojunior/fj26R/
What I'm trying to do is very basic but I'm having very little luck...
Simply enough, I don't want to display a chunk of HTML until a certain Ember Data model property is fully loaded.
As you can see from the jsfiddle, the parent model: App.Person gets loaded into the DOM and it also loads the 3 placeholders for its hasMany property belts.
It then executes the request to populate App.Belt and fills in the placeholders.
While this is usually ok, it makes a big mess of things when trying to build an SVG, for example. Since the surrounding <svg> tags will get appended to the DOM immediately and then some time down the track (once the asynchronous request returns data), the inner svg components will be added between the tags. This usually creates browser rendering errors.
TL;DR
In the example, how do I defer the <h3>...</h3> section of the template from being added to the DOM until the model data and its relationships (belts) are fully loaded? This way everything gets visually and physically added to the DOM at once.
The JS:
// Create Ember App
App = Ember.Application.create();
// Create Ember Data Store
App.store = DS.Store.create({
revision: 11,
//Exagerate latency to demonstrate problem with relationships being loaded sequentially.
adapter: DS.FixtureAdapter.create({latency: 5000})
});
// 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 Parent fixtures
App.Person.FIXTURES = [{
"id" : 1,
"name" : "Trevor",
"belts" : [1, 2, 3]
}];
// Add Child fixtures
App.Belt.FIXTURES = [{
"id" : 1,
"type" : "leather"
}, {
"id" : 2,
"type" : "rock"
}, {
"id" : 3,
"type" : "party-time"
}];
// Set route behaviour
App.IndexRoute = Ember.Route.extend({
model: function() {
return App.Person.find();
},
renderTemplate: function() {
this.render('people');
}
});
The HTML/HBS:
<script type="text/x-handlebars">
<h1>Application</h1>
{{outlet}}
</script>
<script type="text/x-handlebars" id="people">
<h3>Don't load this header until every belt defined in App.Person.belts is loaded</h3>
<ul>
{{#each controller}}
{{debugger}}
<li>Id: {{id}}</li>
<li>Name: {{name}}</li>
<li>Belt types:
<ul>
{{#each belts}}
<li>{{type}}</li>
{{/each}}
</ul>
</li>
{{/each}}
</ul>
</script>
The fiddle: http://jsfiddle.net/zfkNp/4/
Check for the controller.content.length and belts.isLoaded, See the jsfiddle for a solution.
<script type="text/x-handlebars" id="people">
{{#if controller.ready}}
<h3>Don't load this header until every belt defined in App.Person.belts is loaded</h3>
{{/if}}
<ul>
{{#each controller}}
{{debugger}}
{{#if belts.isLoaded}}
<li>Id: {{id}}</li>
<li>Name: {{name}}</li>
<li>Belt types:
<ul>
{{#each belts}}
<li>{{type}}</li>
{{/each}}
</ul>
</li>
{{/if}}
{{/each}}
</ul>
</script>
App.IndexController = Ember.ArrayController.extend({
content: null,
ready:function() {
return this.get('content.length')>0
}.property('content.length')
});
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'
})
}
})