EmberJS: How to render a template on select change - javascript

I'm new to ember and am trying to figure out how to render a template when a select control changes.
CODE:
App.LocationTypeController = Ember.ArrayController.extend({
selectedLocationType: null,
locationTypeChanged: function() {
//Render template
}.observes('selectedLocationType')
});
{{view Ember.Select
contentBinding="model"
selectionBinding="selectedLocationType"
optionValuePath="content.id"
optionLabelPath="content.name"}}
When the locationType changes the locationTypeChanged function is fired in the controller.
But how do I render some content into the dom from there? (this.render()?)...

Yes you have to use this.render() only, but the key here is into option inside it.
App.LocationTypeController = Ember.ArrayController.extend({
selectedLocationType: null,
locationTypeChanged: function() {
var selectedLocationType = this.get('selectedLocationType');
this.send('changeTemplate',selectedLocationType);
}.observes('selectedLocationType')
});
Have the action in your route as
changeTemplate: function(selection) {
this.render('template'+selection.id,{into:'locationType'});
}
and have an {{outlet}} in your locationType's template.
{{view Ember.Select
contentBinding="model"
selectionBinding="selectedLocationType"
optionValuePath="content.id"
optionLabelPath="content.name"}}
{{outlet}}
Sample JSBin for your requirement

