Edit: found the answer here: Using Ember.js, how do I run some JS after a view is rendered?
In the mapView
didInsertElement: function() {
this._super();
map_initialize();
},
*********** original post ****************
I'm pretty new to js and am trying ember.js.
I am trying to use the google maps api to show a map. Normally you should initialize the map with
<body onload="map_initialize()">
but as the map div that will receive the map is part of an ember view template it is not yet in the DOM when the function is called.
console.log(document.getElementById("map_canvas"));
returns false.
I then placed the initialization function in my map view so that could call it from an event tied to the map div itself. This way it is impossible to call map_initialize() before the div is loaded.
<script type="text/x-handlebars" data-template-name="map" >
<div id="map_canvas" style="width:100%; height:100%" {{action "map_initialize" on="click"}}></div>
</script>
this works perfectly. When the page loads, I can click on the map div and then the map is loaded.
My question is, how can I get this event to be called automatically after the div is added to the DOM
<div id="map_canvas" style="width:100%; height:100%" onload="initialize()"></div>
has no effect (the map_initialize() function is not called).
I have also tried to do this via ember with
<div id="map_canvas" style="width:100%; height:100%" {{action "map_initialize" on="load"}}></div>
but this also does nothing.
What is the correct event name?
Thanks
Override didInsertElement() on the view (and probably call this._super() inside, calling the function you just shadowed).
You can also listen to changes to the DOM, such as DOMNodeInserted or DOMSubtreeModified, but that would include any change
Related
As in title, is it possible to add something like
classNames
in Component but inside Route or Controller object?
Edited
In application template I add few elements
<header></header>
<div class="content"></div>
<footer></footer>
And as result on page I have:
<div id="ember1122" class="ember-view">
<header></header>
<div class="content"></div>
<footer></footer>
</div>
And I'd like to have
<div id="ember1122" class="ember-view customClassName">
At present, you can't do what you want in a straight forward way.
1. But you can wrap application.hbs content with root div.
Define rootElement in index.html
<div id="my-app" class="customClassName">
{{content-for "body"}}
</div>
inside app.js file, you can change rootElement
import Ember from 'ember';
export default Ember.Application.extend({
rootElement: '#my-app'
});
2.May be you can even try applying styles to application.hbs div using css like
#my-app > .ember-view { // customClassName style goes here }
I don't think this is possible. Altough documentation states that "controller is a specialized component", in fact they are not at all similar.
Component is a subclass of Ember.View. Every time a Component is rendered, there's created a new instance that is hooked to specific part of DOM, has hooks related to rendering, and is aware of DOM elements that it contains.
Controller is an abstract singleton, subclass of Ember.Object. It has functions, properties and actions hooked to rendered template, but doesn't know itself anything about DOM it's attached to.
I have the following template setup displaying a list of items, and wrapping each list in its own controller:
{{#each controller itemController="contact"}}
<div class="contact-entry">
<div class="title" {{action toggle on="click"}}>{{title}}</div>
{{#if showDetails}}
<div class="details">
<div>{{safe details}}</div>
</div>
{{/if}}
</div>
{{/each}}
The main controller is an ArrayController called ContentsController, and each item is wrapped in an ObjectController called ContentController. The content of the ContentsController is set explicitly in the route (I've simplified the data):
App.ContactsRoute = Em.Route.extend({
model: function() {
return [{'title': 'first'}, {'title': 'second'}];
}
});
This works really well. However if I navigate to another path and then back again, the settings on the ContentController don't persist. What happens is that the model data gets reloaded, and I assume a ObjectController gets created for each of the list items. Incidentally this is not the case for the ContentsController, which keeps its settings.
What is the solution to preventing ember of creating new ContentController for every list item every time I access the page?
I'm assuming your reference to ContentController is really ContactsController since you are using itemController="contact" in your #each block.
What kind of data are you trying to persist? The showDetails flag? The ContactControllers are going be created and destroyed anytime you exit / enter the route and there isn't anyway I know of to keep those around.
The ContactsController keeps its properties because its a singleton controller generated because you have a ContactsRoute.
I'm building a popover/dropdown with Ember which essentially boils down to:
<div class="popover">
<span {{action showPopover}}>Click</span>
{{#if popoverShowing}}
<div class="popover-body">The popover body</div>
{{/if}}
</div>
All works fine but I have other elements on the page which are absolutely positioned and due to them forming a new stacking context there's no way I can make the popover be displayed above them.
If this were plain old Javascript, I'd append the popover to the body much like how Bootstrap does with the container option but we don't have that level of control in Ember AFAICT.
The only solution I can think of is to use an {{outlet}} in the application template and render to that, but that means for every popover/dropdown I have to split the contents out to a different view/template/controller and have an action in the router which seems rather over-complicated!
Can anyone think of a better option?
One approach that seems to work is to detach the body element on didInsertElement and then manually destroy on willDestroyElement:
didInsertElement: function() {
Ember.$("body").append(this.$())
},
willDestroyElement: function() {
this.$().remove()
}
This appears to work fine, but there are probably bugs lurking!
Is the popup library toastr not going to work with Ember because of direct dom manipulation that ember doesn't like?
Are there any other libraries like this one that work nicely with ember?
Edit
Even through the working example posted below I could not get this to work locally. I finally used Pine Notify which worked straight away.
This works fine in Ember, you just have to handle the event in the right place. The "right place" depends on your implementation. If you want this to be fired from a button within your view, you'll need to use the {{action}} helper passing the action name. Example:
<script type="text/x-handlebars" >
<button class="btn btn-info" {{action showInfo}}>Info</button>
</script>
In the template above, I'm saying that the button should fire the showInfo event, so the Controller responsible for this view should have a function with the same name:
App.ApplicationController = Em.ArrayController.extend({
showInfo: function() {
toastr.info('This is some sample information');
}
});
You can also have the view handle the event; the code below defines a click event, so if you click anywhere in the view, it would run your function:
App.OtherView = Em.View.extend({
click: function(e) {
toastr.error('This is some sample error');
}
});
and in your Handlebars template, you don't have do tell the action since you are already saying in the view class that you want to handle the click event for that view, so you can simple render the view and style it:
{{#view App.OtherView class="btn btn-danger"}}
Error
{{/view}}
Here's a sample in JSFiddle: http://jsfiddle.net/schawaska/YZwDh/
I recommend that you read the Ember Guide about the {{action}} helper
/START description of why
I'm doing a 'load more' type of interaction: user gets at bottom of a page, I load more content.
As I'm using a plugin that formats content in a 'pinterest-style' (masonry), I need to take the output of a rest call, formatted using an ember view, and i'm doing something like:
<div id="list">
</div>
<div id="hidden" style="display:none">
{{#each item in App.itemsController}}
test {{item.id}}
<br />
{{/each}}
</div>
What I want to do is, load the content in the hidden div, and then pass its HTML generated content to the plugin to process in my formatted list view.
If I just copy the #hidden content, the Ember scripts get in, and on subsequent 'load more', content is inserted in the #list, in addition of going in the #hidden div.
That's because I copied the entire handlebars.
So I get rid of the container tag, the one I supposed was wrapping the controller content binding, but even when stripping it from the content I pass to #list, the #list still auto-updates when a 'load more' is called.
I know that's a dirty hackish thing, but in order to improve performance in the client I must take a similar route.
/END description of why
//ACTUAL QUESTION ;)
Given this background, the question is, stripping the container metamorph script tags (like the ones here below), and just take the content inside them, shouldn't get rid of the auto-updating content functionality?
<script id="metamorph-X-start" type="text/x-placeholder"></script>
//ALL THE CONTENT
<script id="metamorph-X-end" type="text/x-placeholder"></script>
Inside those, I just have the actual content generated, like:
<script id="metamorph-9-start" type="text/x-placeholder"></script>
test <script id="metamorph-59-start" type="text/x-placeholder"></script>2873<script id="metamorph-59-end" type="text/x-placeholder"></script>
<br>
<script id="metamorph-9-end" type="text/x-placeholder"></script>
<script id="metamorph-10-start" type="text/x-placeholder"></script>
test <script id="metamorph-60-start" type="text/x-placeholder"></script>2872<script id="metamorph-60-end" type="text/x-placeholder"></script>
<br>
<script id="metamorph-10-end" type="text/x-placeholder"></script>
The alternative is programmatically render the template inside a variable and process that, which is surely a better way of doing this, but I just wonder how the #each binding works internally as I thought the metamorph was doing that.
Have you looked into using a CollectionView and calling the plugin through the didInsertElement callback?
e.g.
MyList = Ember.CollectionView.extend({
itemViewClass: 'App.ListItem',
didInsertElement: function(){
view.$().jqueryPlugin({ ... })
}
})