the controller looks like this
class App.ContactsController extends Tower.Controller
index: (params) ->
#set('person', App.HighrisePerson.create())
#render "index"
the view looks like this
App.ContactsEditView = Ember.View.extend
templateName: 'contacts/edit'
resourceBinding: 'controller.person'
init: (args...) ->
#._super(args...)
console.log(#.get('resource'))
console.log('inited')
with the above block i see in the console that resource is set to an instance of my Ember.Object
but with the following viewcode
div class: "row-fluid", ->
text "{{#with resource}}"
text "Hello"
text "{{/with}}"
relevant part of parent view
div class: "row-fluid contact-form", ->
div class: "row-fluid", ->
h1 "Want to work with us?"
p "So...you want to be kohactivated!?!? Please take a few moments to fill out the form below and provide us with some details about your project, company or start-up."
text "{{view App.ContactsEditView}}"
i see no rendered output for hello
if i move hello outside of the #with block i see hello,
so I assume that it isnt recognizing resource for some reason
Any help is appreciated.
That's right -- it's looking for resource in the current handlebars context rather than the view (which as of ember.js 0.9.8 I believe, is no longer the default context). You'll need view.resource instead.
(Side note: conversely, to reference the handlebars context from the view, use context.whatever)
Related
I have an Ember demo app that works fine if the first route loaded is 'index', 'list' or 'list/index', but not if the first route loaded is 'list/show'. Code is at https://github.com/DougReeder/beta-list , demo is running at https://ember-demo.surge.sh To see the problem, set your window narrower than 640px and surf to https://ember-demo.surge.sh/list/5 You'll see the list panel, rather than the detail panel.
The underlying problem is that, when the route is 'list/show', the divs with class 'panelList' and 'panelDetail' should also have the class 'right'.
I can't set this in the template, because panelList and panelDetail are created by the parent 'list' template. If I move panelList and panelDetail to the child templates 'list/index' and 'list/show', then the list gets re-rendered when going from 'list/index' to 'list/show' which would be a terrible performance hit.
Currently, I use the 'didTransition' action to toggle the class 'right'. This is called both then transitioning from 'list/index' to 'list/show', and when 'list/show' is the initial route. Unfortunately, if 'list/show' is the first route, none of the DOM elements exist when 'didTransition' is called.
I can envision two routes to a solution, but don't know how to implement either:
Toggle the class 'right' on some action which happens after DOM elements exist.
Insert conditional code in the 'list' template, which sets the class 'right' on 'panelList' and 'panelDetail' if the actual route is 'list/show'.
Suggestions?
Answer current as of Ember v2.12.0
You can use the link-to helper to render elements other than links, with styles that change based on the route. Utilizing the activeClass, current-when, and tagName properties, you can basically have that element be styled however you want depending on which route you are on. For example, to render your panelList div:
{{#link-to tagName='div' classNames='panelList' activeClass='right' current-when='list/show'}}
More markup
{{/link-to}}
I love a trick with empty component. In didInsertElement and willDestroyElement hooks you can add and remove a css class from parent element or (I like it better) body. Here is a code:
import Ember from 'ember';
export default Ember.Component.extend({
bodyClass: '',
didInsertElement() {
const bodyClass = this.get('bodyClass');
if (bodyClass) {
Ember.$('body').addClass(bodyClass);
}
},
willDestroyElement() {
const bodyClass = this.get('bodyClass');
if (bodyClass) {
Ember.$('body').removeClass(bodyClass);
}
}
});
I use it in template (in my example it's a template of player route) like this
{{body-class bodyClass='player-page-active'}}
To apply classes to parent element, you can use this.$().parent(), but using body is more reliable. Note that this component will create an empty div, but it shouldn't be a problem (can be in rare cases, fix it with classNames and css if needed).
Sukima suggested looking at currentRouteName, and I thus found hschillig's solution, which I simplified for my case. In the controller, I created an isShow function:
export default Ember.Controller.extend({
routing: Ember.inject.service('-routing'),
isShow: function() {
var currentRouteName = this.get('routing').get('currentRouteName');
return currentRouteName === 'list.show';
}.property('routing.currentRouteName'),
In the template, I now use the if helper:
<div class="panelList {{if isShow 'right'}}">
RustyToms's answer eliminates the need for adding a function to the Controller, at the expense of being less semantic.
We have an example ember app where we CRUD recipes. For the route where we create a new Recipe, if we pass attributes into createRecord like so:
Cookbook.RecipesNewRoute = Ember.Route.extend
model: ->
Cookbook.Recipe.createRecord(title: "blank")
the recipe appears immediately in the list on the left side of the screen. Here's the jsbin to show what I mean
However, if I create the recipe without args a la
Cookbook.Recipe.createRecord()
I don't see the recipe appear in the list until I edit one of the attributes. This happens even though I've specified default values, as proved by this jsbin.
My question is: Why does this happen and how can I create a record with no params specified and still have it show up immediately?
Looking at your jsbin's the only thing you are missing is #get('store').commit() after you create the empty record.
Here an example:
Cookbook.RecipesNewRoute = Ember.Route.extend
model: ->
Cookbook.Recipe.createRecord()
#get('store').commit()
and here your working jsbin. I've added id's in parenthesis to the template so you can see the record is created correctly.
EDIT After your comment I've made some changes to the jsbin, basically what I've changed is this:
Cookbook.RecipesNewRoute = Ember.Route.extend
setupController: (controller) ->
controller.startEditing()
Cookbook.RecipesNewController = Ember.ObjectController.extend
startEditing: ->
#controllerFor('recipes').pushObject(Cookbook.Recipe.createRecord())
I've added the method startEditing for convenience (you can call it what ever you want).
In the startEditing method as you can see we get the recipesController and add a new record to the content array, this causes the new recipe to show up even if not commited to the store. Give it a try.
Hope it helps
So it seems pretty hacky, but here's how I got this working the way I wanted it to:
Cookbook.RecipesNewRoute = Ember.Route.extend
model: ->
recipe = Cookbook.Recipe.createRecord()
#controllerFor('recipes').get("content").addReference(recipe.get("_reference"))
recipe
I don't know if this is kosher, at all, but it seems to work. Feedback and improvements are welcome. I won't mark this as answered for a few days to give anyone else a chance to come up with something better.
look into the HTML and you will see that the <li> element has been created, but the title property of this model is empty, so the new recipe doesn't show up in the list.
<li>
<a id="ember413" class="ember-view" href="#/recipes/ember369">
<script id="metamorph-12-start" type="text/x-placeholder"></script>
<script id="metamorph-12-end" type="text/x-placeholder"></script>
</a>
</li>
I am trying to put together a simple master-details Ember app. Directory tree on one side and file list on another.
Ember offers few helpers to render context into a view. Which of them I can use for:
Subtrees of the directory tree.
Details list.
In fact, would be very helpful if someone can point me to any docs I can read about the difference between {{render view}}, {{view view}} and {{control view}} helpers and how to use them properly.
Thanks a lot!
{{view "directory"}} renders the view within the context of the current controller.
{{render "directory"}} renders the view App.DirectoryView with template directory within the context of the singleton App.DirectoryController
{{control directory}} behaves the same way as render only it creates a new instance of App.DirectoryController every time it renders (unlike render which uses the same controller instance every time).
Update 18 Feb 2014: {{control}} has been removed.
The last two helpers are relatively new, so there isn't much documentation about them. You can find {{view}} documentation here.
Now looking at your use case, I don't think you need any of these helpers. Just use nested routes and the {{outlet}} helper and it should just work.
App.Router.map(function(){
this.resource('directories', function() {
this.resource('directory', { path: '/:directory_id'}, function() {
this.route('files');
});
});
});
You can build on that following this guide.
UPDATE: {{render}} now creates a new instance every time if you pass a model.
For a very good explanation of the helpers render, partial, outlet and template have a look at this question.
Just as a rough a summary, how one might use those helpers:
{{render "navigation"}} -> Renders the NavigationController and NavigationView at this place. This is helper is good for places, where the Controller and View do not change, e.g. a navigation.
{{outlet "detailsOutlet"}} -> This will provide a stub/hook/point into which you can render Components(Controller + View). One would use this with the render method of routes. In your case you will likely have a details route which could look like this. This would render the DetailsController with DetailsView into the outlet 'detailsOutlet' of the index template.
App.DetailsRoute = Ember.Route.extend({
renderTemplate: function() {
this.render('details', { // the template/view to render -> results in App.DetailsView
into: 'index', // the template to render into -> where the outlet is defined
outlet: 'detailsOutlet', // the name of the outlet in that template -> see above
});
}
});
{{view App.DetailsView}} -> This will render the given view, while preserving the current context/controller. One might change the context, e.g. using your master entity and pass its details to a view like this:
{{view App.DetailsView contextBinding="masterEntity.details"}}
This is helper is useful, when you want to encapsulate certain parts of a component in subviews, that have own custom logic like handling of actions/events.
{{control}} I know that control instantiates a new controller every time it is used, but I cannot see a good fit for your, nor have i a good example for using it.
To Understand the difference between ember {{render}},{{template}},{{view}},{{control}}
you can refer this article
http://darthdeus.github.io/blog/2013/02/10/render-control-partial-view/
I'm having trouble doing things on Ember, and I'm sure it's because I still haven't fully grasped "the Ember way" of doing things, and I'm a trying to do a couple of things that get out of the scope of standard tutorials out there.
I'm developing some sort of textfield-with-suggestions component that will appear in every single page of my web app. I won't ask here all the details on how to do it, but just a couple specific things I am having trouble accomplishing from the start. The following are the relevant code fragments of what I have so far.
// handlebars template: searchbar.hbs
{{view App.SearchField viewName="searchField"}}
<div class="results" {{bindAttr class="searchField.hasFocus"}}>
This is where the results for whatever the user has typed are shown.
</div>
// coffeescript source of the views: searchbar.coffee
App.SearchBar: Ember.View.extend
templateName: 'searchbar'
App.SearchField: Ember.TextField.extend
placeholder: 'Search'
hasFocus: false
eventManager: Ember.Object.create
focusIn: (event, view) ->
#set('hasFocus', true)
focusOut: (event, view) ->
#set('hasFocus', false)
// somewhere in the layout of the pages in my app
<div id="header">
{{App.SearchBar}}
</div>
This will probably also need a controller, but I haven't developed it yet, because I don't know where it fits within this setup yet.
First, I want the suggestions popup panel to appear as soon as the search field obtains focus. That's the reason of my attempt above to implement a hasFocus property on the searchfield. But how do I achieve making my div.results panel react to the focus state of the input field?
In general, and this is the core of my question here, how do I connect all things to develop this component? If the answer is by attaching it to a controller, then how do I setup a controller for this component, and how do I specify that it is the controller for it, so that it acts as a context for everything?
I think you have to clearly separate concerns. Stuff related to the view (ie manipulating the DOM with jquery) should stay in the view. Stuff related to the application state should be in the controller. Though,in your case, I think you can simply bind an observer on the hasFocus property, and show the suggestions. Something like:
App.SearchField: Ember.TextField.extend
placeholder: 'Search'
hasFocus: false
eventManager: Ember.Object.create
focusIn: (event, view) ->
#set('hasFocus', true)
focusOut: (event, view) ->
#set('hasFocus', false)
focusDidChange: (->
if #hasFocus
$('.results')... // here I let you do the suggestion stuff
// based on data retrieved from the controller
else
// probably hide the results div.
).observes('hasFocus')
When I do
var bob = App.MyModel.create().setProperties({ name: "bob marley" });
router.get('applicationController').connectOutlet('my', bob);
The instance of MyModel becomes the content property of the MyController instance. From what I read in the guide, the controller instance becomes the context for the handlebars template rendered by MyView.
Does that mean I always have to prefix the model's properties with content. in the templates?
<h1>{{content.name}}</h1>
is there something I'm missing or is this the correct way of doing it?
If App.MyController extends Ember.ObjectController, then you should be able to use {{name}} directly. This is because Ember.ObjectController extends Ember.ObjectProxy, rendering the content reference unnecessary.
If you're using Ember.Controller, however, you'll have to prefix the properties with content. like you said. For more details, see #trek's controller documentation that was recently committed.