Subscribe to DOM events within Ember app - ember.js

I've got a Ember CLI based blog. I currently resize all <img> inside posts upon clicking them using this function inside the application-controller.
I rely on on('init') and Ember.run.later and this feels just dirty.
How can I improve the subscription code below?
import Ember from 'ember';
export default Ember.ArrayController.extend({
imageScaling: function() {
Ember.run.later((function() {
//Subscribe to the actual event
Ember.$(".post-content img").on("click", function(){
// RESIZE HERE
})
}), 3000)
}.on('init')
});

If you're inserting all of your images inside Ember templates, you can always use the action helper:
<img {{action 'scaleImages' on='click'}} src='' />
I realize that's probably not the case though. The way I've dealt with DOM interactions in the past is to override the application view then set up and tear down my event handler there. You can also avoid the Ember.run.later call by modifying your jQuery event listener. Here's how I would do it:
// app/views/application.js
export default Ember.View.extend({
setupImageScaling: function() {
$('body').on('click.image-scaling', 'img', function() {
// RESIZE HERE
});
}.on('didInsertElement'),
teardownImageScaling: function() {
$('body').off('click.image-sacling');
}.on('willDestroyElement')
});
In my opinion, the above is the 'most Ember' way of handling this kind of situation.

Related

Dynamic add/remove a component to the page via action

