I have a question regarding rendering a route into a component/outlet.
I would like to have a component that sits in the application template that acts like a modal/popover.
When rendering a route, I would like to render the template into the component, and when the content changes, run some logic in the component that opens/closes the popover.
I'm hoping someone can enlighten me on how I would go about doing this?
Currently I have a component in the application template, that has a named outlet inside of it (see below). I can render the route into that named outlet, but I can't figure out how to pick up the changes in the content when it changes. Clicking different links will change the content of that outlet.
Any thoughts? A better way to accomplish what I'm wanting to do?
app/templates/application.hbs
{{#primary-popover}}
{{outlet 'primary-popover'}}
{{/primary-popover}}
/app/profile/route.js
import Ember from 'ember';
export default Ember.Route.extend({
renderTemplate() {
this.render('profile', {
outlet: 'primary-popover'
});
}
});
I think the best way to solve your problem is to use liquid-fire http://ef4.github.io/liquid-fire/#/modals . It's recommended by ember core team library and also is well supported. It has nice modal component which can be triggered with link-to component or via this.transitionTo programmatically.
Related
I am really new to Ember, which I am asked to do and well, love to learn. Basically, the current project uses Gentelella Admin Dashboard. I being trying to get the dashboard to load properly but failed.
After I login, I get redirected to /dashboard/ route, which basically loads the main dashboard interface. Now the problem is I can't click-expand the menus on the sidebar nor toggle the sidebar menu. And the main page is not extended to fill the space, as in our current application.
I know the function init_start() takes care of the resize and the click, which is already added to vendor.js from custom.js but I can't seem to call the function from ember at all.
My latest attempt was using mixins but it failed too:
import Ember from 'ember';
export default Ember.Mixin.create({
activate: function() {
this._super();
init_sidebar();
}
});
then from dashboard.js route:
import HandleTempLoadMixin from '../mixins/handle-temp-load';
export default Route.extend(AuthenticatedRouteMixin,HandleTempLoadMixin, {
});
but still the function is not executed.
I have read that it is best to avoid working with jquery inside ember in SO but I have pretty much many JQuery functions that I cant transfer right now (nor sure why exactly since it says somewhere in the documentation jquery is built into ember itself).
Anyway, what is the best way to initailize the dashboard interface?
From my understanding you have some jQuery stuff that you would like to utilise. I suggest looking into Component's didInsertElement hook and triggering your custom code from there.
You can find more details in here https://guides.emberjs.com/v2.17.0/components/the-component-lifecycle/#toc_integrating-with-third-party-libraries-with-code-didinsertelement-code
In general, try avoid working with view related stuff in Routes. Ember's power comes from strong conventions. Learning where to place your code is crucial.
I have an Ember 2.11 application template with a few component placeholders at the moment (menu, breadcrumbs, related items) and an outlet which displays the main content which works fine. Now that I'm feeling more comfortable with the basics, I'm ready to try getting the breadcrumbs working.
I read about services, but I don't see that it is the right solution for breadcrumbs because it doesn't need to be persistent, it is based off the route. Although it is based off the route, I don't want to use the route literally as I want to use nice titles, and when viewing a specific item, the route doesn't accurately reflect what the breadcrumbs should show.
Since the breadcrumbs is based off the model that is being used for the display, I feel that I should be able to construct a breadcrumb object and then pass that into the component from the application template. I suppose this was the purpose of the controller back in the day. My thought was in the route to construct a breadcrumb object/property and return it with the model like RSVP and then I could access both in the template to pass the appropriate object to the appropriate component. But that seems wrong as the route should return an Ember data object, promise or Javascript array.
My current line of thinking is along these lines.
template/application.hbs
{{bread-crumbs crumbs=model.breadcrumbs}}
{{outlet}}
route/category/show
export default Ember.Route.extend({
model(params) {
let recipe = this.get('store').query('recipe', { category: params.category_id});
let crumbs = [{name: 'Category', link: 'category'},
{name: recipe.category.title, link: 'category.show', target: recipe.category.id}];
return {recipe: recipe, breadcrumbs: crumbs};
}
});
I'm not sure if this is the right way to approach this or if this will cause problems with async data fetching with Ember data. Something like this I would have to define on each route, but I don't have a lot of routes and seems to offer flexibility when I'm displaying a recipe (route is /recipe/recipe_id), but have the breadcrumbs show Home > Categories > Main Dishes > My Awesome Dish.
How would you approach the problem?
Updated 2017-02-10:
It appears that the model is not passed to the application template, only the route template. I'm not sure how to pass data 'down' to the application template.
You could probably create a breadcrumb like this by tracking the elements in a service, but I'd check out the ember-crumbly addon. It seems like it will meet your needs.
Remaining in your thinking line, if you want to pass your model as a variable of your controller in the route that you are accessing you need something like this:
export default Ember.Route.extend({
model(params){
let recipe = ...;
let crumbs = ...;
return {...};
},
setupController(controller, model){
this._super(controller, model);
controller.set('variable', model);
}
});
Having this, in your controller you can access to the crumbs like this:
this.get('variable');
And in then with an Ember.computed you can create a variable for display in your template.
Sorry for the minimize your code but I'm not in my computer.
Another approach is setting the crumbs variable at the init of the application controller, this set the variables defined in that controller global to the application, so you can modify them from other controllers and in the application controller lookup for that changes via didUpdateAttrs() or with didUpadteElement().
Hope this resolve your problem.
Greetings
In my Ember template in my application, I have got a header tag, which should print the title of the current route. Does anyone know how I would accomplish this in EmberJS? Is there any way to set a variable once a route is accessed?
Route
Ember.Route.extend({
setupController: function(controller) {
controller.set('routeName', this.routeName);
}
});
Controller
Ember.Controller.extend({
routeName: null
});
Template
<header>{{routeName}}</header>
If you about application template, there is currentPath property in Application Controller (and in some other objects too, see details here: https://github.com/emberjs/ember.js/pull/12034
//application template
<header>{{currentPath}}</header>
There are a few ways.
First, an application controller has a property, currentPath. In this and this answers you may see examples of using it.
Also, you may use didTransition to determine when ember made a transition and update a title in some way.
And finally, if you want to change a document title (displayed in browser tab), there is an addon
I have a a basic ember cli app with a component, and I wanted to specify different layoutName to the component based on a property passed to it. I am not sure how to achieve this. Right now I just want to know how to specify a templateName to a known template to get started. This is what I tried:
import Ember from 'ember';
export default Ember.Component.extend({
layoutName: null,
initialize: function() {
this.set('layoutName', 'pi/pods/components/questions/single-select/standard-layout');
}.on('init')
});
and my folder structure looks like:
app
|-pods
|-components
|-questions
|-single-select
|-component.js
|-template.hbs//just has {{yield}}
|-standard-layout.hbs//says hello
JUST A THOUGHT : This may be a new question itself - before we used jsbin to collaborate but now how can we achieve same when we are building stuff with ember-cli!!
SOLUTION :
as per #sunrize920 suggestion, I had to break out of POD structure a bit. So the working solution looks like this:
import Ember from 'ember';
export default Ember.Component.extend({
layoutName: null,
initialize: function() {
this.set('layoutName', 'components/questions/single-select/standard-layout');
}.on('init')
});
And then my folder structure looks like this:
dev-folder
|-app
|-pods
|-components
|-questions
|-single-select
|-component.js
|-standard-layout
|-template.hbs
So component's and layout don't exactly work out of the box like you are expecting. Lets say we have component called my-component. Ember interprets the template you define at templates/components/my-component as the layout and anything you wrap the component around via block syntax: {{#my-compenent}}{{/my-component}}as the component's template. Look at this JSBin to see what I mean.
What I am talking about isn't using pod syntax, but I would imagine that if you did not define the template.hbs or whatever the convention is that ember-cli uses to resolve the components template, you should be able to pass a layoutName and templateName to your component. Hope that works
As of Ember 2.3.0 (and perhaps before), it is not possible to set layout or layoutName in the component's init or in an on('init') handler to override the template. If there is a template, it will be used regardless of the layout specified in the component.js.
If you have the option to extend the component and omit the template, then the above technique will work. If instead you need to reopen and existing component, as of now you appear to be out-of-luck. Hopefully this will change in the future.
I am building an admin dashboard using ember. I want to create a reusable chart object of which I can have multiple instances throughout the application. The chart object should have a template consisting of some markup and a canvas element of which I need the id after insertion in the DOM in order to attach the actual chart (chart.js). I have tried several approaches, but I can not seem to figure out the right architecture to do this.
What would be the right architecture in ember to achieve the above?
Thanks!
Ember.Component is your friend
As #raulbrito already mentioned, the best way to go if you want reusable components in ember is indeed to use the new Ember.Component which is heavily based on the new w3 draft for web components and thus beeing future proof.
I've tried to make a simple example on how this could be implemented.
Given a simple route where the model hook returns some static data:
Index Route
App.IndexRoute = Ember.Route.extend({
model: function(){
return Ember.Object.create({
modelOne: data,
modelTwo: data2
});
}
});
data and data2 are simply static objects globally defined for simplicity (as you will see in the demo), but this could be also data coming from a backend or from fixtures etc.
Index template
In the template then we insert our chart component with the line {{line-chart data=model.modelOne}} and as you can see, we also set the data attribute to the index model model.modelOne or model.modelTwo:
<script type="text/x-handlebars" id="index">
<h2>Chart one</h2>
{{line-chart data=model.modelOne}}
<h2>Chart two</h2>
{{line-chart data=model.modelTwo}}
</script>
Component Template
Our component template looks fairly simple because it will render a simple canvas element, but it could be as complex as needed, on how to use Ember.Component please refer also to the docs:
<script type="text/x-handlebars" id="components/line-chart">
</script>
Component Subclass
App.LineChartComponent = Ember.Component.extend({
tagName: 'canvas',
attributeBindings: ['width', 'height'],
width: '480',
height: '360',
data: null,
didInsertElement: function() {
var ctx = this.get('element').getContext("2d");
var myNewChart = new Chart(ctx).Line(this.get('data'));
}
});
Note the naming is important here, Ember knows which subclass powers a component based on its name. For example, if you have a component called line-chart, you would create a subclass called App.LineChartComponent. If your component was called bar-chart-simple, the class name would be App.BarChartSimpleComponent and so on. Ember will look for a class with the camelized name of the component, followed by Component.
So, and since Ember.Component extends from Ember.View we can define all sorts of properties Ember.View supports like tagName. In our case we use canvas because this is what chart.js needs to work. As you can see we have also defined some attributeBindings to control the width and height of the canvas from inside ember. The component has also a data attribute (which could be called whatever you find appropriate) defined on which we later set our model data in the template returned from the IndexRoute model hook. And finally in your didInsertElement hook of our component we initialize the chart passing with this.get('data') the data object to new created Chart.js class.
var ctx = this.get('element').getContext("2d");
var myNewChart = new Chart(ctx).Line(this.get('data'));
And last but not least, please see here for a working example of the above explained.
Hope it helps.
Update in response to your last comment
I've tried to simulate a delay in the resolution of the model hook to mimic a response from a backend, as you can see the template rendering is waiting for the model promise to resolve first. Basically what I've done is to use Ember.run.later with a delay of 2000ms that resolves the promise once timed out:
App.IndexRoute = Ember.Route.extend({
model: function(){
return new Ember.RSVP.Promise(function(resolve) {
Ember.run.later(function() {
var m = Ember.Object.create({
modelOne: data,
modelTwo: data2
});
resolve(m);
}, 2000);
});
}
});
And just for fun I've also added a LoadingRoute to show a spinner while the promise resolution is waiting for data, the LoadingRoute is a less documented feature of ember, you can read more about it here: https://gist.github.com/machty/5647589 under How do I put up a (global) Loading Spinner during a transition w/ Promises?
Plase see here for a updated example: http://jsbin.com/odosoy/145/edit
Update in response to #SamSelikoff's comment
As for the above mentioned LoadingRoute #SamSelikoff pointed out that it's officially documented now: http://emberjs.com/guides/routing/defining-your-routes/#toc_initial-routes
I have some thoughts on this, so just throwing it out there, in case it helps you.
First of all, I would advise you to go and watch Sam Selikoff's presentation on using Ember with D3. All the info here: http://www.samselikoff.com/blog/2013/08/09/ember-d3-simple-dashboards/ . Also, don't miss the comments section on the blog post.
It is a great example on using Ember Views to wrap D3 objects, and can be a good reusable solution. The caveat here is that Ember Views require a backing controller that provides the data. Depending on where in the application you would want to reuse your charts, this might be inconvenience.
The alternative would be to use Ember Components. In that case, you just need to define the Component and associated handlebars template. The good thing about it is that it won't need any backing controller, therefore freeing you from a dependency, which might make it easier for you to add such a component in different places of your application. Without a concrete example, I think it's hard to reach a great conclusion, but maybe this will help you clarify things.