I working on a toy app to learn ember. I have recipes that belong to styles. A style just consists of a name attribute (and the implicit id as well). I am having trouble setting up a select field in the new template to select which style the recipe is.
Here's what I currently have for my routes, controllers and templates:
Routes:
App.RecipesNewRoute = Ember.Route.extend({
model: function() {
return App.Recipe.createRecord({
title: '',
description: '',
instructions: ''
});
},
setupController: function(controller, model) {
controller.set('content', model);
}
});
Controllers:
App.RecipesController = Ember.ArrayController.extend({});
App.RecipesNewController = Ember.ObjectController.extend({
create: function() {
this.transitionToRoute('recipes.show', this.content);
},
cancel: function() {
this.content.deleteRecord();
this.transitionToRoute('recipes.index');
},
buttonTitle: 'Add Beer Recipe'
});
Templates:
recipes/new and form partial:
<script type="text/x-handlebars" data-template-name="recipes/new">
{{ partial 'recipes/form' }}
</script>
<script type="text/x-handlebars" data-template-name="recipes/_form">
{{ title }}
<form>
<fieldset>
<div>
<label {{bindAttr for="titleField.elementId"}}>Title</label>
{{view Ember.TextField valueBinding='title' name='title' viewName='titleField'}}
</div>
<div>
<label>Style</label>
{{view Ember.Select valueBinding='style' name='style' viewName='styleField' contentBinding='App.Style.find()'}}
</div>
<div>
<label {{bindAttr for='descriptionField.elementId'}}>Description</label>
{{view Ember.TextArea valueBinding='description' name='description' viewName='descriptionField'}}
</div>
<div>
<label {{bindAttr for='instructionsField.elementId'}}>Instructions</label>
{{view Ember.TextArea valueBinding='instructions' name='instructions' viewName='instructionsField'}}
</div>
<a href='#' {{action create target='controller'}}>{{buttonTitle}}</a>
</fieldset>
</form>
<a href='#' {{action cancel target='controller'}}>Cancel</a>
</script>
The main takeaway being: {{view Ember.Select valueBinding='style' name='style' viewName='styleField' contentBinding='App.Style.find()'}}
I've also tried setting up a styles variable in the controller:
In RecipesNewController: styles: App.Style.find() and in the view {{view Ember.Select valueBinding='style' name='style' viewName='styleField' contentBinding='styles' optionValuePath='styles.id' optionLabelPath='styles.name'}}
as well as in the route's setupController: controller.set('styles', App.Style.find()) (with the same view code as with setting it in the controller).
Any help would be great, I'm sure it's something simple.
Thanks
Update
So I think I've almost got it.
In my RecipesNewRoute's setupController I have controller.set('styles', App.Style.find());. Then in my view I have {{view Ember.Select valueBinding='style' name='style' viewName='styleField' contentBinding='controller.styles' contentValuePath='content.id' contentLabelPath='content.name'}}. Now my problem is I have the select box being populated, except the label and value are set to <App.Style:ember407:100> instead of the id and the name.
The Ember Select uses optionValuePath and optionLabelPath, and it appears you are using contentValuePath and contentLabelPath.
An example jsFiddle here: http://jsfiddle.net/nrionfx/BJwLR/2/
From the ember docs: http://emberjs.com/api/classes/Ember.Select.html
{{view Ember.Select
contentBinding="App.programmers"
optionValuePath="content.id"
optionLabelPath="content.firstName"}}
Related
I am trying to create a select box in a modal that has a list of all cars in inventory. If I enter the app from a page that has all the data loaded and open the modal it works correctly. However if I am on a route that doesn't have the data loaded, then open the modal the select options do not show. Furthermore the options will never update from that point. How do I get the proper data to load? Should not this work since I am fetching the data direct from the store?
Dms.SellDialogController = Ember.ArrayController.extend({
cars: function() {
return this.store.find('car').filterProperty('isSold', false);
}.property('model'),
selectedCar: '',
carObjects: function() {
var cars = this.get('cars').map(function(car) {
return obj = {id: car.get('id'), key: car.get('keyNumber'), label: 'KEY#:'+car.get('keyNumber') + ' - STOCK#:' + car.get('stock')+' '+car.get('year')+' '+car.get('vModel')};
});
cars.sort(function(a, b){return a.key-b.key;})
return cars;
}.property('route'),
title: 'Select the car you are selling'
});
ApplicationRoute...
...
openModal: function(modalName, model) {
this.render(modalName, {
into: 'application',
outlet: 'modal',
model: model
});
},
Action to open modal inside application template
{{action 'openModal' 'sellDialog' model}}
and the templates
<script type="text/x-handlebars" id="components/modal-dialog">
<div class='modal-overlay' {{action "closeModal" target=cntrllr}}>
<div class='modal' {{action "dngn" target=cntrllr bubbles=false}}>
<div class='modal-content'>
<div class='modal-header'>
<button type="button" class="close" aria-label="Close" {{action "closeModal" target=cntrllr}}><span aria-hidden="true">×</span></button>
<h4 class="modal-title">{{title}}</h4>
</div>
{{yield}}
</div>
</div>
</div>
</script>
<script type="text/x-handlebars" data-template-name="sellDialog">
{{#modal-dialog title=title cntrllr=controller model=model}}
<div class="modal-body">
<span class='pull-left'>Key Number:
{{view "select" content=carObjects optionValuePath="content.id" optionLabelPath="content.label" selection=selectedCar}}
</span>
</div>
<div class="modal-footer">
<button {{action "modalAction" controller "sell" selectedCar.id}}>Sell</button>
<button {{action "closeModal"}}>Cancel</button>
</div>
{{/modal-dialog}}
</script>
UPDATE 2015/5/4:
I have a hack to make it work for now. I added a cars property in the application route and I am getting it in SellDialogController. This looks like a bug to me. I will make a jsFiddle when I get some time and check it further before issuing a bug report.
try using setupController on the Application Route, since setupController is always called.
import Ember from 'ember';
export default Ember.Route.extend({
model: Ember.RSVP.hash({
optionsForSelect: function() {
// fetch your select data here
},
otherData: function() {
// get your regular model data here
}
}),
setupController: function(controller, model) {
this.controllerFor('sellDialog').set('model', model.optionsForSelect);
this.controller.set('model', model.otherData);
}
});
I have a really basic test app. It has 2 pages an empty index and a login form on an other page. And I would like to know how can I pass variables to the layout which is the same on every page (user name/status for display it and display login/logout by status).
Or the best solution could be something like this: pass a user model to the AppController which is available from anywhere (like login page or login modals) and If I change and save the data the site template is just changing by the passed variables.
I was trying to find a good way, I find a few ones but none of them was working :( If you know a working way please share me or show me what is the solution.
Here is my code: the JS I have:
App = Ember.Application.create();
App.ApplicationController = Ember.Controller.extend({});
/* *** Index *** */
App.IndexView = Ember.View.extend({
layoutName: 'app_view'
});
App.IndexController = Ember.Controller.extend({
});
/* *** LOGIN *** */
App.Router.map(function () {
this.resource("login");
});
App.LoginView = Ember.View.extend({
layoutName: 'app_view'
});
App.LoginController = Ember.Controller.extend({
login: function() {
// blah blah
}
});
Layout template:
<script type="text/x-handlebars" data-template-name="app_view">
<header id="site">
{{#link-to "index"}}Index{{/link-to}}
{{#link-to "login"}}Login{{/link-to}}
{{variablaWhatIWant}}
</header>
<hr />
<section id="content">
{{yield}}
</section>
<hr />
<footer id="main">
Footer
</footer>
</script>
2 view templates:
<script type="text/x-handlebars" data-template-name="index">
Hello INDEX page!
</script>
<script type="text/x-handlebars" data-template-name="login">
<form class="form-horizontal" {{action "login" on="submit"}}>
<div class="control-group">
Username: {{input value=username type="text"}}
</div>
<div class="control-group">
Password {{input value=password type="password"}}
</div>
<button type="submit" class="btn">Log in!</button>
</form>
</script>
Thank you very much! :)
You have the right idea, see managing dependancies between controllers http://emberjs.com/guides/controllers/dependencies-between-controllers
just set the needs property, and you can access that controller from any other
App.LoginController = Ember.Controller.extend({
needs: ['application'],
login: function() {
this.authenticate(this.get('username'), this.get('password')).then(function(user) {
this.set('controllers.application.user', user);
},
function(err) {
//auth err
}
}
});
I have 2 named outlets in my application template, slider-area and pre-footer. Is there a way to pass view components that take parameters, as in the main-slider component shown in the index template, to a named outlet? So I would need to pass {{main-slider sliders=filteredSlider}} to the outlet {{outlet "slider-area"}} in the index template?
I have come from rails so forgive my thinking if this is not how ember does it. One could specify yield :slider_area in the application template and then wrap any content for this area in a content_for :slider_area block. Is a similar approach available in ember?
index.html
<script type="text/x-handlebars" data-template-name="application">
{{panasonic-topbar}}
<div id="batterywrap">
{{outlet "slider-area"}}
<div class="index_contents">
<div class="index_contents_inner">
<div class="main_area">
{{outlet}}
</div>
</div>
</div>
</div>
{{panasonic-footer}}
</script>
<script type="text/x-handlebars" data-template-name="index">
# Something like {{outlet "slider-area" render main-slider sliders="filteredSlider}} ?
{{main-slider sliders=filteredSlider}}
{{partial "social_footer"}}
</script>
app.js
App.IndexController = Ember.ObjectController.extend({
filteredSlider: function(){
return this.get('sliders').filterBy('page', 'index');
}.property('sliders.#each.page')
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return Ember.RSVP.hash({
sliders: this.store.find("slider")
});
}
});
Ok, so I have a solution to this issue, rather than trying to pass a view component within a template to a specific outlet (the ruby on rails way), the key is to create a template, not a component, and render this from within the Route to the specific outlet.
When rendered from Route, the slider template has access to all of the functions in the scope of the Routes controller, so we namespace the functions/arguments universally across all controllers that will use this template and our dynamic parameters should work.
So below in the IndexRoute we define the data that we send to the controller, sliders, we also specify that we want to render normal content in the default outlet using this.render();, and then we render our shared slider template into the named outlet "slider-area". Then in our controller, we filter the models data to our specification and we name this function batterySliders across all controllers that use this feature.
app.js
App.IndexController = Ember.ObjectController.extend({
batterySliders: function(){
return this.get('sliders').filterBy('page', 'index');
}.property('sliders.#each.page')
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return Ember.RSVP.hash({
sliders: this.store.find("slider"),
});
},
renderTemplate: function(){
this.render();
this.render("slider", {outlet: "slider-area"});
}
});
index.html
<script type="text/x-handlebars" data-template-name="slider">
{{panasonic-navigation tagName="div" classNames="gnavi_area"}}
<div class="slider_wrap">
<div id="slider" class="main_slider">
{{#each slider in batterySliders}}
<div class="slider_area slider0{{unbound slider.id}} {{unbound slider.background}}">
<div class="slider_inner">
<div class="inner0{{unbound slider.id}}">
<img {{bind-attr src="slider.image" alt="slider.image"}} class="nosp"/>
<img {{bind-attr src="slider.sm_image" alt="slider.sm_image"}} class="sp"/>
</div>
</div>
</div>
{{/each}}
</div>
</div>
</script>
application.html
<script type="text/x-handlebars" data-template-name="application">
{{panasonic-topbar}}
<div id="batterywrap">
<div class="content_head">
{{outlet "slider-area"}}
</div>
<div class="index_contents">
<div class="index_contents_inner">
<div class="main_area">
{{outlet}}
</div>
</div>
</div>
</div>
{{panasonic-footer}}
</script>
Essentially I have a list of objects, and the user is able to add new ones by typing into a textbox. As the user types, the new object appears. However, when the user clicks save, the object disappears from the listing. It is successfully saved to the database (Ruby on Rails/SQLite) so it appears on a full refresh. Why is it doing this, and how can I get it to stay on the screen when the user presses submit?
Here is some relevant code:
index.hbs
<div class="messages">
{{#each userhashtag in controller}}
{{render "userhashtag" userhashtag}}
{{/each}}
{{#link-to 'userhashtags.new'}} Add Hashtag{{/link-to}}
</div>
{{outlet}}
show.hbs
{{userhashtag.name}} <button class="btn btn-dancer" type="submit" {{action "destroy"}}>Remove</button><br>
new.hbs
<form role="form">
<fieldset>
<div class="form-group">
{{view Ember.TextField valueBinding='name' class='form-control' name='name' viewName='nameField'}}
</div>
<div class="btn-group">
<button type="submit" {{action "save"}} class="btn btn-success">Submit</button>
</div>
route
App.UserhashtagsNewRoute = Ember.Route.extend({
model: function(){
return this.store.createRecord('userhashtag');
},
setupController: function(controller, model){
controller.set('model', model);
},
renderTemplate: function() {
this.render('app/templates/userhashtags/new');
},
actions: {
save: function(){
return this.controller.get('model').save().then(function(){
this.transitionTo('userhashtags');
}.bind(this));
}
}
});
Make sure that you are manually pushing the new model into your Array collection (not sure how you're managing the collection). I'd suggest moving the #save action into a controller where you manage the list of objects.
// assuming "list" is some reference to your collection list within your controller
save: function() {
this.get('userhashtag').save().then(function(_model){
list.pushObject(_model);
I want a View to be hidden on load, then when a user clicks on a link it will display the view. Can someone review my code and let me know what I have done wrong?
App.parentView = Em.View.extend({
click: function() {
App.childView.set('isVisible', true);
}
});
App.childView = Em.View.extend({
isVisible: false
});
Here is the jsfiddle: http://jsfiddle.net/stevenng/uxyrw/5/
I would create a simple isVisibleBinding to the view you want to hide/show, see
http://jsfiddle.net/pangratz666/dTV6q/:
Handlebars:
<script type="text/x-handlebars" >
{{#view App.ParentView}}
<h1>Parent</h1>
<div>
<a href="#" {{action "toggle"}}>hide/show</a>
</div>
{{#view App.ChildView isVisibleBinding="isChildVisible" }}
{{view Em.TextArea rows="2" cols="20"}}
{{/view}}
{{/view}}
</script>
JavaScript:
App.ParentView = Em.View.extend({
isChildVisible: true,
toggle: function(){
this.toggleProperty('isChildVisible');
}
});
App.ChildView = Ember.View.extend();
A note about your naming conventions: classes should be named UpperCase and instances lowerCase. See blog post about this.
Valuebinding for some reasons didnt work for me so observing parentView property inside childView did the trick for me
Handlebar:
<script type="text/x-handlebars" >
{{#view App.ParentView}}
<h1>Parent</h1>
<div>
<a href="#" {{action "toggle"}}>hide/show</a>
</div>
{{#view App.ChildView }}
{{view Em.TextArea rows="2" cols="20"}}
{{/view}}
{{/view}}
</script>
Coffeescript:
App.ParentView = Em.View.extend
isChildVisible: true
toggle: ->
#toggleProperty 'isChildVisible'
App.ChildView = Em.View.extend
isVisible: (->
#get('parentView.isChildVisible')
).property '_parentView.isChildVisible'