Skip to content

Instantly share code, notes, and snippets.

@Geokoumpa
Last active January 14, 2019 20:49
Show Gist options
  • Save Geokoumpa/117698efb4acc9c40d14fa5a8240b26f to your computer and use it in GitHub Desktop.
Save Geokoumpa/117698efb4acc9c40d14fa5a8240b26f to your computer and use it in GitHub Desktop.
collapsible
import Ember from 'ember';
export default Ember.Component.extend({
_model: Ember.computed.oneWay('model'),
actions: {
itemChanged(node){
this.propagateDownwards(node)
this.propagateUpwards(node)
},
},
propagateUpwards(node){
let parent = Ember.get(node, 'parentNode');
if (parent) {
let allChildrenChecked = Ember.get(parent, 'children').every(c => Ember.get(c, 'checked'))
Ember.set(parent, 'checked', allChildrenChecked)
this.propagateUpwards(parent);
}
},
propagateDownwards(node){
if (Array.isArray(Ember.get(node, 'children')) && !Ember.isEmpty(Ember.get(node, 'children'))) {
Ember.get(node, 'children').forEach(c => {
Ember.set(c, 'checked', Ember.get(node, 'checked'))
this.propagateDownwards(c)
})
}
},
// For each node in the tree, find its parent
didReceiveAttrs(){
this._super(...arguments)
this.get('_model').forEach(n => {
Ember.set(n, 'parentNode', null)
this.applyParentToChildren(n)
})
},
applyParentToChildren(parentNode){
if (Array.isArray(Ember.get(parentNode, 'children')) && !Ember.isEmpty(Ember.get(parentNode, 'children'))) {
Ember.get(parentNode, 'children').forEach(c => {
Ember.set(c, 'parentNode', parentNode)
this.applyParentToChildren(c)
})
} else {
// if we have reached a leaf node, sanitize data bottom-up
this.propagateUpwards(parentNode)
}
},
willDestroyElement(){
this._super(...arguments)
this.get('model').forEach(n => { this.cleanupSelfAndChildren(n)})
console.log(this.get('model'))
},
cleanupSelfAndChildren(parentNode){
Ember.setProperties(parentNode, {
parentNode: undefined,
checked: undefined
})
let children = Ember.get(parentNode, 'children')
if (Array.isArray(children) && !Ember.isEmpty(children)) {
children.forEach(c => { this.cleanupSelfAndChildren(c) })
}
}
//TODO
// * Cleanup on component destroy or make sure model is deeply copied and initial model is left in tact
});
import Ember from 'ember';
export default Ember.Component.extend({
classNames: ['collapsible'],
collapsed: false,
parentAction: 'childCheckedWasToggled',
buttonText: Ember.computed('collapsed', function(){
return this.get('collapsed') ? "-" : "+"
}),
actions: {
toggleCollapsed(){
this.toggleProperty('collapsed')
},
checkToggled(){
this.toggleProperty('node.checked')
this.get('notifyContainer')(this.get('node'))
}
}
});
import Ember from 'ember';
export default Ember.Controller.extend({
appName: 'Ember Twiddle',
});
import Ember from 'ember';
export default Ember.Controller.extend({
items: [
{title: "test1", children: [
{title: "test1.1", children: [
{title: "test1.1.1", checked: true},
{title: "test1.1.2", checked: true},
{title: "test1.1.3", checked: true},
{title: "test1.1.4", checked: true},
]},
{title: "test1.2", children: [
{title: "test1.2.1"},
{title: "test1.2.2"},
{title: "test1.2.3"},
{title: "test1.2.4"},
]},
{title: "test1.3", children: [
{title: "test1.3.1"},
{title: "test1.3.2"},
{title: "test1.3.3"},
{title: "test1.3.4"},
]},
]},
{title: "test2", children: [
{title: "test2.1", children: [
{title: "test2.1.1"},
{title: "test2.1.2"},
{title: "test2.1.3"},
{title: "test2.1.4"},
]},
{title: "test2.2", children: [
{title: "test2.2.1"},
{title: "test2.2.2"},
{title: "test2.2.3"},
{title: "test2.2.4"},
]},
{title: "test2.3", children: [
{title: "test2.3.1"},
{title: "test2.3.2"},
{title: "test2.3.3"},
{title: "test2.3.4"},
]},
]},
]
});
import Ember from 'ember';
export function equal(params) {
return params[0] === params[1];
}
export default Ember.Helper.helper(equal);
import EmberRouter from '@ember/routing/router';
import config from './config/environment';
const Router = EmberRouter.extend({
location: 'none',
rootURL: config.rootURL
});
Router.map(function() {
this.route("test")
});
export default Router;
body {
margin: 12px 16px;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 12pt;
}
.collapsible{
padding-left: 30px;
display: block;
}
.checkbox-wrapper{
display: inline;
}
{{#each _model as |node|}}
{{tree-node node=node notifyContainer=(action 'itemChanged')}}
{{/each}}
{{#if node.children}}
<button {{action 'toggleCollapsed'}}>{{buttonText}}</button>
{{/if}}
<input type="checkbox" checked={{node.checked}}
onclick={{action "checkToggled"}}
>
{{node.title }}
{{#if collapsed}}
{{#each node.children as |child|}}
{{tree-node node=child notifyContainer=(action notifyContainer) }}
{{/each}}
{{/if}}
{{outlet}}
{{tree-container model=items}}
<ul>
{{#each items as |item|}}
<li>{{item.title}} {{item.checked}}
<ul>
{{#each item.children as |child|}}
<li>{{child.title}}: {{child.checked}}</li>
{{/each}}
</ul>
</li>
{{/each}}
</ul>
{{#link-to 'test'}}test{{/link-to}}
{{#link-to "index"}}back {{/link-to}}
{
"version": "0.15.1",
"EmberENV": {
"FEATURES": {}
},
"options": {
"use_pods": false,
"enable-testing": false
},
"dependencies": {
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js",
"ember": "3.4.3",
"ember-template-compiler": "3.4.3",
"ember-testing": "3.4.3"
},
"addons": {
"ember-data": "3.4.2"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment