Ember-table: fails when used in a hidden div (workarounds welcome!) - ember.js

I'm trying to use ember-table inside a Bootstrap tab, but apparently if the table is contained in a tab that is initially display: none, the table layout doesn't work: everything is mis-sized and stuck in the upper-left of the table container.
I've narrowed the problem down by making a manually display-toggled div and it exhibits the same behavior.
I have a couple of ideas of how to workaround this, but I'm interested in others' ideas.
Also, should I file this as a bug? Seems like a common use case.

You can resize the table when you open the tab.
this.get('tableController').set('_width',820);

I had a similar situation, albeit not exactly the same. I initially show the ember-table component in a 'light' view, with one width, but the user can toggle between that and a 'full' view, with a larger width (as implied by its container). The toggle to larger width would size the container correctly but the portion of the ember-table not visible in the light view would contain only blank space, not cell contents nor borders, nor column headers. It's as if it never re-rendered after the container's dimensions changed.
The solution I found was to force a re-render of the table manually on the basis of an external property I would change when this light/full mode was toggled by the user. To achieve this, I extended the table component as shown:
export default Ember.Table.EmberTableComponent.extend({
// exposes a hook to resize (i.e. rerender) the table when it would otherwise
// not have been detected
resizeTriggered: function() {
this.handleWindowResize();
}.observes('resizeTrigger')
});
Since I'm using ember-cli, I saved the above under components/my-table-component.js.
Having done this, I would then include the table in my template like this:
{{my-table-component
columnsBinding="columns"
contentBinding="content"
resizeTrigger=resizeTriggerProperty
}}
Now, I simply update the controller's resizeTriggerProperty to any (new) value every time I want to ensure that the table re-renders. In my case, I would set it in the course of an action (e.g. MyRouteController):
actions: {
triggerResize: function() {
this.set('resizeTriggerProperty', new Date().getTime());
}
}

This may be a little late, but I am using Ember Table 0.4.1 (latest), and I found that ember table redraws on window resizes, so I used this to make the tab show the table when the tab changes:
$(window).resize();
and this did the trick, ember table redraws itself whenever I change the tab.

Related

Ember.js: sending actions to components?

