How do I observe properties in an Ember route template? - ember.js

I have a route that sometimes shows a dialog. My idea for this was to create a component like this:
{{modal-dialog visible=dialogVisible contents=dialogContents}}
The route would, as needed, set the dialogVisible and dialogContents properties, and the template would pass these properties on to the modal-dialog component accordingly, so the dialog would appear and show the correct content.
But that doesn't work. Templates observe routes' models, not their properties. How can I get access to route properties in my template?

Not sure what is your problem. The way your're doing it seems correct.
controllers/index.js
actions: {
createModal: function(someSpecialContent) {
this.set('dialogVisible', true);
this.set('dialogContents', someSpecialContent);
$('#modalId').modal('show');
}
 }
templates/index.hbs
{{modal-dialog visible=dialogVisible contents=dialogContents}}
templates/components/modal-dialog.hbs
{{#if visible}}
your modal html here
...
<div class="modal-body">
{{#each contents as |content|}}
{{content.stuff}}
{{/each}}
</div>
...
{{/if}}

Have you simply tried .observes('model.PROPERTY_NAME')?
Why is it that you're trying to observe this property from the route? I've done something similar before and it seemed to make much more sense setting the dialogVisible property on the controller, not the route.

Related

Specify a login only template in Ember

Is there a way in EmberJS to have a separate template for login that doesn't render the rest of the application template? Ideally I would like to create a login component, but I can't figure out how to render it on the login route without it being wrapped in the application template.
Unfortunately there isn't. There might be some very clever hacks, but there's no way out of the box. However, I had the same problem and solved it using a simple if statement. You can use the currentRouteName property to get the current route name, which allows you to do something like this:
Application Controller
isLoginRoute: Ember.computed('currentRouteName', {
get() {
return (this.get('currentRouteName') === 'login');
}
})
Application Template
{{#if isLoginRoute}}
{{outlet}}
{{else}}
<div>
<span>Some content</span>
{{outlet}}
</div>
{{/if}}

Differing models inside ember #each

I'm working on an Ember.js application and have a table of devices, where I would like each listed device to open a modal dialog, when clicked, using that specific device as a model for the dialog.
The official documentation is slightly vague on how to do this, so I've been doing some guessing. I tried to pass the id of each device as a parameter inside the template:
{{#each}}
<button data-toggle="modal" data-target="#modal" {{action 'openViewModal' 'viewDeviceModal' id}} class="btn btn-default btn-xs">View details</button>
{{/each}}
{{id}} works as intended elsewhere in the template.
Now, inside the route, I put this:
actions: {
openModal: function(modalName, id) {
this.controllerFor(modalName).set("model", this.store.find('device', id));
return this.render(modalName, {
into: "application",
outlet: "deviceModal"
});
},
This gives me the following error:
"The value that #each loops over must be an Array. You passed (generated devices controller)"
Presumably, there is some confusion with the model of the list of devices (#each) and the model of the modal dialog. The modal dialog does not have #eachinside it.
I also lack a means to find out if my variables actually are what they are supposed to be. console.log does not work. I have not yet read all the documentation on debugging ember, so I'm hoping there will be some pointers there. Meanwhile, any help on how to get my modal dialog models to work would be greatly appreciated.
This is not a problem with your each loop, but with your model. You're setting the model for the controller as a single record when it looks like it expects an array of records. Change the first line of your action to this:
this.controllerFor(modalName).set("model", [this.store.find('device', id)]);
Also, as a tip, don't load the record from the store again when you already have it. Just pass the whole record in the action helper:
{{action 'openViewModal' 'viewDeviceModal' this}}
Then your action handler can look like this:
openModal: function(modalName, record) {
this.controllerFor(modalName).set("model", [record]);

how to set an itemView for an itemController in Ember?

I have successfully implemented an ArrayController an defined an ItemController for it like this:
export default Ember.ArrayController.extend(InboxTab, {
itemController: 'messages.message-list-item'
});
then in the template for the array controller I just do
{{#each}}
<li {{action 'someActionFromItemController'}}>{{someComputedPropertyFromItemController}}</li>
{{/each}}
This works great and I can handle a lot of actions and computed for each item, but I'm running into difficulties associating a view to each item. The docs are not helpful. The only instance of itemView is in this article:
http://emberjs.com/api/classes/Ember.CollectionView.html#sts=Specifying itemViewClass
and here the example seems to revolve around adding the view to the template and specifying the content from there and I'm not sure how that applies to the way I'm doing it.
A different way you could do it:
{{#each itemController='messages.message-list-item'}}
{{#view your-view}}
{{action}}{{computed property from view/controller}}
{{/view}}
{{/each}}
specify the item controller in the loop and wrap the actions and properties in the view - which means either of these could also be called or set in the view.
I think way to do this best is with itemViewClass, see the api for the full details.
{{#each message itemController='messageListItem' itemViewClass='messageListItem'}}
{{! Assumes you have view defined in an App.MessageListItemView defined in your JavaScript}}
{{action}}
{{/each}}

where would I define this helper in Ember for handling site search functionality

I am just starting out with Ember and am curious if I had a template like the following:
<script type="text/x-handlebars">
<input type='text' id='myVal' /><button {{action "searchInventory"}} class='search_inventory'>search inventory</button>
{{outlet}}
</script>
Where would I define the searchInventory helper? It seems like it might be in some global controller that might forward it to a search results route; I have an InventoryItemController but how can I hook up the searchInventory action to this? What would be the Ember way to set this up?
Can I tell ember when invoking the action to use that controller like:
{{action "searchInventory" controller:"InventoryItem" }}
thx
I don't know how you have structured your app, but assuming you have a controller for the template above, you should define the action on that controller within an 'actions' hash. Also, you should use the
{{input}}
handlebars helper instead of using the input tag. If you do that, you can have a property on the controller for this template, say 'searchTerm' for which you can provide a valueBinding, like this:
{{input type="text" valueBinding="controller.searchTerm"}}
This will bind the input the user types into the input element to the searchTerm property on the controller. Hope this helps.

how do you catch a 'click' on a {{linkTo}} using a view?

(Note: I am using Ember version 1.0.0-rc.3)
I'm trying to catch the 'click' of a {{linkTo}} using a view, so that I can do additional stuff (basically scroll the list of users in the sidebar) besides merely loading the new template. Me being relatively new to this (but having read the documentation!), I thought the following would just work:
"users" template:
{{#each user in users}}
{{#view App.ClickView}}
{{#linkTo user user}}{{ user.name }}{{/linkTo}}
{{/view}}
{{/each}}
the view code:
App.ClickView = Ember.View.extend({
click: function(evt) {
// do stuff
}
});
and for context, the layout template:
<div id='sidebar'>
{{#each user in users}}
{{#linkTo user user}}{{ user.name }}{{/linkTo}}
{{/each}}
</div>
<div id='main'>
{{ outlet }}
</div>
Referring back to the users template, you can see that each {{linkTo}} is contained within a view. I'm expecting for a click on that {{linkTo}} to therefore bubble up to, and caught by the view (App.ClickView). Unfortunately, it doesn't. It seems like the click is somehow not being bubbled up to the view when it's happens on a {{linkTo}}... What should I do?
Note #1:
If I replace the {{linkTo}} (only in div#main! I don't intend to replace the ones in div#sidebar) with an <a> element, it works, and the click gets caught by the view. However, I'm not so sure that i want to go down this route (I'd have to replicate the functionality of the {{linkTo}}!). And I'm thinking that there ought to be a better way to do this. Is there?
Note #2:
*Note that I'm also aware that i can put my intended "do stuff" code in renderTemplate() of the UserRoute, but the problem with that is that the effect will happen for every link to that route (including the ones in the sidebar - which is not what I want). I want the scroll to only trigger for specific {{linkTo}}s - specifically the {{linkTo}}s in div#main.
I would suggest using an anchor (<a>) tag with {{action ... target="view"}} in it instead of linkTo, apply your conditional logic in the view, and then if appropriate, re-send to the controller (this.get('controller').send(actionName), let it bubble to the router, and do a transitionTo in a router event handler.