Missing views in container after transition to another route - ember.js

In the OfferRoute I have a container and a controller's method to add views dinamically to it:
addProduct: function() {
var container = Ember.View.views['containerView'];
var child = container.createChildView(Gmcontrolpanel.InsertProductView);
container.pushObject(child);
}
Everything works fine but if I go to another page on the application and then I come back to the offer page, the child views in the container are missing;
I can see that the HTML output is the empty container:
<div id="containerView" class="ember-view"></div>
Does someone know why is this happening?

Views redraws when you switch pages. So, you get a new containerView instance with empty children list every time.
I'm not recommending to use Em.View.views object for that task. If you need to store state of data between redrawing, it's more likely controller job, and containerView needs to bind to this controller.product list and display each product.
It's possible that {{#each}} helper or custom CollectionView is what will help you to solve the task more elegant.

Related

What is the best way for all instances of an Ember component to share a variable?

I have an Ember component which is essentially a panel. There can be multiple instances of this panel on a page, but only one can be "active" at any given time. Each instance of the component must be aware if any of the other panels become "active" so they can remove their "active" state. I would really rather not move the JavaScript to make this happen to a parent component. Instead, I would like to keep it within this component. In Angular, I used to use a static variable to do this. What is best way to do this in Ember?
I would really rather not move the JavaScript to make this happen to a
parent component
Do you want to avoid having the parent component dealing with anything related to panel "activity"? If so, why?* If not:
Ember automatically gives each component's tag (unless it's a tagless component) an id that is accessible from the js code as elementId. You could create a property activePanelId on the parent component and pass it to all panels: {{pa-nel activePanelId=activePanelId}} and then check in each panel
{{#if (eq elementId activePanelId)}}
{{!whatever is different on the active panel}}
{{/if}}
or use it in the js code:
isActive: Ember.computed('activePanelId', function() {
return this.get('activePanelId')===this.get('elementId');
},
If the panel becomes active by an action related to itself (e.g. clicking on it), just set activePanelId to the elementId in the respective action - since the property activePanelId exists only once on the parent component, all other panels to which it is passed will take note.
If using the elementId feels to hacky, you might as well give each panel a distinct name and store the activePanelName in the calling component.
*If you really do not want the property in the parent component, you could move it to a service that you then inject into the panel components, but I cannot yet imagine a good reason for preferring that.
#arne.b 's anwser sums it pretty well, the best way of handling component's state with common parent data, but if you are dead serious about not using a parent component there is kind of way to get it done.
Example:
import Ember from 'ember';
export default Ember.Component.extend({
sharedObject: {},
sharedArray: [],
});
In the above component you can use sharedObject or sharedArray to exchange state with multiple instances of the component. Any changes in the object or array will be reflected to all the instances of the same component.
A sample Ember twiddle.

emberjs providing data for multiple components

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

How to create a link-to drop-down in Ember.js

I have an application where we'd like to use a <select> element to control which object is loaded in a detail route.
If this were a normal <ul>, like if I were using bootstrap's drop-down, using the #link-to helper would be perfect here, because I could use the active class to always automatically select the item that is loaded in the child route.
With a select element it's a little different. I'll probably need to write a view, I'm okay with that, but it occurs to me that parents aren't supposed to know about their children in ember, so how does a parent view get access to know which child view is currently selected?
Even a link to how the link-to helper is implemented could be helpful here.
Thanks!
If I'm interpreting this correctly, you're looking to change the URL when the <select> option changes.
With a combination of transitionToRoute, observes, and currentPath you can achieve this using the built in Ember.Select view class.
Here's a JSbin that might help:
http://emberjs.jsbin.com/jogadudase/2/edit
parents aren't supposed to know about their children in ember
Where does this come from?
I'm not 100% sure of what you want to accomplish, but you probably want to use one of this: Ember.ArrayController or Ember.CollectionView.
With either of those you can have control over children objects.

Ember Custom View Creating a Chart View

Im in the process of creating a view for a chart with jquery.flot. So I created a view and in my template call it like so:
{{view GraphView graphableBinding="graphables"}}
Problem is the graphables object is an array and the observers do not fire which notifies me of changes to the array within the view.
I thought about using a CollectionView, however, the array itself doesnt affect the html result of the template but rather needs to fire a $.plot() command on the container div with the new data array. Any thoughts on how I would implement this sort of thing?
You can use #each key lets you observe changes to the contents of the array. See the ember.js guide regarding this feature:
http://emberjs.com/guides/object-model/computed-properties-and-aggregate-data/

In an Ember view, what's the best way to run code after the rerendering of an internal child view?

I found a lot of questions about how to initialize jQuery plugins with dynamic data etc and usually the answer is that you should use the didInsertElement hook. My problem is that when using ember-data, initially the view is inserted empty and the template vars are filled afterwards through the bindings. Thus in didInsertElement the elements I need are not there.
I found a solution that works for me but I think there must be a better solution.
In addition to the didInsertElement method I also observe the attributes in the controller and call my code from there, too, wrapped in a Ember.run.next so the child view is rendered before.
Is there a better solution to react to updated child views?
It depends a bit on the size of your application, and the number of elements your view is rendering.
Edit: You might successfully observer the isLoaded property of the RecordArray. Some details here: https://github.com/emberjs/data/blob/master/packages/ember-data/lib/system/record_arrays/adapter_populated_record_array.js#L21
One option that I have used for views that have a single object as their content is something like:
MyApp.CustomView = Ember.View.extend({
content: null,
contentObserver: function() {
this.rerender();
}.observes('content')
}
If your view contents is a list, it makes little sense to re render the view for each item in the list, though.
However, I think this approach is fairly similar to what you are already doing, though.
Another approach is to implement your own adapter with Ember Data. This way, you can tell the rest of your application that it's finished loading your data. Something like:
MyApp.Adapter = DS.Adapter.create({
//Finding all object of a certain type. Fetching from the server
findAll: function(store, type, ids) {
var url = type.url;
jQuery.getJSON(url, function(data) {
store.loadMany(type, data);
});
//Perform some custom logic here...
}
}
This should work, but its not as generic as it should/could be though.
Also, you might want to take a look at this commit, which allows for registering a one-time event.
https://github.com/emberjs/ember.js/commit/1809e65012b93c0a530bfcb95eec22d972069745#L0R19
I had this problem and came up with a solution: make a handlebars helper that tells you when the sub-section has rendered. Hope that helps some other folks!