Modifying a Handlebar template based on dropdown value - ember.js

Based on dropdown selected i.e if B is selected the template has to be modified for B.
Template -
<script type="text/x-handlebars" id="search">
...
<div class="controls">
<select class="input-medium focused">
<option>Any</option>
<option>A</option>
<option {{action 'chosenB'}} >B</option>
<option>C</option>
<option>D</option>
</select>
</div>
{{#if searchB}}
//Show view specific to B-only
{{/if}}
</script>
Router -
App.Router = Ember.Router.extend({
this.resource('search');
});
SearchController = Ember.ObjectController.extend({
searchB: false,
chosenB: function() {
this.set('searchB', true);
},
});
So, when I goto URL /search, intially B's only view is not shown as IF condition is not met. But, when a user selects B from dropdown, I have called action chosenB of SearchController from template which should have set the 'searchB" to true and B-only view should have appeared which is not happening..
Can you please help finding mistake here ..
Ember - 1.0.0-RC.2

Ember.js has a built-in Select view, please check out this doc http://emberjs.com/api/classes/Ember.Select.html, I think your problem is binding related, using the right component for the job would have the desired behaviour.
hope it helps

Related

Different layouts depending on sub resources

Sorry if this is a really obvious questions but I have the following routes:
Web.Router.map(function () {
this.resource('orders', function(){
this.resource("order", {path:":order_id"});
});
});
And for my orders template I have something like:
<div class="someclass">
{{outlet}}
</div>
And what I want todo is:
{{#if onOrderRoute}}
<div class="someclass">
{{outlet}}
{{else}}
<div class="someotherclass">
{{/if}}
</div>
I was wondering what the best way of doing this is, or am I mising something?
There are multiple ways to accomplish this. The view has a layoutName property you can use to specify your layout. Another option is to specify a property on your child view, and then your template can bind to that by using the view property.
For example:
Web.OrderView = Ember.View.extend({
childView: true
);
Then, in your template you bind to view.childView
{{#if view.childView}}
<!-- code goes here -->
{{/if}}
Further, you can even create a mixin and then just inject that mixin into every view.
Web.ChildViewMixin = Ember.Mixin.create({
childView: true
});
Web.ChildView = Ember.View.extend(ChildViewMixin, {
});

Ember checkbox and value

I have checkboxes created in a loop. I set checked to a property name, and want to reach a dynamic value from the checkbox on click. In the property (for key, value) I'm getting the wrong thing - the label of my property instead of the value. Is there a simple way in Ember to get the value out of the checkbox?
Any help is much appreciated.
In HTML:
{{#each url in controllers.application.env.urls}}
<div>
{{view Ember.Checkbox checked=updateServerList valueBinding="url"}}{{url}}
</div>
{{/each}}
In javascript:
updateServerList:function(key,value)
{
if(value!=undefined)
{
console.log("----1 ", key, value);
}
}.property(''),
Per your comment, I started actually playing around with this and realized that it was not as simple as I thought. What I ended up doing was just building a custom component rather than working with Ember.Checkbox.
In your template:
{{#each}}
{{check-box url=url}}
{{/each}}
Component Template:
<script type='text/x-handlebars' id="components/check-box">
{{input type="checkbox" checked=toggleURL}}{{url}}
</script>
The component code:
App.CheckBoxComponent = Ember.Component.extend({
toggleURL: false,
logURL: function() {
console.log(this.url);
// Do something with the URL
}.observes('toggleURL')
});

getting back reference to a specific model using Ember's Array Controller

I'm new to Ember and am finding some of their concepts a bit opaque. I have a app that manages inventory for a company. There is a screen that lists the entirety of their inventory and allows them to edit each inventory item. The text fields are disabled by default and I want to have an 'edit item' button that will set disabled / true to disabled / false. I have created the following which renders out correctly:
Inv.InventoryitemsRoute = Ember.Route.extend({
model: function(params) {
return Ember.$.getJSON("/arc/v1/api/inventory_items/" + params.location_id);
}
<script type="text/x-handlebars" data-template-name="inventoryitems">
{{#each}}
<div class='row'>
<p>{{input type="text" value=header disabled="true"}}</p>
<p>{{input type="text" value=detail disabled="true"}}</p>
<button {{action "editInventoryItem" data-id=id}}>edit item</button>
<button {{action "saveInventoryItem" data-id=id}}>save item</button>
</div>
{{/each}}
</script>
So this renders in the UI fine but I am not sure how to access the specific model to change the text input from disabled/true to disabled/false. If I were just doing this as normal jQuery, I would add the id value of that specific model and place an id in the text input so that I could set the textfield. Based upon reading through docs, it seems like I would want a controller - would I want an ArrayController for this model instance or could Ember figure that out on its own?
I'm thinking I want to do something like the following but alerting the id give me undefined:
Inv.InventoryitemsController=Ember.ArrayController.extend({
isEditing: false,
actions: {
editInventoryItem: function(){
var model = this.get('model');
/*
^^^^
should this be a reference to that specific instance of a single model or the list of models provided by the InventoryitemsRoute
*/
alert('you want to edit this:' + model.id); // <-undefined
}
}
});
In the Ember docs, they use a playlist example (here: http://emberjs.com/guides/controllers/representing-multiple-models-with-arraycontroller/) like this:
App.SongsRoute = Ember.Route.extend({
setupController: function(controller, playlist) {
controller.set('model', playlist.get('songs'));
}
});
But this example is a bit confusing (for a couple of reasons) but in this particular case - how would I map their concept of playlist to me trying to edit a single inventory item?
<script type="text/x-handlebars" data-template-name="inventoryitems">
{{#each}}
<div class='row'>
<p>{{input type="text" value=header disabled="true"}}</p>
<p>{{input type="text" value=detail disabled="true"}}</p>
<button {{action "editInventoryItem" this}}>edit item</button>
<button {{action "saveInventoryItem" this}}>save item</button>
</div>
{{/each}}
</script>
and
actions: {
editInventoryItem: function(object){
alert('you want to edit this:' + object.id);
}
}
Is what you need. But let me explain in a bit more detail:
First of all, terminology: Your "model" is the entire object tied to your controller. When you call this.get('model') on an action within an array controller, you will receive the entire model, in this case an array of inventory items.
The {{#each}} handlebars tag iterates through a selected array (by default it uses your entire model as the selected array). While within the {{#each}} block helper, you can reference the specific object you are currently on by saying this. You could also name the iteration object instead of relying on a this declaration by typing {{#each thing in model}}, within which each object would be referenced as thing.
Lastly, your actions are capable of taking inputs. You can declare these inputs simply by giving the variable name after the action name. Above, I demonstrated this with {{action "saveInventoryItem" this}} which will pass this to the action saveInventoryItem. You also need to add an input parameter to that action in order for it to be accepted.
Ok, that's because as you said, you're just starting with Ember. I would probably do this:
<script type="text/x-handlebars" data-template-name="inventoryitems">
{{#each}}
<div class='row'>
<p>{{input type="text" value=header disabled=headerEnabled}}</p>
<p>{{input type="text" value=detail disabled=detailEnabled}}</p>
<button {{action "editInventoryItem"}}>edit item</button>
<button {{action "saveInventoryItem"}}>save item</button>
</div>
{{/each}}
</script>
with this, you need to define a headerEnabled property in the InventoryitemController(Note that it is singular, not the one that contains all the items), and the same for detailEnabled, and the actions, you can define them also either in the same controller or in the route:
App.InventoryitemController = Ember.ObjectController.extend({
headerEnabled: false,
detailEnabled: false,
actions: {
editInventoryItem: function() {
this.set('headerEnabled', true);
this.set('detailEnabled', true);
}
}
});
that's just an example how you can access the data, in case the same property will enable both text fields, then you only need one, instead of the two that I put . In case the 'each' loop doesn't pick up the right controller, just specify itemController.

EmberJS - sharing a controller / template for different routes

I have a very simple CRUD application that allows for creating new objects as well as editing them.
The template used for adding a record and editing a record are almost identical.
They use the exact same form elements.
The only difference is the title and the button below the form (that should either update or create a record)
In my implementation, I have
2 route definitions
2 route objects
2 controller objects
2 templates
I was wondering if
I can't promote re-use here
if all of these objects are required.
What bothers me :
I have 2 separate templates for create and edit (while they are almost identical)
I have 2 separate controllers that do exactly the same thing.
I was hoping to solve this on the controller level.
As a controller decorates a model, in my case 1 single controller object could wrap either a new record or an existing record.
It could then expose a property (isNewObject) so that the template can decide if we are in the "new" or "edit" flow. The controller could have a single createOrUpdate method that works both in the new and in the update scenario.
Routes
The current implementation is using a new and an edit route for my resource.
this.resource("locations", function(){
this.route("new", {path:"/new"});
this.route("edit", {path: "/:location_id" });
});
The new route
The new route is responsible for creating a new record and is called when the user navigates to the new record screen.
App.LocationsNewRoute = Ember.Route.extend({
model: function() {
return App.Location.createRecord();
}
});
The edit route
The edit route is responsible for editing an existing object when the user clicks the edit button in the overview screen.
I haven't extended the default edit route but instead I'm using the auto generated one.
Controllers
The new and edit controllers are responsible for handling the action that occurs in the template (either saving or updating a record)
The only thing both controllers do is commit the transaction.
Note: I guess this is a candidate for re-use, but how can I use a single controller for driving 2 different routes / templates ?
App.LocationsNewController = Ember.ObjectController.extend({
addItem: function(location) {
location.transaction.commit();
this.get("target").transitionTo("locations");
}
});
App.LocationsEditController = Ember.ObjectController.extend({
updateItem: function(location) {
location.transaction.commit();
this.get("target").transitionTo("locations");
}
});
Templates :
As you can see, the only code-reuse I have here is the partial (the model field binding).
I still have 2 controllers (new and edit) and 2 templates.
The new templates sets the correct title / button and re-uses the form partial.
<script type="text/x-handlebars" data-template-name="locations/new" >
<h1>New location</h1>
{{partial "locationForm"}}
<p><button {{action addItem this}}>Add record</button></p>
</script>
The edit templates sets the correct title / button and re-uses the form partial.
<script type="text/x-handlebars" data-template-name="locations/edit" >
<h1>Edit location</h1>
{{partial "locationForm"}}
<p><button {{action updateItem this}}>Update record</button></p>
</script>
The partial
<script type="text/x-handlebars" data-template-name="_locationForm" >
<form class="form-horizontal">
<div class="control-group">
<label class="control-label" for="latitude">Latitude</label>
<div class="controls">
{{view Ember.TextField valueBinding="latitude"}}
</div>
</div>
<div class="control-group">
<label class="control-label" for="latitude">Longitude</label>
<div class="controls">
{{view Ember.TextField valueBinding="longitude"}}
</div>
</div>
<div class="control-group">
<label class="control-label" for="accuracy">Accuracy</label>
<div class="controls">
{{view Ember.TextField valueBinding="accuracy"}}
</div>
</div>
</form>
</script>
Note: I would expect that I can do something efficient/smarter here.
I would want my template to look this this : (getting the title from the controller, and have a single action that handles both the update and the create)
<script type="text/x-handlebars" data-template-name="locations" >
<h1>{{title}}</h1>
{{partial "locationForm"}}
<p><button {{action createOrUpdateItem this}}>Add record</button></p>
</script>
Question
Can I re-work this code to have more code-reuse, or is it a bad idea to attempt to do this with a single template and a single controller for both the "edit record" and "new record" flows.
If so, how can this be done ? I'm missing the part where my 2 routes (create and edit) would re-use the same controller / template.
You were correct throughout.
And you can use the new controller and template in edit route also.
You have to do only two things.
First give the template name as new in the renderTemplate hook of edit route.
App.LocationsEditRoute = Ember.Route.extend({
setupController: function(controller, model) {
this.controllerFor('locations.new').setProperties({isNew:false,content:model});
},
renderTemplate: function() {
this.render('locations/new')
}
});
As the new template is rendered the controller also will be newController, and you can have the action to point to an event in the newController.
If you want to change the title and button text, you can have them as computed properties observing isNew property.
Hope this helps.
P.S: Don't forget to set the isNew property to true in the setupController of new route
Use this:
App.YourNewRoute = Em.Route.extend ({
controllerName: 'controllerName',
templateName: 'templateName'
});
Only use initial name like for homeController user "home" thats it.
Example:
App.YourNewRoute = Em.Route.extend ({
controllerName: 'home',
templateName: 'home'
});
Now you can use template and controller methods of "home".

Accessing properties of a child view created within {{#each}} iterator

Edit: Problem was with my own local settings and the way I was including the views in my application. Once I fixed those issues, the problem was resolves. The code here is actually correct. The answer Chrixian provided also work.
I am stuck on something that seems rather simple. I want to access some computed properties of my view constructed inside an each loop in handlebars.
<div class='build-buttons-wrapper'>
<button class="list-builds-button" {{action "toggleBuildsList" target="view"}} ></button>
<button class="build-button" {{action "buildApp" on="click" target="view"}} >Build</button>
</div>
<div class='builds-list'>
<h2 class="build-title">Latest builds</h2>
<ul class="builds-list">
{{#each content}}
{{#view Jimux.BuildView buildBinding="this"}}
<span class="build-date">{{createdAt}}</span>
<a {{bindAttr href="srcArchive"}} class="download-button source">Source</a>
{{! *here are different ways I have tried to access "finished" property* }}
{{log build.view.finished}}
{{log view.finished}}
{{log finished}}
{{log this.finished}}
{{log build.finished}}
{{#if build.finished}}
<div class="build-progressbar"></div>
{{else}}
<div class="build-progressbar"><div class="build-percent" style="width:{{unbound percent}}%"></div></div>
{{/if}}
{{/view}}
{{/each}}
</ul>
</div>
Here is the BuildsView which is using this template:
Jimux.BuildsView = Em.View.extend({
templateName: 'builds'
listVisible: false
classNames: ['builds-view']
buildApp: (view, event, ctx) ->
#get('controller').newBuild()
,
hideList: ->
#set 'listVisible', false
this.$(".builds-list").hide("slide", {direction: "up"}, 300)
,
showList: ->
#set 'listVisible', true
this.$(".builds-list").show("slide", {direction: "up"}, 300)
,
toggleBuildsList: (view, event, ctx) ->
if #get 'listVisible' then #hideList() else #showList()
,
didInsertElement: ->
#hideList() if not #get 'listVisible'
})
And here is the BuildView which is created inside the {{#each}} iterator in the template above.
Jimux.BuildView = Ember.View.extend(
tagName: 'div',
classNames: ['build-item'],
#testBinding: true,
sample: true,
finished: ( ->
return true
#return (#get 'percent') == 100
).property('percent')
)
Everything above works as expected. For example I can access percent property of each child view using {{percent}}. But if I define my own properties inside the Jimux.BuildView as show above, I cant seem to find a way to access them within the handlebars {{#each}} iterator. You can see the different ways I have tried inside the handlebars code with {{log}} statements, all those print undefined in the console. What am I missing here?
I'm assuming the percent property you are referring to is a property of each "content" object you are looping over-- if that's the case making finished look like this:
finished: (->
return #get('context.percent') is 100
).property('context.percent')
You should be able to simply use {{finished}} within the {{#view Jimux.BuildView}} .. {{/view}} block