How to programatically add component via controller action in ember 2.x - ember.js

I am being faced with the same problem on
How to programatically add component via controller action
However since I am using ember cli, I am unable to do so.
Here is my source code
import Ember from "ember";
export default Ember.Component.extend({
actions : {
remove : function(){
this.remove();
},
add : function()
{
Ember.AuthorNameComponent.create().appendTo($('#authors'));
}
},
});
When I try to run this code, I get undefined error. Also name of component is author-name.
Any help, how can I create component via programmatically?

You need to import the component, then you don't need the Ember Global.
import AuthorNameComponent from '../components/author-name-component'
Another way is to have an array of items and base the list of AuthorNameComponent from that.
{{#each items as |item|}}
{{author-name name=item.name}}
{{/each}}

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.

Adding component dynamically in Ember

using
Ember : 1.13.11,
Ember Data : 1.13.8,
ember-cli : 1.13.12
I want to add a component dynamically to webpage - this webpage is template of another component don't think that it will make any difference-. Here is my code snippet in which I try to add a component named LyricsEditorLine to <div> tag, somehow like this
agenda-alpha/components/lyrics-editor.js
import Ember from 'ember';
import LyricsEditorLine from 'agenda-alpha/components/lyrics-editor-line';
export default Ember.Component.extend({
afterRenderEvent:function(){
LyricsEditorLine.create().appendTo($("#in"));
},
init:function(){
Ember.run.scheduleOnce('afterRender', this, this.afterRenderEvent);
this._super();
}
});
agenda-alpha/templates/components/lyrics-editor.hbs
<div id='in'> </div>
every time this gives me
'Uncaught Error: Assertion Failed: You cannot append to an existing Ember.View. Consider using Ember.ContainerView instead'
Looked for ContainerViewhere found that it is deprecated
Most of the answers that I found are not using ember-cli and being a beginner makes it harder to understand
I want to be able to add components as much as the user needs
I think you probably want the {{component}} helper which allows to dynamically render a component.
{{component "componentName" param1=paramValue param2=anotherParamValue}}
Which means you can have (made up example)
{{component "lyrics-editor-line" line=line}}
One the best things is that componentName can be a bound property
{{component componentName line=line}}
And in your controller/component:
componentName: Ember.computed('prop1','prop2', function() {
if (this.get('prop1') === 'A') {
return 'my-component-a';
}
return 'default-component';
}),
line: computed('prop3', function() {
return this.get('prop2');
})
Also, you can have the component helper inside an each loop (example taken from the Ember documentation)
{{#each model as |post|}}
{{!-- either foo-component or bar-component --}}
{{component post.componentName post=post}}
{{/each}}

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