I have a mixin for Ember components, named in-view, the job of which is to request that that the element be brought in view. It is provided an attribute whose value is an piece of content to be brought into view, and if that attribute matches the component's content then I call scrollIntoView or the equivalent. The code looks something like this:
// calling template
{{#each items as |item|}}
{{my-item content=item inViewItem=inViewItem}}
}}
// mixins/in-view.js
scrollIntoView() {
if (this.get('content') === this.get('inViewItem'))
this.get('element').scrollIntoView();
}.on('didInsertElement')
// components/my-item/component.js
import InView from 'mixins/in-view';
export default Ember.Component.extend(InView,
This works fine. The question I have arises when I want to change the item in view. I can have the in-view mixin observe the inviewItem attribute:
}.on('didInsertElement').observes('inViewItem')
and this also works, but seems like a bit of a code smell.
In addition, my actual code structure is that there is a controller which knows which item is supposed to be in view, and then its template calls a my-item-list component which displays the scrollable div containing the item list, and that in turn calls the my-item component. This means I have to pass the inViewItem attribute from the controller down through two levels, as in
// resource/controller.js
inViewItem: something
// resource/template.js
{{my-item-list items=item inViewItem=inViewItem}}
// components/my-item-list/template.js
{{#each items as |item|}}
{{my-item content=item inViewItem=inViewItem}}
}}
I could avoid this by having the my-item template hard-wired to access the inViewItem attribute on the controller:
scrollIntoView() {
if (this.get('content') === this.get('controller.inViewItem'))
this.get('element').scrollIntoView();
}.on('didInsertElement')
but that's another code smell; I don't want to build this kind of dependency on a specific controller field into the mixin. Instead I could possibly pass the component the name of the controller attribute to watch, but this seems unduly clumsy, and it's tricky to observe an attribute whose name is variable. More importantly, I don't think this will work when controllers go away in 2.0.
What I want essentially is a way to "ping" or somehow send a message to a template. I know that in principle this violates the DDAU principle, but in this particular case what I need is exactly to somehow send an "action down"--an action telling the component to adjust itself to bring itself into view.
Of course, I could give up on the entire idea of the in-view mixin and simply have the controller dig down into the generated HTML to find the item to bring into view and issue the scrollIntoView on it directly. However, this seems to violate some principle of separation of concerns; the my-item template would no longer be in complete control of itself.
What is the recommended design pattern for this kind of case?
The solution here is to go the opposite direction that you have. Your component here is a localized scope, and the pain you are feeling is that your localized scope needs to access and mutate global state (the app's scroll position).
Some people use a scroll-service for keeping track of and mutating state, I've used several variations on that myself.
It sounds though like you're dealing with a scrollable list, perhaps a div, and that what item is in view isn't merely a function of page state, but programmatically may change. For instance, a new item has been inserted and you want to scroll the new item into view.
A plugin like jquery.scrollTo or similar (collectively "scroller") would be better for that than simply jumping to the new position as it preserves the user's contextual awareness to where they are on page.
With a scrollable div or list or similar, you might choose to have your top level component control scroll state. The scroll state is still localized in this case, but instead of being localized to each item it's been localized to the scrollable region as a whole, which is where it better belongs.
There are a number of patterns for list items to register themselves with a parent list-component. In a robust scenario, I might do so, but a quick and not very dirty approach is to do something wherein on didInsertElement the new child emits an action to the parent containing it's context, which the parent then uses to check if it's the active item and if so triggers the scrollTo.

Ember select element - unable to capture on change event

Ember question - still getting used to Ember, but making progress. Here's my issue: I have a template which references a component; the component contains a select element. The select element displays properly, and I want to update the contents of another select element based on the selection in the first element. However, I have not been able to capture the on change event of the first select element. Here is the component code containing the select:
{{view
"select"
content=types
value=selectedtType
selection=selectedtType
prompt="Select Type..."
}}
So I'm not sure how to reference the on change event in the component template, or where the function itself should go - the component's component.js file, or in the route.js file of the parent template. I've done much research on this, but haven't been able to make it work yet. Any help would be appreciated. Thanks.
Check this JSBin. In this example, I had my dependent select use a computed property as its content. This computed property's single dependent key is the value of the first select. If you are doing this with components, your component needs to take the computed property as the attribute that is the select views' content. Any time the first select changes, it will cause the computed property to recompute and thus update the content of the second select. Even in an example using components, this code would probably sit on the controller to keep your component generic enough that it simply takes a content for its select and displays it rather than controlling the display logic itself.
Other options, have a function that observes the first select value and updates a controller variable that is the second select's content. Now if you're example is more complicated in that the changing select's have different option.valuePath and option.labelPath, you can pass pass those values into your component as well.

What is the ember way to add popovers to views?

I'm working on a events board app. Events are displayed in columns at the height matching the start time and pack into the space if there is more then one overlapping. Each event is a view and I want to have a div next to the view that shows and hides on hover.
I know how to bind to mouseEnter and mouseLeave to show and hide part of the template but I want to show something adjacent to my view/template not within it.
I've already got some computed properties on the view to place the event with the correct height and width so I don't want to add the popover inside the view.
Here is something to mess with http://jsbin.com/osoner/1/edit
Seems like something simple but I want to make sure I'm doing things the Ember way.
After messing a little with your provided jsbin, here the results.
Basically what I've done was adding a new popup css declaration wich do position the popup so that it appears outside the parent view, and also moved the {{#if...}} helper into the originating view.
If you want to go more fancy, checkout this jsfiddle wich uses the twitter boostrap popover.
Hope it helps.

Ember: Cannot read property 'enterStates' of undefined and textfield update

I have the following problem with ember.
I have a table with a set of datas. I have an event that returns me the current element of the table. Then it opens another view by transitioning into a new state and writes the selected table data in a textfield.
click: function(e) {
var element = $(e.target).closest("td");
App.tee = element.text().trim();
var router;
router = this.get('controller.target.router');
router.transitionTo('newRoute')
As you can see I have some other routes in my router as well.
The last two routes(home and profile) are part of a nav-tab. They work perfectly beside I click on the table. Then i get the following error when i click on a tab: Uncaught TypeError: Cannot read property 'enterStates' of undefined
Ok i give it another try to explain what i wanted to do.
What i want to do is to create a table with some data (for example persons).
When i click on a specific person in the table the corresponding table-data should be shown in the textfields that appear below. Whenever i click on another person the textfields below should change to the informations of the current clicked person in the table. When i click "New" Button a more detailed Tabview appears on the right side with some additional informations. I was playing around with ember and so far i just implemented some of the views and the routing. Im stucked as i have tried to implement the event that updates the textfield below the table. It updates once but after it has transitioned into the new state(newRoute) nothing happens. I know the code is very raw, but it is just a test to understand how this works in ember.
Ok the solution was easier than i thought. The problem was not the state changing. It was more a problem of how to access the data and how to effect the change of binded data. I realised too late that i needed to understand how the variable access works in Ember and what exactly the App.initialize() method does. So App.initialize() initializes all Controller classes in the router. If you want to access any variables within a controller you have to get the access over the router like
{{view Ember.TextField valueBinding="App.router.tableController.person"}}
Secondly i wasnt familiar with the usage of the set and get methods in Ember and the difference between extend and create. I wondered before where ember instantiates my object.
So my problem had nothing to do with states it was just a totally misunderstanding of the ember framework. Im a noob thats all.
Ok, this is the first shot of the answer.
I think the main issue is just a typo gotoHome instead of goToHome in the template.
By the way I get rid of some deprecation warnings by using <button {{action }}></button> instead of Ember.Button.
There is some other warnings when I click on the table, because you are referencing some properties which don't exist.
here is the corresponding fiddle: http://jsfiddle.net/Sly7/rKw9A/25/
Since I don't understand how it should work exactly, I'm not sure of the overall behavior. I let you explain me the flow (by editing the question please).
Any other comment is welcome :)

Ember classNameBindings not being called for the sample program

I am facing issue with class name bindings. Here is the jsfiddle code for the same. Logging the number of times binding is called. It is never called when the property is changed.
You appear to have a couple of issues here. Primarily, if you want properties to be recalculated when the contents of an array change, you cannot just depend on the array property itself - it will only fire a change when it is set to a different array. If you depend on myArray.#each instead, your property will be recalculated when the contents change as well.
Next, your template containing the span isn't rendering because you're providing an empty view template in your handlebars view declaration. Change your "HTML" to:
{{view App.contact}}
and your span will appear.
Finally, running Ember.run.sync() does not appear to be enough here. I am not as clear on the reason behind this but...computed properties only update when read (versus observers that update immediately). I would hypothesize that since your computed property is only used by the view and the view may only update on a subsequent run through the JS event loop, your computed property is recalculated only once for all your changes to "subordinates". Change your code to use timeouts and it'll work fine.
Here's a jsfiddle with all of my proposed changes.