Adding component dynamically in Ember - ember.js

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}}

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();

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

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}}

In Ember, why does my template want {{model.key}}, not just {{key}}?

I am trying out Ember, and finding a discrepancy with the docs. I used the Ember CLI to ember generate template index and ember generate route index. Then I set up a trivial model in index.js:
model: function () {
return {name: "Joe"};
}
From my reading of the docs and examples, I expected to be able to access this value simply with {{name}} in my index.hbs template, but instead I only get the value with {{model.name}}. Why?
Before Ember 1.11 you could use ObjectController, that works like a proxy to corresponding route model, and you could write {{name}} for model.name.
ObjectController was deprecated in Ember 1.11, details here:
http://emberjs.com/deprecations/v1.x/#toc_objectcontroller. So in last Ember versions you should use Controller class instead ObjectController, that doesn't work as proxy of model. You could think of it as of Ember Object with model property from corresponding route. So {{name}} means property of Controller, {{model.name}} - property of model.
For example:
//route
model: function () {
return {name: "Joe"};
}
//controller
import Ember from 'ember';
export default Ember.Controller.extend({
name: 'Marry'
});
//template
{{name}} //=> Marry
{{model.name}} //=> Joe
I think this might be a thing about explicitness but I'm not 100% sure - you can also have data sent to the template on a property other than model so it might be about allowing that to be more easily understood - model is a poor property name IMO anyway
You could use the with helper if the syntax is too verbose for you:
{{#with story}}
<div class="intro">{{{intro}}}</div>
<div class="body">{{{body}}}</div>
{{/with}}

Update Ember Handlebar template when if/else condition changed

I'm using a little "helper" function in my app to get the current user/login status:
App.ApplicationController = Ember.Controller.extend({
isAuthenticated: function() {
return Docket.AuthManager.isAuthenticated()
}.property('Docket.AuthManager.apiKey'),
currentUser: function() {
return Docket.AuthManager.get('apiKey.user')
}.property('Docket.AuthManager.apiKey')
});
Now this is how my application.hbs looks:
{{#if isAuthenticated}}
foo
{{else}}
bar
{{/if}}
But even if isAuthenticated return another value, the template doesn't get it. Only solution: refreshing the page. How can I achieve that without refreshing the whole page?
if Docket.AuthManager.apiKey isn't an ember property, (which I'm pretty sure it isn't) ember won't know when it's changed causing the computed property to re-trigger and check again. Ember is only aware of properties that are get/set using its getter and setters.

How do I add classNames to the first element of the page without using a view?

I want to add a wrapper class to the first div element in my page. I used to do this with a view. So, it seems that Ember 2.0 won't support Views anymore. So how can I do that now?
view/application.js:
import Ember from 'ember';
export default Ember.View.extend({
classNames: ['wrapper'],
});
Resulting in the following page:
<body class="ember-application">
<div id="ember573" class="ember-view wrapper">
the rest of my page in this div
</div>
</body>
How is this done now that views are deprecated?
I used css to solve this problem:
body > .ember-view {
padding-left: 240px; //styles for container goes here
}
I don't have a neat solution, but subclassing Ember.Component from inside applications/view.js works.
https://ember-twiddle.com/b15411266f996191605c
Like the others said, the only way to add a class using Ember 2.0 is to use a component on your page. The component has the same properties that the view had. Your page will have a component-only call in the template, like the following:
your-page.hbs
{{your-page-component}}
If you really don't want to have a component on your page, my advice to you would be to add manually a class name in your template:
your-page.hbs
<div class="your-page">
{{outlet}}
</div>
Views are deprecated in ember 2.0. The way to do things from now on is using component and route. You can specify which class name is applied to your component by doing:
export default Ember.Component.extend({
/* Wrap your component in primary class*/
classNames: ['primary'],
/*defined class binding*/
classNameBindings: ['isUrgent'],
isUrgent: true
});
All information regarding on how to customize your component can be found in the ember documentation(click here to find out more)
You're not supposed to use Views now since they're deprecated. Use components instead, example:
import Ember from 'ember';
export default Ember.Component.extend({
classNameBindings: ['functionName'],
functionName: Ember.computed(function() {
// function logic
})
});