Creating a new model in ember with a zurb foundation range slider bound to an attribute - ember.js

I am trying to use the range slider from the zurb foundation framework with Ember. I want a slider next to an input field, both of them showing the same value updating each other so a user would be able to enter either a number by hand or use the slider to set a score for a game played in the past.
My template looks like this:
{{input type="text" id="new-game-score" value=newGameScore}}
<div class="range-slider" data-slider={{newGameScore}} data-options="start: 0; end: 16;">
<span class="range-slider-handle" tabindex="0" role="slider"></span>
<span class="range-slider-active-segment"></span>
</div>
And in the controller I'm calling this.get('newGameScore'); to get the value for the creation of the model.
When I open the template I get a text-input that behaves like I expect. It allows to set the score for the creation of the model. The data-slider-attribute of the slider next to it gets updated correctly, but it doesn't change how the slider looks and I can't move the slider at all.
When I set newGameScore as a function returning a number in the controller both the slider and the input field start out with the set value and I can move the slider. Moving it does not change the value of the input field though, while entering a number in the text field adjusts the data-slider-value without any visible change on the slider. The number used for the creation of the new model is the one from the text input.
I'm using ember-cli and installed foundation with ember install ember-cli-foundation-sass.
I have
var app = new EmberApp({
'ember-cli-foundation-sass': {
'modernizr': true,
'fastclick': true,
'foundationJs': 'all'
}
});
in my Brocfile and the corresponding view looks like this:
export default Ember.View.extend({
didInsertElement: function() {
this.$().foundation();
}
});
The controller is an ArrayController with the template showing all the games with their scores. I would like to keep at that way if possible.
I started out using the zurb foundation documentation:
http://foundation.zurb.com/docs/components/range_slider.html
and found the following solution to a more complicated but closely related problem:
http://danahartweg.com/foundation-5-sliders-in-dropdowns-using-emberjs
but I couldn't figure out how to properly synchronise the slider with newGameScore. http://danahartweg.com/foundation-5-sliders-in-dropdowns-using-emberjs states at the beginning of the article that ember with a foundation-slider is easy to do. So I assume I don't have to write custom on-change-functions to solve my problem, but maybe someone more experienced with ember can find a solution from bottom part of the article, although it seemed to me that this is only needed for the combination of the dropdown and the slider.
Thanks for reading and for any idea about this.
regards,
Andy

