How to order addEventlistener in Ember. Want to execute my eventListener before 3rd party on same element - ember.js

Hi I am trying to do things with emberjs and google maps.
The issue with google autocomplete api is if user enters text in location search but does not select any value from dropdown suggestion in textbox the first suggestion is not selected. I found a good solution for it on stack overflow solutiion in jquery only
I am trying to do it in ember.
App.AutocompleteAddressView = Ember.TextField.extend({
tagName: 'input',
autocomplete: null,
didInsertElement: function () {
this._super();
var options = {
componentRestrictions: {
country: "xx"
}
};
this.set('autocomplete', new google.maps.places.Autocomplete(this.$()[0], options));
//var autocomplete = new google.maps.places.Autocomplete(this.$()[0], options);
},
keyDown: function (e) {
var that = this;
var suggestion_selected = $(".pac-item.pac-selected").length > 0;
if (e.keyCode === 13 && !suggestion_selected) {
//key down arrow simulations
var event = Ember.$.Event('keypress', {
keyCode: 40
});
Ember.run(this.$("#searchTextField"), 'trigger', event);
console.log('loggin autocomplete');
console.log(this.get('autocomplete'));
//adding google map event and then triggering it
google.maps.event.addListener(this.get('autocomplete'), 'place_changed', function () {
var place = that.get('autocomplete').getPlace();
that.set('autocomplete_result', place)
console.log('google event was triggered');
console.log(that.get('autocomplete_result'));
});
google.maps.event.trigger(this.get('autocomplete'), 'place_changed');
}
}
});
Now I need to do simulation part. I guess ember testing has somthing that simulates keypress but I cannot used it.
If I use the solution from the link I mentioned things work fine first time but on clicking browser back navigation it does not work.
The solution I tried in code not working

Related

How to clear the DOM with multiple tests in single ember integration test file

