I am new to Ember, and I am trying to set up a list of folders. When you click on the icon next to a folder, it will load (i.e. find('folder', folder_id) ) the child folders. If the top level folder has 16 sub-folders, I am trying to set a property on those sixteen folders as they are finished loading -- so if the model for one of the sub-folders is finished loading, I want to set a property on it while the other fifteen folders are still being retrieved and serialized.
In my folder model:
import DS from 'ember-data';
export default DS.Model.extend({
files: DS.hasMany('file'),
children: DS.hasMany('folder', { inverse: 'parent', async: true }),
parent: DS.belongsTo('folder', {inverse: 'children'}),
name : DS.attr('string'),
nodeId : DS.attr('string'),
classId : DS.attr('string'),
parentId: DS.attr('string'),
contents: DS.attr(),
isVisible: DS.attr('boolean'),
childName: DS.attr('string')
});
In my template/view:
{{#each child in children}}
{{#if child.isLoading}}
Loading -->
{{else}}
{{setChildProperty}}
{{/if}}
{{/each}}
In my controller:
import Ember from 'ember';
export default Ember.Controller.extend({
children: function() {
var model = this.get('model');
var children = model.get('children');
return children;
}.property(),
setChildProperty: function(){
// how can I know, here in the controller, what the index is for
// the child that triggered this function, so that I can set a
// property on it without getting some type of
// 'didSetProperty / root.state.loading' error.
// The code below will cause errors because not all of the
// children have finished loading:
// var model = this.get('model');
// var self = this;
// var children = model.get('children');
// var contents = model.get('contents');
//
// children.forEach(function(item, index){
// var folderName = contents[index].folder;
// item.set('name',folderName);
// });
}.property('children.@each.isLoading'),
});
My Ember-CLI version is 0.1.15
Any help would be greatly appreciated.
UPDATE
In regards to mpowered's solution, the real problem is the nature of my folder models, in that the folder model does not have a name property, instead it has a list of child names. And since the child relationships are retrieved asynchronously when a user clicks on a sub-folder, I need to get the child folder names from another array, the contents array, which has identical indices. So using mpowered's solution my problem would be like so:
foldr: {{folder.id}}<br>
{{#each child in folder.children}}
{{#view 'toggle-list'}}
<i {{bind-attr id="child.id"}} class="fa fa-caret-right"></i>
{{/view}}
Index: {{_view.contentIndex}}
<!-- I need to be able to echo the above index in the
folder.contents array to get the child name.
-->
<!-- these work when uncommented, but I need a dynamic solution
name: {{folder.contents.[1].folder}}
name: {{folder.contents.1.folder}}
-->
<!-- None of these work:
name:{{!folder.contents.[_view.contentIndex].folder}}
name:{{!folder.contents.index.folder}}
name:{{!folder.contents.[index].folder}}
name:{{!folder.contents.{{!_view.contentIndex}}.folder}}
-->
Child:{{child.id}}..
<br>
<div {{bind-attr id="child.childName"}} class="folder-child hidden">
{{#if child.isVisible}}
isVisible is true<br>
{{folder-tree-component folder=child}}
{{/if}}
</div>
{{/each}}
I should also note that I am using a PODS structure and I have no control over the JSON response I get from the server to populate my models (other than Ember serializers of course).
There are many things that are concerning about this.
First, properties are not actions. You don't EVER want to change the state of an object when you're getting a property unless you have very very good reasons for doing so, or if you're implementing a getter/setter pattern. Delete setChildProperty
, because that's all bad. In the template, you should just be displaying the property, not trying to "do" anything with it.
Second, this should probably be created as a component, because it sounds like the recursive structure you have here would lend itself well to reusable components. Something like folder-tree-component.hbs
:
{{folder.name}}
{{#each child in folder.children}}
{{folder-tree-component folder=child}}
{{/each}}
And in your main route:
{{folder-tree-component folder=model}}
// Or, alternatively
{{#each child in model.children}}
{{folder-tree-component folder=child}}
{{/each}}
If I understand you correctly, you want a computed property on your model, not to "set" something on the model (or the controller/component) when it's finished loading. When the property is requested, it will compute the value and cache it in case you ask for it again. On your model:
name: function() {
// something with this.get('contents')
}.property('contents', 'otherDependency') // <- These will tell Ember to recompute the property when changed
I would learn more about ember fundamentals before trying to tackle this, there are some very simple, yet crucial things to learn about how Ember ticks, and a file tree isn't the simplest implementation to begin with.