I am creating a FlashCard app and I would like to dynamically insert a component with property into the view via the action inside the route. See screenshot below,
Click "Add Card" button
Dynamically create a card-editor component in the view
I think one possible way to achieve this is to add a conditional handlebar block inside the view and render the component based on the property state; however, I wish to keep my view as clean as possible and think it could be better if I can dynamically render a component to the view only when the action is triggered.
My solution
<div style="margin-left: 200px;">
{{#if cardEditor}}
{{app/card-editor}}
{{/if}}
</div>
In view's controller
export default Ember.Controller.extend({
cardEditor: false,
actions: {
addNewCardEditor() {
this.set('cardEditor', true));
}
}
});
What I have tried
Based on the answer How to programatically add component via controller action in ember 2.x, but it does not work for me. I get an error,
ember.debug.js:41417 Uncaught Error: Cannot instantiate a component without a renderer. Please ensure that you are creating <(subclass of Ember.Component):ember604> with a proper container/registry.
Inside the view HTML,
{{app/side-bar
addNewCardPressed='addNewCardEditor'
}}
Inside the view route,
import Ember from 'ember';
import CardEditorComponent from '../../components/app/card-editor';
export default Ember.Route.extend({
actions: {
addNewCardEditor() {
CardEditorComponent.create().appendTo($('body'));
}
}
});
Inside the component JS,
actions: {
addNewCardPressed() {
this.sendAction('addNewCardPressed');
}
}
Question
So my question is how can I use the action inside the routes/home/index.js to render the component to the view.
The View HTML,
{{side-bar
addNewCardPressed='addNewCardEditor'
}}
The Index Page route,
import Ember from 'ember';
export default Ember.Route.extend({
actions: {
addNewCardEditor(newCard){}
}
});
What should I put inside the addNewCardEditor function to generate a component in the view on the fly?
Thanks for your time.
in the global.js of your EmberCLI application:
export function initialize(application) {
window.EmberApp = application; // or window.Whatever
}
Where you want to create dynamically your component, even though it might look like a hack, there might be cleaner way to do it without relying on EmberCLI variables.
"App" below is the namespace of your global EmberCLI application that you define in application.js.
var component = App.CardEditorComponent.extend({
renderer: window.EmberApp.__container__.lookup('renderer:-dom'),
}).create();
Ember.setOwner(component , window.EmberApp);
component.append();

Use an application's route's action in a template component

Firstly, I'm fairly new to Ember, so the way things interact muddles me up easily.
I am working within an existing Ember application. In the app's routes/application.js.es6 there are actions defined. Given that these actions are in the application route, can I access them from anywhere?
I am specifically looking to use one of the application's route's actions in a template/component.
In the component's js file, do I need to inject or import something to use the action from the application route?
This demo perfectly demonstrates the thing you try to achieve.
First, define action in application route:
import Ember from 'ember';
export default Ember.Route.extend({
actions: {
myActionFromApp() {
console.log('myAction from application fired');
}
}
});
Then in template pass name of action to component:
{{my-component myAction='myActionFromApp'}}
Then you can call your ApplicationRoute action from component:
import Ember from 'ember';
export default Ember.Component.extend({
actions: {
myAction() {
this.sendAction('myAction');
}
}
});
In demo I do it when someone clicks button:
<button {{action 'myAction'}}>Fire action myAction</button>
Result in console is:
myAction from application fired
When you click on button which is expected.

Run controller event after render in Ember without View

I'm new with Ember and what I want to do is execute some initialization with jQuery like this: Ember.$("select").selectpicker(); to customize default select (it actually inserts div that represents the hidden select). I used to have this code in my Controller:
init() {
this._super();
Ember.run.schedule("afterRender", this, function() {
this.send("initializeJQuery");
}
});
actions: {
initializeJQuery() {
Ember.$("select").selectpicker();
}
}
It really initializes my select tags, but when I transition to another route and go back – it rerenders and doesn't want to call initializeJQuery method despite the run method (not runOnce). I use Ember v1.13 and Views are deprecated so I'm looking for alternative way to do this.
Hope for your help.
init is only called once, because controllers are singletons. Typically these things are put in didInsertElement, preferably in a component. But to show you what's happening consider this:
App.IndexController = Ember.Controller.extend({
init: function(){
console.log("I get called once");
}
});
App.IndexView = Ember.View.extend({
didInsertElement: function(){
console.log("I get called every time Index is rendered");
}
});
JSBin: http://emberjs.jsbin.com/xacosekuku/edit?html,css,js,output
Now what you actually want to do is make a component. For instance you can do this:
App.SelectPickerComponent = Ember.Component.extend({
tagName: 'select',
didInsertElement: function(){
this.$().selectpicker();
}
});
Then in your template do:
{{#select-picker}}
<option value=1>1</option>
<option value=2>2</option>
<option value=3>3</option>
{{/select-picker}}
Something like this (but with our jQuery call added): http://emberjs.jsbin.com/xacosekuku/2/edit?html,css,js,output

Can we route from a component in ember js?

I have a component which has a button with an action like
{{action 'create'}}
and inside the action create i wrote like this.transitionTo('page.new');
But i am getting an exception like Cannot read property 'enter' of undefined can anyone answer please?Just want to know is that possible to route from a component?
The way to do that is to use this.sendAction() from your component and bubble it up to the router. The router can then call this.transitionTo().
The way link-to does it is by injecting routing _routing: inject.service('-routing'),
https://github.com/emberjs/ember.js/blob/v2.1.0/packages/ember-routing-views/lib/components/link-to.js#L530
Ember.Component is extended from Ember.View and you cant use this.transitionTo in a view. It can be done only through a controller/router.
If you want a transition inside the component on clicking, you could use the link-to helper, but if you still want to be able to handle that action, read: http://emberjs.com/guides/components/handling-user-interaction-with-actions/ and the guide after it.
I found out the answer it is possible.we can use simply use the following code from our components action
App.Router.router.transitionTo('new route');
and we will get a call back for this,in which we can set the new route's model.Use the following code for that.
App.Router.router.transitionTo('your route here').then(function(newRoute){
newRoute.currentModel.set('property','value');
});
Injection is the last thing you wanna do. The way you communicate actions between routes and components is to use the sendAction Method Send Action
template.hbs
{{your-component action="nameOfYourRouteAction" }}
route.js
export default Ember.Route.extend({
ratesService: Ember.inject.service(),
model() {
//return yourdata
},
actions: {
nameOfYourRouteAction(...args){
this.transitionTo(...args);
}
}
});
in your component.js
import Ember from 'ember';
export default Ember.Component.extend({
actions: {
toggleTransition: function(...args) {
this.sendAction('action', ...args);
}
}
});
component.hbs
<button {{action "toggleTransition" 'your route'}}>Change Route</button>

transitionToRoute('route') From Inside Component

How do I transition to a route pragmatically from inside a component action?
I tried to use #get('controller').transitionToRoute('images'), but the controller refers to the component itself. I understand that components should be self contained, so should I be using a view instead to interact with controllers/routes better?
Example
App.ImageEditorComponent = Ember.Component.extend
...
actions:
delete: ->
App.Files.removeObject(object)
#transitionToRoute('images') # This throws an exception
...
You could pass the controller in via a binding and then access it inside your component like so:
{{image-editor currentControllerBinding="controller"}}
App.ImageEditorComponent = Ember.Component.extend
...
actions:
delete: ->
App.Files.removeObject(object)
#get('currentController').transitionToRoute('images')
...
Create action on parent controller.
export default Ember.Controller.extend({
actions: {
transInController() {
this.transitionToRoute('home')
}
}
});
Specify this action on component call.
{{some-component transInComponent=(action "transInController")}}
AFTER v3.4.0 (August 27, 2018)
some-component.js
export default Component.extend({
actions: {
componentAction1() {
this.transInComponent();
}
}
});
OR simpler in some-component.hbs
<button onclick={{#transInComponent}}>GO HOME</button>
BEFORE v3.4.0
Ember.component.sendAction
"Send Action" from component up to controller
export default Ember.Component.extend({
actions: {
componentAction1() {
this.sendAction('transInComponent');
}
}
});
A component is supposed to be isolated from its context, so while you could pass in a reference to the controller, that's probably outside the scope of what a component is for. You might want to just stick with using a view with its own controller instead. Check out Views Over Components - An Ember Refactoring Story.
From Ember.js, Sending Actions from Components to Your Application, there's discussion about sending actions from a component up the route hierarchy.
A lot of things changed in Ember since the original post. So maybe today the best option would be to pass down to the component a route action that takes care of the transition (maybe using the fancy addon ember-cli-route-action.
Otherwise you can create an initializer with ember g initializer router and inside put in there a code like this one
export function initialize (application) {
application.inject('route', 'router', 'router:main')
application.inject('component', 'router', 'router:main')
}
export default {
name: 'router',
initialize
}
This way you can access the router in your component with this.get('router') and, for instance, perform a transition
this.get('router').transitionTo('images')
At component.HBS component file make a
{{#link-to "routeDestinationYouWant" }}
For Example:
<section class="component">
{{#link-to "menu.shops.category.content" "param"}}
<figure id="{{Id}}" class="list-block yellow-bg text-center" {{action "showList" "parameter"}}>
<section class="list-block-icon white-text my-icons {{WebFont}}"></section>
<figcaption class="list-block-title black-text">{{{Title}}}</figcaption>
</figure>
{{/link-to}}
</section>
I've written this answer for another similar question.
If you want to use the router only in a specific component or service or controller, you may try this:
Initialize an attribute with the private service -routing. The - because it's not a public API yet.
router: service('-routing'),
And then inside any action method or other function inside the service or component:
this.get('router').transitionTo(routeName, optionalParams);
Note: It'll be transitionToRoute in a controller.
Link to question: Ember transitionToRoute cleanly in a component without sendAction
Link to answer: https://stackoverflow.com/a/41972854/2968465