I have two tests in date-dropdown-test.js:
moduleForComponent('forms/date-dropdown', 'Integration | Component | forms/date dropdown', {
integration: true
});
test('it renders in month mode', function(assert) {
assert.expect(2);
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.on('myAction', function(val) { ... });
this.render(hbs`{{forms/date-dropdown dateFormat='MMMM YYYY' daySpecific=false dayToUse=26 dateUnitsLong=24 startIncrement=1}}`);
// Check 24 months appear
assert.equal(this.$('option').length, 24);
// Check next month is selected by default
var today = new Date();
today.setDate(1);
today.setMonth(today.getMonth() + 1);
today.setDate(26);
var expected = moment(today).format('DD-MM-YYYY');
assert.equal(this.$('select').val(), expected);
});
test('it renders in day mode', function(assert) {
assert.expect(1);
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.on('myAction', function(val) { ... });
this.render(hbs`{{forms/date-dropdown dateFormat='MMMM YYYY' daySpecific=true dateUnitsLong=300 startIncrement=3}}`);
// Check 300 days appear
assert.equal(this.$('option').length, 300);
});
The problem I have is when the second test runs the component from the first test is still in the DOM and found by this.$('option').length. What is the correct way to clear the DOM between or at the end of tests in Ember testing?
Or is there something more specific than this.$() to use in the context of the component rendered in the test?
EDIT
Even more confusing to me is the fact that it seems to work fine at https://github.com/yapplabs/ember-radio-button/blob/master/tests/unit/components/radio-button-test.js with multiple tests and looking at the dom, but in my second test I definitely see 324 option elements in .length instead of the 300 added by that particular component.
EDIT 2
The components code is:
export default Ember.Component.extend({
dateItems: [],
didInitAttrs: function() {
var today = new Date();
var dateItems = this.get('dateItems');
var i = 0;
if (this.get('daySpecific')) {
for (i = 0; i < this.get('dateUnitsLong'); i++) {
var nextDay = new Date();
nextDay.setDate(today.getDate() + i);
dateItems.addObject({date: nextDay, value: moment(nextDay).format('DD-MM-YYYY')});
}
}
else {
for (i = 0; i < this.get('dateUnitsLong'); i++) {
var nextMonth = new Date();
nextMonth.setDate(1);
nextMonth.setMonth(today.getMonth() + i);
nextMonth.setDate(this.get('dayToUse'));
dateItems.addObject({date: nextMonth, value: moment(nextMonth).format('DD-MM-YYYY')});
}
}
},
didInsertElement: function() {
var startDate = new Date();
if (this.get('daySpecific')) {
startDate.setDate(startDate.getDate() + this.get('startIncrement'));
}
else {
startDate.setDate(1);
startDate.setMonth(startDate.getMonth() + this.get('startIncrement'));
startDate.setDate(this.get('dayToUse'));
}
this.$('select').val(moment(startDate).format('DD-MM-YYYY')).trigger('change');
},
actions: {
dateChange: function() {
this.set('value', this.$('select').val());
}
}
});
hbs
<select class="form-control" {{action 'dateChange' on='change'}}>
{{#each dateItems as |dateItem index|}}
<option value="{{dateItem.value}}">
{{date-formatter dateItem.date dateFormat}}
</option>
{{/each}}
</select>
The idea is to create a reusable component that creates a dropdown of months or days for a given period of time and allows a default of something other than the first day/month. So looking at the first test above {{forms/date-dropdown dateFormat='MMMM YYYY' daySpecific=false dayToUse=26 dateUnitsLong=24 startIncrement=1}}would create a dropdown with 24 months from this month and default to next month.
In any case I wonder whether the final line: this.$('select').val(moment(startDate).format('DD-MM-YYYY')).trigger('change'); of didInsertElement is the offender here? Perhaps the tests continue on but this stops the teardown in the test?
The two tests pass individually if I remove one or the other.
Edit 3
Removing this.$('select').val(moment(startDate).format('DD-MM-YYYY')).trigger('change'); didnt help, perhaps its my use of didInitAttrs to create my dateItems that the #each of the template uses?
I've tried with a very simple application and integration tests seem to work here.
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
moduleForComponent('x-div', 'Integration | Component | x div', {
integration: true
});
test('it renders', function(assert) {
assert.expect(2);
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.on('myAction', function(val) { ... });
this.render(hbs`{{x-div}}`);
assert.equal(this.$().text(), '', 'should be an empty string, got "'+this.$().text()+'"');
// Template block usage:
this.render(hbs`
{{#x-div}}
template block text
{{/x-div}}
`);
console.log(this.$('.thediv').length);
assert.equal(this.$().text().trim(), 'template block text');
});
test('it renders again', function(assert) {
assert.expect(2);
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.on('myAction', function(val) { ... });
this.render(hbs`{{x-div}}`);
assert.equal(this.$().text(), '');
// Template block usage:
this.render(hbs`
{{#x-div}}
template block text
{{/x-div}}
`);
console.log(this.$('.thediv').length);
assert.equal(this.$().text().trim(), 'template block text');
});
this.$('.thediv').length returns 1 in both tests, teardown is done properly.
Could the component code make tests fail by any chance?
I thought this might be a bug with didInitAttrs so posted an issue on Github (http://rwjblue.jsbin.com/docepa/edit?html,js,output). It turns out that the issue was in a single line of my code:
dateItems: [],
The array is being shared on the component's prototype. As https://dockyard.com/blog/2014/04/17/ember-object-self-troll explains:
"When you don't pass any properties to create (props), all instances of the Object will share the same prototype. That's pretty much the gist of the prototypical inheritance. It means that any changes on one object will reflect on the others. That explains the behaviour in the first example."
If I had put the component on the same page twice then I would have also seen the issue outside of the test environment.
So I had two choices to resolve this, turn dataItems into a computed property or use Init. I used init like so:
dateItems: null,
init: function() {
this._super.apply(this, arguments);
this.dateItems = [];
},
dateItems are now kept nicely distinct between component instances.

How can I port a bound date input helper to HTMLBars?

I'm trying to port a 'DateInputView' TextField extension with a bound helper to HTMLBars. It appears that 'call()' has been removed from 'Handlebars.helpers.view' so the helper doesn't work anymore. I've tried several syntax changes based on forums I've read but nothing works. The field is a datepicker which switches to the native calendar picker when using a mobile device. I see an example where the bound helper is integrated into the view helper code, so uses one js file rather than two, so am trying to go that route now. Code is below. If anyone who knows to to rework it for HTMLBars please let me know.
// ../templates/avail/navigation.hbs
{{date-input id="checkin" valueBinding="controllers.avail.arrival" class="form-control date-picker"}}
// ../views/date-input.js
var DateInputView = Ember.TextField.extend({
didInsertElement: function(){
Ember.run.scheduleOnce('afterRender', this, this._setupDateInput);
},
_setupDateInput: function(){
var _this = this;
var type = Ember.$(this.get('element')).attr('type');
// Set up Date Picker for object
if( type === "input" ) {
Ember.$(this.get('element')).datepicker({
format: "yyyy-mm-dd",
autoclose: true
});
}
}
});
export default DateInputView;
// ../helpers/date-input.js
import DateInputView from '../views/date-input';
export default Ember.Handlebars.makeBoundHelper(function(options) {
Ember.assert('You can only pass attributes to the `input` helper, not arguments', arguments.length < 2);
var hash = options.hash,
types = options.hashTypes,
inputType = hash.type,
onEvent = hash.on;
delete hash.type;
delete hash.on;
hash.type = "input";
if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) {
hash.type = "date";
}
hash.onEvent = onEvent || 'enter';
return Ember.Handlebars.helpers.view.call(this, DateInputView, options);
});
I wound up re-working this viewHelper as a Component.

Ember.beforeOberver callback is asynchronous to property change

Context: http://emberjs.jsbin.com/diyirecu/23/edit
Is there anyway to have the Ember.beforeOberver callback run first and then the property changes? Or should I be using a different strategy all together?
Not sure if you can turn that event synchronous... However, I refactored your code to use some simple jQuery patterns (callbacks... I know, very non-Ember of me) that achieve the same thing. The code is clunky compared to what you were looking to achieve, but it works.
Example JSBin: http://emberjs.jsbin.com/diyirecu/26/edit
I also changed popObject to removeObject so that it actually removed the item you clicked on.
App.IndexController = Ember.ObjectController.extend({
content: {},
colors: ['red','blue','green'],
color: 'orange',
actions: {
addColor: function () {
var color = this.get('color'),
$ul = Ember.$('ul');
if (color) {
$ul.fadeOut(500, function () {
this.get('colors').pushObject(color);
this.set('color', '');
$ul.fadeIn();
}.bind(this));
}
},
removeColor: function (color) {
var $ul = Ember.$('ul');
$ul.fadeOut(500, function () {
this.get('colors').removeObject(color); //Changed from popObject
$ul.fadeIn();
}.bind(this));
}
},
});

How do I call an action method on Controller from the outside, with the same behavior by clicking {{action}}

Please look at this code...
```
App.BooksRoute = Ember.Route.extend({
model: return function () {
return this.store.find('books');
}
});
App.BooksController = Ember.ArrayController.extend({
actions: {
updateData: function () {
console.log("updateData is called!");
var books = this.filter(function () {
return true;
});
for(var i=0; i<books.length; i++) {
//doSomething…
}
}
}
});
```
I want to call the updateData action on BooksController from the outside.
I tried this code.
App.__container__.lookup("controller:books").send('updateData');
It works actually. But, in the updateData action, the this is different from the one in which updateData was called by clicking {{action 'updateData'}} on books template.
In the case of clicking {{action 'updateData'}}, the this.filter() method in updateData action will return books models.
But, In the case of calling App.__container__.lookup("controller:books").send('updateData');, the this.filter() method in updateData action will return nothing.
How do I call the updateData action on BooksController from the outside, with the same behavior by clicking {{action 'updateData'}}.
I would appreciate knowing about it.
(I'm using Ember.js 1.0.0)
You can use either bind or jQuery.proxy. bind is provided in JS since version 1.8.5, so it's pretty safe to use unless you need to support very old browsers. http://kangax.github.io/es5-compat-table/
Either way, you're basically manually scoping the this object.
So, if you have this IndexController, and you wanted to trigger raiseAlert from outside the app.
App.IndexController = Ember.ArrayController.extend({
testValue : "fooBar!",
actions : {
raiseAlert : function(source){
alert( source + " " + this.get('testValue') );
}
}
});
With bind :
function externalAlertBind(){
var controller = App.__container__.lookup("controller:index");
var boundSend = controller.send.bind(controller);
boundSend('raiseAlert','External Bind');
}
With jQuery.proxy
function externalAlertProxy(){
var controller = App.__container__.lookup("controller:index");
var proxySend = jQuery.proxy(controller.send,controller);
proxySend('raiseAlert','External Proxy');
}
Interestingly this seems to be OK without using either bind or proxy in this JSBin.
function externalAlert(){
var controller = App.__container__.lookup("controller:index");
controller.send('raiseAlert','External');
}
Here's a JSBin showing all of these: http://jsbin.com/ucanam/1080/edit
[UPDATE] : Another JSBin that calls filter in the action : http://jsbin.com/ucanam/1082/edit
[UPDATE 2] : I got things to work by looking up "controller:booksIndex" instead of "controller:books-index".
Here's a JSBin : http://jsbin.com/ICaMimo/1/edit
And the way to see it work (since the routes are weird) : http://jsbin.com/ICaMimo/1#/index
This solved my similar issue
Read more about action boubling here: http://emberjs.com/guides/templates/actions/#toc_action-bubbling
SpeedMind.ApplicationRoute = Ember.Route.extend({
actions: {
// This makes sure that all calls to the {{action 'goBack'}}
// in the end is run by the application-controllers implementation
// using the boubling action system. (controller->route->parentroutes)
goBack: function() {
this.controllerFor('application').send('goBack');
}
},
};
SpeedMind.ApplicationController = Ember.Controller.extend({
actions: {
goBack: function(){
console.log("This is the real goBack method definition!");
}
},
});
You could just have the ember action call your method rather than handling it inside of the action itself.
App.BooksController = Ember.ArrayController.extend({
actions: {
fireUpdateData: function(){
App.BooksController.updateData();
}
},
// This is outside of the action
updateData: function () {
console.log("updateData is called!");
var books = this.filter(function () {
return true;
});
for(var i=0; i<books.length; i++) {
//doSomething…
}
}
});
Now whenever you want to call updateData(), just use
App.BooksController.updateData();
Or in the case of a handlebars file
{{action "fireUpdateData"}}

Nested #each observers in EmberJS (e.g. rows.#each.cols.#each.label)

I'm trying to build an observer to detect when changes deep in a table model occur. I can get notifications on "rows.#each.label" but not on "rows.#each.cols.#each.label".
App.Row = Ember.Object.extend({
init: function () {
this.set('cols', []);
}
});
App.Col = Ember.Object.extend({
label: null
});
var doneWithSetup = false;
var table = Ember.Object.create({
rows: [],
colLabelChanged: function () {
if (doneWithSetup) console.log("IT WORKED! I got notified when a col label changed");
}.observes("rows.#each.cols.#each.label")
});
var row = App.Row.create({});
var col1 = App.Col.create({ label: "Label 1" });
var col2 = App.Col.create({ label: "Label 2" });
row.get('cols').pushObject(col1);
row.get('cols').pushObject(col2);
table.get('rows').pushObject(row);
// OK, lets try this observer out...
doneWithSetup = true;
col1.set('label', 'some new label');
// FIXME: how do I get colLabelChanged to fire???
You can try it here:
http://jsfiddle.net/4H2ya/3/
Am I doing something wrong or are multi-level #each observers not supported?
This is a bug in EmberJS 0.9.8.1: https://github.com/emberjs/ember.js/issues/541
Nested #each is not supported according to the documentation.
Note that #each only works one level deep. You cannot use nested forms like todos.#each.owner.name or todos.#each.owner.#each.name.
http://emberjs.com/guides/object-model/computed-properties-and-aggregate-data/
Ran this code and got "IT WORKED! I got notified when a col label changed " in the console. (Using Chrome 19.)
http://jsfiddle.net/4H2ya/
So multiple #each is in fact supported, and you're doing nothing wrong. Which version of Ember are you using?