If you need to show only a frament, when exist something selected, you can use the if handlebars helper:
In your template
...
{{#if selectedLocationType}}
Any content here will be visible when selectedLocationType has some value
{{/if}}
...
{{view Ember.Select
contentBinding="model"
selectionBinding="selectedLocationType"
optionValuePath="content.id"
optionLabelPath="content.name"}}
I hope it helps

Related

Populate Ember View.Select object on each transition to page

New Ember user here,
I am having an issue trying to get a dropdown view to be populated with initial values from a model on transition to any one of multiple edit routes. I am currently using fixture data...
My router is setup as such:
StoryTime.Router.map(function () {
this.resource('projects', function(){
this.resource('project', { path: '/:project_id' }, function(){
this.resource('stories', function(){
this.resource('story', { path: '/:story_id' }, function(){
this.route('edit');
});
});
this.route('edit');
this.route('report');
this.route('export');
});
this.route('new');
});
});
with a route for editing a story as:
StoryTime.StoryEditRoute = Ember.Route.extend({
setupController: function (controller, model) {
controller.set('model', this.controllerFor('story').get('model'));
}
});
and my controller setup as:
StoryTime.StoryEditController = Ember.ObjectController.extend({
needs:['story', 'project'],
actors: Ember.computed.alias('controllers.project.actors'),
selectedActor: null,
updateActor: function(){
var actor = this.get('selectedActor'),
model = this.get('model');
model.set('actor', actor);
}.observes('selectedActor'),
actions: {
//actions...
}
});
and my template has this piece in question in it:
<div class="form-group">
<label class="control-label col-sm-2 text-left no-padding">Actor:</label>
<div class="controls col-sm-10">
{{model.actor.name}}: {{model.actor.id}}
{{view Ember.Select
name = "actorSelect"
content = actors
optionLabelPath = "content.name"
optionValuePath = "content.id"
selectionBinding = "selectedActor"
class = "form-control"
}}
</div>
</div>
My preferable setup would have:
The model being given to the controller (set by the story edit route to be the model given to the stories route) populate the dropdown box's initial value, but then binds the subsequent selection to the controllers attribute for processing, and
The controller changes the "selectedActor" attribute to null again on transition to another edit route.
Right now, this is not happening. Here's what is happening:
When I navigate to .../stories/1/edit for example, the dropdown is not populated to the model's actor value, but rather the first thing in the list
If I change the value it changes the controller's model's actor, as expected.
However, on subsequent transitions to ../stories/2/edit the dropdown is still populated with the old selected value due to "selectedActor" being set to it.
Can anyone illuminate as to what I am missing here? I feel like there has to be way to both work with the Ember Select view and a way to reset attributes of a particular route or controller on transition. Am I incorrect on this thinking?
Thanks for any insight!
Yeah, fanta is right. You just need to remove a whole bunch of code from your controller and modify your template slightly. Replace your controller and template like so:
StoryTime.StoryEditController = Ember.ObjectController.extend({
needs:['story', 'project'],
actors: Ember.computed.alias('controllers.project.actors'),
actions: {
//actions...
}
});
<div class="form-group">
<label class="control-label col-sm-2 text-left no-padding">Actor:</label>
<div class="controls col-sm-10">
{{actor.name}}: {{actor.id}}
{{view Ember.Select
name = "actorSelect"
content = actors
optionLabelPath = "content.name"
optionValuePath = "content.id"
selectionBinding = actor
class = "form-control"
}}
</div>
</div>
Note you don't need model references in your template. Ember will automatically pass the references back to the underlying model. The primarily job of the controller is to decorate and provide the model (and handle actions & events) to the template / views.

Computed.alias not updating bind-attr

I recently started using Ember.js. In my small application I currently have problems regarding Ember.computed.alias, because an {{#if}}-section is updated properly, but the bind-attr helper in the same template is not updated accordingly.
The application controller and the action influencing the value look as follows:
App.ApplicationController = Ember.ObjectController.extend({
isEditing: false,
actions: {
toggleEdit: function() {
var a = this.get('isEditing');
this.set('isEditing', !a);
}
}
});
The controller taking care of the template causing problems:
App.CategoriesController = Ember.ArrayController.extend({
needs: ['application'],
isEditing: Ember.computed.alias('controllers.application.isEditing'),
general: function() { // example depending on the alias
var result = this.filterBy('type', 1);
if (!this.get('isEditing')) {
result = result.filterBy('isHidden', false);
}
return result;
}.property('#each.type', '#each.isHidden', 'isEditing'),
// ......
The related template:
<ul id="categories">
{{#if isEditing}}YES!{{else}}NO!{{/if}}
{{#each general}}
<li {{bind-attr class=":general isEditing:editing"}}>
{{name}}
</li>
{{/each}}
</ul>
When the action toggleEdit is triggered, the {{#if}} section is updated and swaps between YES! and NO!, but the editing class is not applied to the list element. I tried encapsulated the alias into another property of the controller depending on the alias, but without success.
I assume it's a beginners mistake, but I can't figure out what I am overlooking.
Thanking you in anticipation.
isEditing is no longer in scope, use controller.isEditing, sorry phone response
Here's an example that would keep it in scope, but I'm fully qualifying it just to show you.
{{#each item in general}}
<li {{bind-attr class=":general controller.isEditing:editing"}}>
{{item.name}}
</li>
{{/each}}

How to pass parameters with the action Helper of Ember.js inside an input field from an Handlebars template?

In my handlebars template I have this loop:
{{#each itemController="fund"}}
<li>
<label>{{title}}</label>
<span>{{amount}}</span>
{{input type="text" placeholder="new user"
value=newFullName action="createUser"}}
{{partial 'user-list'}}
</li>
{{/each}}
and need to pass the current object as parameter to the 'createUser' action.
Something like this:
action="createUser(this)"
or:
action 'createUser' this
But it seems that ember can't handle parameters for actions inside an input field...
Am i missing something?
You can now pass a function, along with values -
submit=(action 'setName' 'Sal')
http://emberjs.com/blog/2015/06/12/ember-1-13-0-released.html#toc_closure-actions
I think that isn't possible to do this using the action property from input view helper.
A workaround could be wrap your input in a form that use the action view helper using the submit event, like the following:
Template
{{#each}}
<li>
<form {{action "createUser" this on="submit"}}>
{{name}}
{{input type="text" value=name}}
</form>
</li>
{{/each}}
Route
...
actions: {
createUser: function(user) {
alert(user.get('name'));
}
}
...
So when the user hit enter, will have the event triggered.
The main difference between the action property and the action view helper is that the action view helper is more flexible and you can supply the context and put it inside of any tag:
<div {{action "someAction" someObject}} on="click">Click me</div>
In the route:
actions: {
someAction: function(someObject) {
// do something with the someObject
}
}
See the docs for further information
Please give a look in the jsfiddle to see that sample in action http://jsfiddle.net/marciojunior/UAgjX/
I hope it helps
Finally i ended up with this solution:
Template
{{input class="newUser" type="text" value=newFullName placeholder="add user"}}
<button {{action 'createUser' this newFullName}}>Send</button>
Controller
createUser: function (fund, newFullName) {
var fullName = newFullName;
var user = this.store.createRecord('appUser', {
fullName: fullName,
fund: fund,
payments:[]
});
user.save().then(function(){
fund.get('users').pushObject(user);
fund.save().then(function(){
fund.reload();
});
});
}
You can pass a parameter to an action helper :{{action "doSomething" xxx }}
Where doSomething is your controller method ,and xxx is anything in the current context of the template.

EmberJS and Handlerbars helpers in a CollectionView

I'm trying to use a collection view and in each item view use a handlerbars helper, but I can't get the helper function to expand my path into the value.
Ember.CollectionView.create({content: App.AController,
itemViewClass: App.ItemView
});
Em.Handlebars.registerHelper('editable', function (path, options) {
options.hash.valueBinding = path;
return Em.Handlebars.helpers.view.call(this, App.EditField, options);
});
<script type="text/x-handlebars" data-template-name="edit-field">
{{#if isEditing}}
{{view Ember.TextField valueBinding = "value" propagatesEvents = true}}
{{else}}
{{#if value}}
{{value}}
{{else}}
<span class="no-name">empty</span>
{{/if}}
{{/if}}
</script>
<script type="text/x-handlebars" data-template-name="item-view">
{{view.content.name}}
{{editable view.content.name}}
</script>
http://jsfiddle.net/epigeon/dNqsV/29/ with full code example.
The 'isEditing' property is on the view, but the context for the collectionView itemView is the content for that view. In order to refer to properties on the view in your template, you have to start the property path with 'view,' as in 'view.isEditing.'
I made that change in your fiddle and the example seems to work as I would expect.

How to create a multiselect list in Ember.js

I'm trying to figure out how to build a small app consisting of a list where you can select multiple items and toggle to select all/none and see the number of currently selected rows.
I believe that the "selected" state should not be part of the model objects, but I cannot really figure out how to do it.
This is my current setup (which doesn't work obviously yet)
Runnable code http://jsfiddle.net/jacobk/rU35G/1/
var App = Ember.Application.create();
App.ApplicationRoute = Ember.Route.extend({
model: function() { return Ember.A(["Foo", "Bar", "Baz"]); }
});
App.ApplicationController = Ember.ArrayController.extend({
allSelected: false,
selectedCount: function() {
return 0;
}.property()
});
App.RowController = Ember.ObjectController.extend({
isSelected: false
});
<script type="text/x-handlebars" data-template-name="application">
<h3>{{ selectedCount }} rows selected.</h3>
<label>
{{view Ember.Checkbox checkedBinding="allSelected"}}
Toggle select all
</label>
<hr/>
<ul>
{{#each controller itemController="row"}}
<li {{bindAttr class="isSelected"}}>
{{view Ember.Checkbox checkedBinding="isSelected"}} {{this.content}}
</li>
{{/each}}
</ul>
</script>
Should the individual "row items" be controlled using a custom view per row, or a custom controller like in the fiddle above
How to propagate the "select all" from the ArrayController to all the individual controllers (or views if that's a better fit)
I'm trying to understand when to use bindings, observers, properties, "needs" etc. and when its appropriate to use controllers vs views and so on. I've yet to grok the general flow of information/data in ember apps.
e.g. should the ArrayController from my example above iterate over the "contained" views/controllers and change the "selected" state when the "select all" check box is toggled OR should all the "sub controllers" observe/"have bindings to" the ArrayController and change themselves when it changes, and if so, how should I propagate data the opposite direction. How would the ArrayController get "all currently selected" rows?
I would love to see the "canonical solution" for this.
No need of row controller. #each, computed property and checkedbinding can be utilized to solve this as shown below. isSelected has to be defined in content of the arraycontroller:
App.ApplicationController = Ember.ArrayController.extend({
allSelected: function(key, value) {
if ( value !== undefined ) {
// when check box is ticked, this gets executed
this.setEach( 'isSelected', value );
return value;
} else {
//as a computed property
return !!this.get( 'length' ) &&
this.everyProperty( 'isSelected', true );
}
}.property('#each.isSelected')
selectedCount: function() {
return 0;
}.property()
});
I agree about keeping the selected state out of the model. You need to define the itemController in the Ember.ArrayController.
here is a working example. http://jsbin.com/sunat/3/edit
App.RowController = Ember.ObjectController.extend({
isSelected: false
});
App.IndexController = Ember.ArrayController.extend({
itemController: 'row',
selectAll: function(key, value) {
if (arguments.length == 2) {
this.setEach('isSelected', value);
return value;
} else {
return this.isEvery('isSelected', true);
}
}.property('#each.isSelected')
});
#template
<script type="text/x-handlebars" id="index" >
<label>
{{input type="checkbox" checked=selectAll}}
Toggle select all
</label>
<hr/>
<ul>
{{#each}}
<li>
{{input type="checkbox" checked=isSelected}} {{name}}
</li>
{{/each}}
</ul>
</script>

Categories

Resources