I am trying to upgrade a partially built UI to the latest Ember.js rc1 and it has turned into a very big rewrite job thanks to the dramatically changed API. Most info out there (and here) has been rendered useless. I've had to go through the documentation again several times to get things partially working but there are a lot of loose ends. Here is a biggie. The views do not update like they did under the previous version. I'm missing something that must have to do with rerender, {{outlet}} or something else that I'm not aware of. The ember guides seem to need updates.
The template is very simple:
<script type="text/x-handlebars" data-template-name="index">
<button {{action "addOne"}}>add one</button>
<ul>
{{#each item in controller}}
<li>{{item.title}}</li>
{{/each}}
</ul>
</script>
When clicked, the button adds a new element to the backed array. The console logs show that the array is growing, but the template does not change. Here is a jsfiddle to illustrate how far I've gotten. Can anyone figure out what needs to be added?
I modified your example to highlight the fact when we use arrays in Ember, that we are using Ember arrays (Ember.A() or Em.A() if you want to make explicit this fact). From my understanding, you can use the methods Em.A().addObject and Em.A().removeObject to achieve the basic functionality using the Ember.Object getter and setter methods, (i.e. .get() & .set()) .
In order be properly observed by the Ember application, it is important to use the Ember getters and setters.
A modified version of your fiddle.
Related
I'm currently in the process of updating my Ember App to use 1.13 and am having an issue translating a particular Ember.View and its associated Ember.ArrayController into an Ember.Component, as per the Deprecation Guide.
After repurposing some code, I now have the following:
app/templates/page.hbs
{{example-component content=posts}} // "posts" being an array of objects
app/templates/components/post-list.hbs
{{#each content key="#index" as |post|}}
{{post.title}}
{{/each}}
In some cases, the order of the items in the posts array will need to change (via the Ember.SortableMixin), and these changes need to be reflected on screen. When I do this, however, it seems as though content isn't being binded correctly and doesn't update visually (though the order of the posts data is correct in the PageController).
I hope this makes sense. Any help is greatly appreciated!
With the help of #locks in Freenode IRC, I have an answer. The issue had to do with the key that was being iterated on in the {{#each}} loop. By changing it to key="#identity" (Ember 1.13.2) it now works as desired.
I'm making a sortable component. You could imagine a simple implementation having the following API:
{{sortable-list content=orderedQuestions tag='ul' itemTag='li'}}
But, that offers pretty limited template customization. Ideally I'd like the API to allow something like this:
{{#sortable-list content=orderedQuestions tag='ul'}}
<li>
<h2>{{title}}</h2>
<div>Some more {{details}}</div>
</li>
{{/sortable}}
where this block template gets used for each sortable item. Is this possible? I tried something like this for the component's template:
{{#each item in content}}
{{#with item}}
{{yield}}
{{/with}}
{{/each}}
but this doesn't work, since the block template the user passes into the component has the controller as its context.
Is what I'm after possible?
The exact thing you're trying to do is not currently possible.
Take a look at this Github issue to see a lot of discussion around component use cases like this. In particular take a look at Trek's first comment.
An alternative is to break your component into multiple components:
{{#sortable-list content=orderedQuestions tag='ul'}}
{{#each orderedQuestions}}
{{#sortable-item tag='li'}}
<h2>{{title}}</h2>
<div>Some more {{details}}</div>
{{#sortable-item}}
{{/each}}
{{/sortable}}
If you think about components in terms of existing HTML elements you can see that it kind of looks a little bit like a select which has options inside. To make this work it's likely that the sortable-items would need to communicate with the sortable list. One option is to use parentView in your component to get access to the parent.
Take a look at this talk from Ryan Florence to get more ideas about how to deal with composable components.
Update
Updated my answer to include an each around {{sortable-item}} to change the context.
Reading through the Ember each helper it seems that it would be possible for a component to do what you want but it looks really hairy.
I am embedding my Ember App into another environment. Prototype.js is in use there. Due to this, ember breaks.
I have read http://emberjs.com/guides/configuring-ember/disabling-prototype-extensions/
I followed that but unfortunately issue is not resolved.
JS Bin: http://jsbin.com/raqab/2/edit
I am not sure what i am doing wrong here, Can anyone please assist me?
In the same guide, where the effect of disabling prototype extensions is explained for arrays, it is said that
Native arrays will no longer implement the functionality needed to observe them. If you disable prototype extension and attempt to use native arrays with things like a template's {{#each}} helper, Ember.js will have no way to detect changes to the array and the template will not update as the underlying array changes.
Additionally, if you try to set the model of an Ember.ArrayController to a plain native array, it will raise an exception since it no longer implements the Ember.Array interface.
You can manually coerce a native array into an array that implements the required interfaces using the convenience method Ember.A:
Therefore change your model hook as
App.ApplicationRoute=Ember.Route.extend({
model:function(){
return Ember.A(["one","two","three"]);
}
});
and in your template,
<ul>
{{#each model}}
<li>{{this}}</li>
{{/each}}
</ul>
Updated jsbin: http://jsbin.com/hayata/1/edit
Now no error is thrown by ember, but some error is thrown from your other scripts.
Once you fix that it'll be all ok.
Quick note:
I don't believe this is a duplicate of Ember.js: Prevent destroying of views. Other related questions that I've found are out-of-date.
In case this becomes out-of-date later, I am using Ember 1.7.0 with Handlebars 1.3.0.
Context for the question:
As the title states, I am wondering how to transition between views without destroying them. Using queryParams does not solve my issue.
I am creating a calculator with the following nested views:
>>Calculator View
>>Report View (hasMany relationship to Calculator)
--School Partial (I am using queryParams here)
I am able to navigate between the Report views just fine without destroying my School partial, since I am using queryParams and using a displaySchoolPartial boolean to show/hide the partial. Example below:
Report template (stripped to only show the essential part):
<script type="text/x-handlebars" data-template-name="calculator/report">
...
{{#link-to "calculator.report" (query-parameters displaySchoolPartial="true")}}
{{render "_school"}}
</script>
School template (also stripped down):
<script type="text/x-handlebars" data-template-name="_school">
{{#with controllers.calculatorReport}}
<div {{bind-attr class=":schoolPartialWrapper displaySchoolPartial::hide-element"}}>
...
</div>
{{/with}}
</script>
This works as expected. Navigating between different Report views and School partials, as stated before, does not destroy the view.
The problem:
My problem comes when navigating to the Calculator view, the Report view is destroyed, which then destroys my School view. I do not want to also use queryParams to replace my Report views.
The reason I need to make sure the views aren't destroyed is because I have a select box with 3,000 schools in my School partial. It takes too long to re-render this. It would be a much better UX to simply show/hide the Report views.
Don't fight with Ember. You will lose.
Views are instantiated and rendered when needed and torn down when done.
Why do you have a 3000-element dropdown, anyway?
If you really, really want to do this, what I would suggest is putting a {{render}} on your application page, and hide it. The view will be created and rendered when the app comes up and persist as long as the app is alive. Then, in the didInsertElement of your view, do a cloneNode of that hidden element and insert it into the view's DOM somewhere. You may have to muck around getting event handlers wired up correctly.
My suggestion is not using "render" but using "partial", so you only need to drop in the template that you want. Have a control variable that set show/hide via css class. And control that variable using you controllers.
Using "partial" will allow you to have school template independent from report, thereby removing report will not affect school.
Just make sure you define the outlet and partial correctly.
Hope it helps!
I am currently in a situation where i need to remove the metamorph tags used in a ember js select box. I use a JS plugin that takes my select box and turns it into a styled select box which i have customized using css.
I currently have this ember js select box in my view.
<script type="text/x-handlebars" data-template-name="opportunities">
{{view Ember.Select
contentBinding="controller.filter"
optionLabelPath="content.metacode"
optionValuePath="content.description"
class="mydds opportunitylistcategory"}}
</script>
I just need a way to remove the metamorph code but for ember selectbox to still have binding etc enabled... I have read about removing metamorphic data with a jquery sortable but that hasnt helped me. Thanks in advance!
The best way to work around this is to use an Ember.CollectionView.
I've run into this issue with a simple unordered list (<ul><li>).
Here is an answer that demonstrates how to do it https://stackoverflow.com/a/11749736/701368
Edit
The original answer was for a version of Ember before 1.0.
I would recommend using the {{each}} helper with the itemViewClass attribute when dealing with newer versions of Ember.js. You can find the documentation here: http://emberjs.com/api/classes/Ember.Handlebars.helpers.html#toc_specifying-a-view-class-for-items.
So don't use the block level helper; you want to use {{each}} instead of {{#each}}.