EmberJs: wrapping Bootstrap components (timepicker) - ember.js

I'm already using the ember-bootstrap plugin (great job !!)
But I need to add a time picker (https://github.com/jdewit/bootstrap-timepicker) to this Bootstrap plugin.
I'm not an EmberJs expert yet...
Can someone provide me some help to wrap this time picker with ember-bootstrap ?
Update:
I have found this solution without ember-bootstrap plugin and it works.
Is it the right way to process ?
Bootstrap.TimePicker = Em.ContainerView.create({
classNames: 'input-append bootstrap-timepicker-component',
childViews: ['inputView', 'iconView'],
inputView: Em.TextField.create({
type: 'text',
valueBinding: 'App.PassController.storeCard.relevantDate',
classNames: 'timepicker-default input-small'
}),
iconView: Em.View.create({
tagName: 'span',
classNames: 'add-on',
template: Ember.Handlebars.compile('<i class="icon-time"></i>')
}),
didInsertElement: function() {
$('.timepicker-default').timepicker({showSeconds: true, defaultTime: 'value'});
}
}).append();

Related

Ember 2.0 - views for pages

I am used to put some jquery code in View files, that I create for pages.
For instance:
I have the route this.route('buildings');
Next create the view file app/views/buildings.js
import Ember from "ember";
export default Ember.View.extend(Ember.TargetActionSupport, {
didInsertElement: function () {
//jquery here
}
});
But now with Ember 2.0 we have no such ability, what should I do?
In Ember 2.0.0 you can still do the following:
App.ApplicationView = Ember.Component.extend({
classNames: ['customClassName'],
didInsertElement: function() {
alert('did insert element')
}
});
App.BuildingsView = Ember.Component.extend({
classNames: ['customClassName2'],
didInsertElement: function() {
alert('did insert element2');
}
});
See this jsbin for a working example.
P.S. Comment by Robert Jackson: "Using a component as ApplicationView will allow customization of classNames and whatnot, but is definitely going to have a number of negative results as well (for example controller is not correct)." https://github.com/emberjs/ember.js/issues/11486#issuecomment-131366332
Put it in a component. In components/my-component.js
import Ember from 'ember';
export default Ember.Component.extend({
didInsertElement: function(){
// jquery here.
}
});
In your template:
{{my-component}}
Or:
{{#my-component}}
Stuff
{{/my-component}}

Trigger an action on the change event with Ember.js checkbox input helper?

How can I fire a named action upon changing a checkbox in Ember.js? Any help will be greatly appreciated.
Here is what I have. Checking or unchecking the checkbox has no effect.
Template:
{{input type="checkbox" on="change" action="applyFilter"}}
Controller:
actions: {
applyFilter: function() {
console.log("applyFilter");
}
}
I'd like to post an update to this. In Ember 1.13.3+, you can use the following:
<input type="checkbox"
checked={{isChecked}}
onclick={{action "foo" value="target.checked"}} />
link to source
using an observer seems like the easiest way to watch a checkbox changing
Template
{{input type='checkbox' checked=foo}}
Code
foo:undefined,
watchFoo: function(){
console.log('foo changed');
}.observes('foo')
Example
http://emberjs.jsbin.com/kiyevomo/1/edit
Or you could create your own implementation of the checkbox that sends an action
Custom Checkbox
App.CoolCheck = Ember.Checkbox.extend({
hookup: function(){
var action = this.get('action');
if(action){
this.on('change', this, this.sendHookup);
}
}.on('init'),
sendHookup: function(ev){
var action = this.get('action'),
controller = this.get('controller');
controller.send(action, this.$().prop('checked'));
},
cleanup: function(){
this.off('change', this, this.sendHookup);
}.on('willDestroyElement')
});
Custom View
{{view App.CoolCheck action='cow' checked=foo}}
Example
http://emberjs.jsbin.com/kiyevomo/6/edit
Post Ember version >= 1.13 see Kori John Roys' answer.
This is for Ember versions before 1.13
This is a bug in ember's {{input type=checkbox}} helper.
see https://github.com/emberjs/ember.js/issues/5433
I like the idea of having a stand-in. #Kingpin2k's solution works, but accessing views globally is deprecated and using observers isn't great.
In the linked github ember issue, rwjblue suggests a component version:
App.BetterCheckboxComponent = Ember.Component.extend({
attributeBindings: ['type', 'value', 'checked', 'disabled'],
tagName: 'input',
type: 'checkbox',
checked: false,
disabled: false,
_updateElementValue: function() {
this.set('checked', this.$().prop('checked'));
}.on('didInsertElement'),
change: function(event){
this._updateElementValue();
this.sendAction('action', this.get('value'), this.get('checked'));
},
});
Example usage in a template ('checked' and 'disabled' are optional):
{{better-checkbox name=model.name checked=model.checked value=model.value disabled=model.disabled}}
For those using Ember > 2.x:
{{input
change=(action 'doSomething')
type='checkbox'}}
and the action:
actions: {
doSomething() {
console.warn('Done it!');
}
}
In Ember v1.13 it can be done simply by creating a component named j-check in my occasion(no layout content required):
import Ember from 'ember';
export default Ember.Checkbox.extend({
change(){
this._super(...arguments);
this.get('controller').send(this.get('action'));
}
});
And then you just call it from your template like this:
{{j-check checked=isOnline action="refreshModel" }}

Ember.js persist classNameBindings on transition to different routes

I'm fairly new to ember and I've been trying to tackle this problem for a couple of days but I can't seem to find a solution anywhere online.
I have a page with a list of all posts, each post has one tag (like a hashtag), either 'Fitness', 'Knowledge' or 'Social'. At the top of the page I have 3 view helpers and each view helper represents a tag (fitness, knowledge or social). These will be used to filter out the posts with that particular tag name.
My problem is that when I click on a view helper I toggle the "isSelected" property to true, which adds the "isSelected" class via classNameBindings. But when I transition to a different route on the site and come back, the "isSelected" property is reset back to false and the "isSelected" class has been removed. How do I keep these values persistent and in-tact for when I revisit the route?
Here's my code:
<script type="text/x-handlebars" data-template-name="global">
<ul class="categories">
<li>{{view App.Tag class="label fitness" text="fitness"}}</li>
<li>{{view App.Tag class="label knowledge" text="knowledge"}}</li>
<li>{{view App.Tag class="label social" text="social"}}</li>
</ul>
</script>
View:
"use strict";
App.Tag = Ember.View.extend({
tagName: 'span',
template: Ember.Handlebars.compile('{{view.text}}'),
classNames: ['label'],
classNameBindings: ['isSelected'],
isSelected: false,
click: function () {
this.toggleProperty('isSelected');
}
});
I have also tried using a controller with actions but that way persisted the "isSelected" property but didn't preserve the addition of the class when I revisited the route.
This may not be ideal, but to save the state of the application, you can put the state in the controller. You probably had a simple implementation, but maybe did not specify the isSelected as a property. The below works and you can view the jsbin here
App = Ember.Application.create();
App.Router.map(function() {
this.route('global');
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return ['red', 'yellow', 'blue'];
}
});
App.GlobalController = Ember.Controller.extend({
activeTags: Ember.A()
})
App.Tag = Ember.View.extend({
tagName: 'span',
template: Ember.Handlebars.compile('{{view.text}}'),
classNames: ['label'],
classNameBindings: ['isSelected'],
isSelected: function () {
console.log("ON CHANGE", this.get('controller.activeTags'));
return this.get('controller.activeTags').contains(this.text);
}.property('controller.activeTags.#each'),
click: function () {
var tagArray = this.get('controller.activeTags');
if (tagArray.contains(this.text))
this.set('controller.activeTags', tagArray.without(this.text))
else
tagArray.pushObject(this.text);
}
});

Sibling attribute binding in Ember.js

I am a relative Ember newbie, so I may be missing something obvious here. I'm trying to build a custom view for a Bootstrap accordion. I'm using relative views for this, and while it is working, I'd like to understand if I can simplify a little bit. Specifically, I'd like to know if it's possible to make the href computed property of the embedded toggleView dependent on the elementId of the embedded bodyView. Here's the top level view, which is a collection:
App.AccordionView = Em.CollectionView.extend({
headingTemplateName: 'accordion-heading',
innerTemplateName: 'accordion-inner',
tagName: 'div',
classNames: ['accordion'],
contentBinding: 'controller.content',
The item view corresponds to the DOM element with the accordion-group css class.
itemViewClass: Em.View.extend({
tagName: 'div',
classNames: ['accordion-group'],
target: null,
template: Em.Handlebars.compile("{{view view.headingView contentBinding='view.content'}}{{view view.bodyView viewName='bodyViewInstance' contentBinding='view.content'}}"),
headingView: Em.View.extend({
tagName: 'div',
template: Em.Handlebars.compile("{{view view.toggleView contentBinding='view.content'}}"),
classNames: ['accordion-heading'],
toggleView: Em.View.extend({
tagName: 'div', // Can be an 'a'
templateNameBinding: 'parentView.parentView.parentView.headingTemplateName',
classNames: ['accordion-toggle'],
attributeBindings: ['dataToggle:data-toggle', 'dataParent:data-parent', 'href'],
dataToggle: 'collapse',
dataParent: function() {
return this.get('parentView.parentView.parentView.elementId');
}.property(),
Note that I've made the href property dependent on the target field in the item view.
href: function() {
return "#" + this.get('parentView.parentView.target');
}.property('parentView.parentView.target')
})
}),
bodyView: Em.View.extend({
tagName: 'div',
template: Em.Handlebars.compile("{{view view.innerView contentBinding='view.content'}}"),
classNames: ['accordion-body', 'collapse'],
This field is set when the bodyView embedded view instance is inserted into the DOM.
didInsertElement: function() {
this.get('parentView').set('target', this.get('elementId'));
},
innerView: Em.View.extend({
tagName: 'div',
classNames: ['accordion-inner'],
templateNameBinding: "parentView.parentView.parentView.innerTemplateName"
})
})
})
});
That all works. However, I'd like to do something like this in the toggleView:
href: function() {
return "#" + this.get('parentView.parentView.bodyViewInstance.elementId');
}.property('parentView.parentView.bodyViewInstance.elementId')
When I do, I get the following error, and I'm not sure I understand why:
Error: Something you did caused a view to re-render after it rendered but before it was inserted into the DOM.
Is there any way to accomplish what I'm looking to do?

Ember template displayed twice

There is a simple ember.js app with one view displayed in a particular place on the webpage. Have a look at this jsfiddle: http://jsfiddle.net/jkkK3/9/
App = Ember.Application.create({
ready: function(){
this._super();
this.ApplicationView.create().appendTo(".content");
},
ApplicationController: Ember.Controller.extend({
}),
ApplicationView: Ember.View.extend({
templateName: 'application'
}),
Router: Ember.Router.extend({
root: Ember.Route.extend({
})
})
});
My question is: Why is the "some content here" element displayed twice? It works when I remove the router, but that's exactly what I cannot do, since I try to add Router to my Ember app. Could you please help me to display application view only once, inside the red box?
When using router, applicationController/view are used by default. In your ready method you append it explicitly. So 'application' template is appended twice. Remove appending it in ready method and it will be appended only once.
By default it's appended to body but if you want to override use rootElement property of Ember.Application
Ember.Application.create( {
rootElement : '.content',
....
})