It's been a while since I've worked with an Ember app, but I do remember working with some of the quirks you've mentioned here.
In my article, I was data-binding both the slider value and the input value. Honestly, though, it would probably work better to just have the data-slider feed the input via the display_selector option without any binding. It's been a while since I worked with this code, so it might have been something I initially tried and failed with. It seems that might be better overall because it would let the display_selector option on the range-slider itself keep the internal state of the slider intact.
As far as using Ember data-bindings properly, html content is easy (as you've found), but element attributes are a little trickier. You have to use the bind-attr syntax: {{bind-attr data-slider="newGameScore"}}

I gave it another try after a good night's sleep.
I added:
$("#new-game-score").on('change', function(e) {
var target_slider = $('.range-slider');
var new_value = target_slider.attr('data-slider');
target_slider.foundation('slider', 'set_value', new_value);
});
And now the slider updated visibly when I enter values in the text-field.
I added:
var slider = this.$('.range-slider');
slider.on('mouseup.fndtn.slider touchend.fndtn.slider pointerup.fndtn.slider', function(e) {
$(e.target.parentElement).find('input[type=text]').trigger('change');
});
and to the slider: data-options="display_selector: #new-game-score and now it worked both ways!
I'm still a bit at a loss of why this all works, but my problem seems to be fixed.
It only works when I explicitly set newGameScore in the controller though and all in all I have the feeling I didn't use Ember as intended.
Also this only seems to work when I carefully make sure I release the mouse button while on the slider. If I want to set it to max for example and release the mouse button outside of where the slider is, the text-field gets updated but newGameScore does not.
I made it work with:
this.$(".slider-input").on("blur", function(e) {
var target_slider = $(e.target.parentElement.parentElement.parentElement).find(".range-slider")
var new_value = target_slider.attr("data-slider");
target_slider.foundation("slider", "set_value", new_value);
});
this.$(".range-slider").on("change.fndtn.slider", function(e) {
$(e.target.parentElement.parentElement.parentElement).find("input[type=text]").trigger("change");
});
in the view.
I'm still unhappy with setting the initial value in the controller. I'm unsure about on('blur'), but on('change') lead to a infinite circle of both functions calling each other.

Related

Recommended way to rerender page in Ember after language switch

I need to rerender whole page application after language has switched. I don't like to use observers for it because of performance issues.
Language switch is done by a Ember.View. I tried to rerender parentView after changing Ember.I18n.translations but running into a known bug. It works one time but Ember Inspector shows parentView has dutzend of children views of it's own afterwards. After another switch parentView got destroyed. Just like demonstrated in this JSFiddle.
Here is simplified code of my view:
export default Ember.View.extend({
templateName: 'language-switch',
languageChanged: function() {
// change language
var language = this.get('controller.language.selected');
Ember.I18n.translations = translations[language];
// rerender page
this.get('parentView').rerender();
}.observes('controller.language.selected')
});
There is also a discussion about that problem on discuss.emberjs.com. I tried the suggestions there but they don't work. Last post suggest a work-a-round to map over all views, rerender them and afterwards use a transition. I don't like that one since I am afraid getting side problems by that hack.
I am pretty sure there must be a way to do a language switch in Ember with ember-i18n but what's the right way to do that?
Update:
I tried to implement the hack from discuss.emberjs.com. It's working but it's very limited. I implement it like this in the view:
var self = this;
var currentRoute = self.get('controller.currentRouteName');
this.get('controller.target').transitionTo('loading').then(function(){
self.get('controller.target').transitionTo(currentRoute);
});
Problem is that all data stored in model is lost. I did not find a way to get the current model and use it in transition. Also query parameters are lost. That limitation makes this work-a-round unacceptable for my application.
Update App.reset():
As suggested in comment I tried to use App.reset(). It works better than transitionTo work-a-round but doesn't match all needs. If calling App.reset() queryParams aren't lost but model is reseted. I'm looking for a way to rerender application while keeping current model and queryParams.
It seems that mostly these problem is handled by a full page reload on locale change. E.q. one of the main developers of ember-i18n said so in this discussion.

How do I make a component absolutely unique?

So this is pissing me off at the moment. According to the Ember documentation a component is reusable, but apparently it is not unique!
So I have spend two days to make a custom date selector component. The component consists of three text inputs (day, month and year) and some additional functionalities that are displayed in popups on focus. But when I have two date selectors in one screen, when I select just one of them, the popups of both date selectors appear.
Now one of these popups contains buttons for all days of a month, 0 to 31. These are added in the template with an {{#each}} helper. Makes sense right? Well, if I have two date selectors, these buttons are added twice to both components, displaying 0-31 and another 0-31.
So, first off all, I'd like to warn everybody. A component is not unique! It's a singleton with properties and methods shared by all instances. This is not made clear at all in the Ember documentations. (which sucks anyway since a lot of info is missing)
Second, I'd like to know if someone has a quick fix for this. Is a component the way to go, or should I switch to something else?
The issue you are seeing is because you are setting the foo property on the FooBarComponent Class/Prototype, which is shared between all instances.
To achieve what you want you need to set the foo property on each FooBarComponent instance. One way to do that is to set the foo property in the init hook like below.
App.FooBarComponent = Ember.Component.extend({
init: function(){
this._super();
this.set('foo', Ember.A(["bar"]));
},
actions: {
onClick: function() {
this.get('foo').pushObject("whatever");
}
}
});
You can see a working bin here: http://jsbin.com/leyut/1/edit

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

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.

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 :)

use results from ember data findQuery as ArrayController contentBinding

I'm trying to write my first "real" ember app. I've gone through a couple tutorials and I'm now attempting to use ember in conjunction with Ember Data to fetch data from a Rails app and display it.
I've gotten it to fetch the data, parse it, and display it, although I'm not convinced it's in the best way possible. I have an App.itemsController that is similar to this:
App.itemsController = Em.ArrayController.create({
content: App.store.findQuery(App.Item, {visible: true}),
});
I also have an App.ItemIndexView, whose template looks like
{{#each App.itemsController}}
{{id}}{{view App.ItemView item=this}}
{{/each}}
I have a couple of questions about this.
First and foremost, I want to allow a user to change an items visibility to false. I have this code in the App.ItemView file:
acknowledge: function() {
this.item.set('visible', false);
App.store.commit();
}
the record gets updated, however I want that entire item to drop from the view and it doesn't. How do I make this record be removed from App.itemsController.content?
My second question, which may in fact also answer the first, am I completely off in the boondocks as far as my implementation of this? I feel like there should be a way for me to set something like contentBinding: 'App.store.findQuery(App.Item, {visible: true})' and have non-visible ones be removed but I've tried that and nothing shows up. So I'm wondering if something in the way I have my whole app setup is flawed and keeping things from playing nice.
You can use the filter function instead of findQuery:
content: App.store.filter(App.Item, function (item) {
return item.get('visible');
})
The result will be re-evaluated when underlying data change. You still have to get data from the server via find/findAll/findQuery though.