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

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.

Related

handlebars, access to parent each loop variable

I have code like here. I would like to set input name to #index from previous each loop.
Can I access #previousIndex somehow? Or can I assign inputs to some kind of group created before second each loop that will set all input names inside?
Data I receive:
questions:[
{
question:"How old are you?",
answers:[
'16',
'18',
'20',
'25',
'other'
]
},
{
question:"How many kids do you have?",
answers:[
'0',
'1',
'2',
'other'
]
}
]
hbs code:
{{#each questions}}
<li>
<h3 class='question'>{{this.question}}</h2>
<div class='flexbox'>
{{#each this.answers}}
<label class="container">
<input type='radio' name='{{#previousIndex}}' value='{{#index}}'>
<span class='checkmark'>{{this}}</span>
</label>
{{/each}}
</div>
</li>
{{/each}}
When you are using #each then you get the index in #index and if you want previousIndex why not just do parseInt(#index) - 1
Here's how you do it:
var Handlebars = require('handlebars');
Handlebars.registerHelper("previous", function(value, options)
{
return parseInt(value) - 1;
});
Now update your hbs code as:
{{#each questions}}
<li>
<h3 class='question'>{{this.question}}</h2>
<div class='flexbox'>
{{#each this.answers}}
<label class="container">
<input type='radio' name='{{previous #index}}' value='{{#index}}'>
<span class='checkmark'>{{this}}</span>
</label>
{{/each}}
</div>
</li>
{{/each}}
Hope this works for you!
I can't give you a 100% working code (environment is not currently setup) but from my previous experience with handlebars I will try to guide you to a possible solution.
{{assign "previousIndex" 0}}
{{#each questions}}
<li>
<h3 class='question'>{{this.question}}</h2>
<div class='flexbox'>
{{#each this.answers}}
<label class="container">
<input type='radio' name='{{../previousIndex}}' value='{{#index}}'>
<span class='checkmark'>{{this}}</span>
</label>
{{/each}}
</div>
</li>
{{assign "../previousIndex" "{{#index}}"}}
{{/each}}
I had to create two helper functions like here. Get is here because I wasn`t able to access variable directly from .hbs file by simply writing {{myVariableName}}.
Hbs file looks now like here.
<div class='flexbox'>
{{assign "parentIndex" #index }}
{{#each this.answers}}
<label class="container">
<input type='radio' name='{{get "parentIndex"}}' value='{{#index}}'>
<span class='checkmark'>{{this}}</span>
</label>
{{/each}}
</div>
helpers
assign: function(varName, varValue, options) {
options.data.root[varName] = varValue;
}
get: function(varName, options) {
return options.data.root[varName]
}

How to toggle between links in ember js?

Hi, I'm new to ember js. Can someone please help me with my code? I have created this but I don't know how can I give action to each links. When I click on BreakFast, it should only show me 3 search box(BreadType, cheeseType and meatType) only and other should hide. same for Lunch and Drinks.
I also created route for menu in router.
--------------/////application.hbs
<h1>Welcome!!</h1>
{{#link-to 'menu'}}BreakFast{{/link-to}}
{{#link-to 'menu'}}Lunch{{/link-to}}
{{#link-to 'menu'}}Drinks{{/link-to}}
{{outlet}}
-------------/////menu.hbs
<div>
<p>Hello from BreakFast</p>
<label>
Bread Type:{{input value=bread}}
Cheese Type:{{input value=cheese}}
Meat Type:{{input value=meat}}
</label>
</div>
<div>
<p>Hello from Lunch</p>
<label>
Calories:{{input value=cal}}
Price:{{input value=price}}
Veg/Non-veg:
<select>
<option>V</option>
<option>N</option>
</select>
</label>
</div>
<div>
<p>Hello from Drinks</p>
<label>
Drink Name:{{input value=name}}
Price :{{input value=price}}
Ice: <select><option>Y</option>
<option>N</option>
</select>
</label>
</div>
one way to solve this is to use Ember's router to do most of the work for you.
First we define three nested routes under application, corresponding to breakfast, lunch, and drinks:
Router.map(function() {
this.route('breakfast');
this.route('lunch');
this.route('drinks');
});
Then, we just put the common parts in the application template, along with an {{outlet}} where nested templates will be rendered into:
// application.hbs
<h1>Welcome!!</h1>
{{#link-to 'breakfast'}}BreakFast{{/link-to}}
{{#link-to 'lunch'}}Lunch{{/link-to}}
{{#link-to 'drinks'}}Drinks{{/link-to}}
{{outlet}}
And finally, you put each menu type in the respective template:
// app/templates/breakfast.hbs
<div>
<p>Hello from BreakFast</p>
<label>
Bread Type:{{input value=bread}}
Cheese Type:{{input value=cheese}}
Meat Type:{{input value=meat}}
</label>
// app/templates/lunch.hbs
<div>
<p>Hello from Lunch</p>
<label>
Calories:{{input value=cal}}
Price:{{input value=price}}
Veg/Non-veg:
<select>
<option>V</option>
<option>N</option>
</select>
</label>
// app/templates.breakfast.hbs
<div>
<p>Hello from Drinks</p>
<label>
Drink Name:{{input value=name}}
Price :{{input value=price}}
Ice:
<select>
<option>Y</option>
<option>N</option>
</select>
</label>
</div>
And now you should only see the menu type you want! Without more information I can't really advise regarding models and data binding the fields to models.
You can also use the index route that Ember automatically creates for you to provide more template for when you visit / in your app.
Hope this helps.
Edit: I created twiddle to reflect what you requested. queryParam example with single template and controller
I would encourage you to go through official ember guides and play around with ember-twiddle. Here is my comment for your reply in discuss forum,
queryparams : [type], change this to queryparams : ['type'],
In menu.js controller file, you can't write code like you did, that will throw syntax error while compiling
import Ember from 'ember';
queryparams : [type],
export default Ember.Controller.extend({
if('breakfast'){
BreakFast:true
}
else if('lunch'){
Lunch:true
}
else if('drinks'){
Drinks:true
}
});
In menu.hbs, you have used lots properties in input helper but you haven't defined in corresponding controller/model.
I saw this question earlier in discussion.
There are many ways for your use case implementation, but I implemented it for complex requirement it will scale as well.
1.twiddle for dynamic segment implementation and included dynamic component rendering using component helper.
twiddle for queryaram and included dynamic component rendering using component helper..
I implemented using URL with dynamic segment and dynamic component rendering and component for each menu type. It may be overkill for your requirement but this will give you the better idea.
router.js
import Ember from 'ember';
import config from './config/environment';
const Router = Ember.Router.extend({
location: 'none',
rootURL: config.rootURL
});
Router.map(function() {
//introducing dynamic segment for men types
this.route('menu',{path: 'menus/:menu_id'});
});
export default Router;
routes/menu.js
import Ember from 'ember';
const { isEqual } = Ember;
export default Ember.Route.extend({
model(params){
//usually we will request data from server through ember-data or ajax call
var data = '';
if(params.menu_id === '1'){
data = { bread:' bread name',cheese:'cheese name',meat:'meat name '};
} else if(params.menu_id === '2'){
data = { cal:'200',price:'20'};
}
data.menu_id = params.menu_id;
return data;
}
});
controllers/menu.js
import Ember from 'ember';
const {computed} = Ember;
export default Ember.Controller.extend({
menuNameComponent: computed('model.menu_id',function(){
return this.get('model.menu_id') === '1' ? "menu-breakfast" : "menu-lunch";
})
});
application.hbs
<h1>Welcome!!</h1>
{{#link-to 'menu' '1'}}BreakFast{{/link-to}}
{{#link-to 'menu' 2}}Lunch{{/link-to}}
{{outlet}}
menu.hbs
<!-- component helper will dynamically load the specified component -->
{{component menuNameComponent}}
{{outlet}}
templates/components/menu-breakfast.hbs like this implement it for all the menu type.
<div>
<p>Hello from BreakFast</p>
<label>
Bread Type:{{input value=bread}}
Cheese Type:{{input value=cheese}}
Meat Type:{{input value=meat}}
</label>
</div>
{{yield}}
templates/components/menu-lunch.hbs
<div>
<p>Hello from Drinks</p>
<label>
Drink Name:{{input value=name}}
Price :{{input value=price}}
Ice: <select><option>Y</option>
<option>N</option>
</select>
</label>
</div>
{{yield}}

Meteor.js affecting only newly inserted child template

These are templates:
<template name="postsList">
{{#each posts}}
{{>postItem}}
{{/each}}
</template>
<template name="postItem">
<div class="more-less">
<div class="more-block">
<p>{{{text}}}</p>
</div>
<p class="continued">…</p>
[ + ]
</div>
</template>
When posts is updated with new post, postItem is inserted to postsLists. How can I apply this only for new inserted postItem template:
$('.more-less .more-block').css('height', 20).css('overflow', 'hidden');
Template.postItem.rendered affects all posts in page, but I need to affect only newly inserted post without affecting existing ones.
I'm going to assume new posts are added to the top of the list? if so...
<template name="postsList">
<div class="posts-list">
{{#each posts}}
{{>postItem}}
{{/each}}
</div>
</template>
<template name="postItem">
<div class="more-less">
<div class="more-block">
<p>{{{text}}}</p>
</div>
<p class="continued">…</p>
[ + ]
</div>
</template>
You can simply achieve this with CSS:
.posts-list .more-less:first-child .more-block {
height: 20px;
overflow: hidden;
}
No need to use JavaScript and worry about reactivity and DOM mutation.

Sending data into a nested Controller/View in EmberJS

I'm messing around with JSON structures in EmberJS starting from the top level Application level and trying to pass data to a general MenuController/MenuView child so I can reuse it.
The JSON I've come up with to play around with how data gets passed through nested ember structures is this:
JSON
{
appName: "CheesyPuffs"
menuItems: [
{name:"Gouda"}
{name:"Crackers", menuItems: [
{name: "Cheezeits"}
]
}
]
}
Handlebars
<script type="text/x-handlebars">
<div class='app'>
<div class='header'>
{{#if menuItems}}
// Embed template 'menu'
{{/if}}
</div>
</div>
</script>
<script type="text/x-handlebars" data-template-name="menu">
<ul>
{{#each menuItems}}
<li>{{name}}</li>
{{#if menuItems}}
// Embed menu template
{{/if}}
{{/each}}
</ul>
</script>
JS
App.MenuController = Ember.ArrayController.extend({
actions: {
click: function(){
// Signal Event to App
}
}
});
App.MenuView = Ember.View.extend({
click: function(){
console.log("I clicked a menu");
}
});
All the examples I've seen concerning nested stuff involves routes with the classic _id subroute. I'm not sure how I would go about telling the App Router that it needs to pass the menuItems data to a child controller that's embedded in the same view. I have a feeling you can do this both via the template and programmically? If so, how would you do it both ways?
using the template you should use render. And that's the way you should do it, programmatically doesn't make sense.
<script type="text/x-handlebars">
<div class='app'>
<div class='header'>
{{#if menuItems}}
{{render 'menu' menuItems}}
{{/if}}
</div>
</div>
</script>
<script type="text/x-handlebars" data-template-name="menu">
<ul>
{{#each menuItems}}
<li>{{name}}</li>
{{#if menuItems}}
{{render 'menu' menuItems}}
{{/if}}
{{/each}}
</ul>
</script>

Ember - Object has no method 'map'

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}}

Categories

Resources