Skip to content

Commit

Permalink
add component rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
miguelcobain committed Jan 13, 2016
1 parent 756fdf6 commit 47bfcaa
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 75 deletions.
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,16 +105,15 @@ Its usage should be very similar to `Ember.Select`, but with additional features
<td valign="top">
<code>optionFunction</code>, <code>itemFunction</code>, <code>optionCreateFunction</code>, <code>optgroupHeaderFunction</code>, <code>optgroupFunction</code>
</td>
<td valign="top">Will be called on the component with two parameters <code>data</code> and <code>escape</code>. <code>escape</code> is a function to escape text. These functions are expected to build the desired html and return it as a string. These functions take precedence over their <code>Template</code> and <code>View</code> counterparts.</td>
<td valign="top">Will be called on the component with two parameters <code>data</code> and <code>escape</code>. <code>escape</code> is a function to escape text. These functions are expected to build the desired html and return it as a string or DOM elements. These functions take precedence over their <code>Component</code> counterparts.</td>
</tr>
<tr>
<td valign="top">
<code>optionTemplate</code>, <code>itemTemplate</code>, <code>optionCreateTemplate</code>, <code>optgroupHeaderTemplate</code>,
<code>optgroupTemplate</code><br> <code>optionView</code>, <code>itemView</code>,
<code>optionCreateView</code>, <code>optgroupHeaderView</code>
and <code>optgroupView</code>
<code>optionComponent</code>, <code>itemComponent</code>,
<code>optionCreateComponent</code>, <code>optgroupHeaderComponent</code>
and <code>optgroupComponent</code>
</td>
<td valign="top">Render using templates or views! View takes precedence over template, so if you do strange things like setting <code>optionView</code> and <code>optionTemplate</code>, the latter will be ignored. Might not work with all Ember versions. This is delicate. Check <a href="https://github.com/miguelcobain/ember-selectize/issues/13#issuecomment-56155784">this issue</a>.</td>
<td valign="top">Render using components! Functions (see above) take precedence over components, so if you do strange things like setting <code>optionFunction</code> and <code>optionComponent</code>, the latter will be ignored. Inside your component and template <code>data</code> will contain the data for the current item being rendered. An example component could be <code>Hi, {{data.firstname}}!</code></td>
</tr>
<tr>
<td valign="top"><code>required</code></td>
Expand Down
89 changes: 23 additions & 66 deletions addon/components/ember-selectize.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,8 @@ export default Ember.Component.extend({
var content = this.get('content');
if (!isArray(content)) { return; }

var _this = this;
return content.reduce(function(previousValue, item) {
return previousValue.addObject(get(item, _this.get('_groupPath')));
return content.reduce((previousValue, item) => {
return previousValue.addObject(get(item, this.get('_groupPath')));
}, Ember.A());
}
}),
Expand All @@ -73,7 +72,6 @@ export default Ember.Component.extend({
*/
content: computed('groupedContent.[]', function() {
var groupedContent = this.get('groupedContent');
var _this = this;

if (!groupedContent) { return; }

Expand All @@ -86,7 +84,7 @@ export default Ember.Component.extend({
//set in the object. Use ObjectProxy to keep original object intact.
var proxiedContent = content.map(item => {
var proxy = { content: item };
proxy[_this.get('_groupPath')] = groupLabel;
proxy[this.get('_groupPath')] = groupLabel;
return Ember.ObjectProxy.create(proxy);
});

Expand All @@ -105,9 +103,8 @@ export default Ember.Component.extend({
var optgroups = this.get('optgroups');
if (!optgroups) { return; }

var _this = this;
optgroups.forEach(group => {
_this._selectize.addOptionGroup(group, { label: group, value: group});
this._selectize.addOptionGroup(group, { label: group, value: group});
});
}),

Expand Down Expand Up @@ -144,49 +141,37 @@ export default Ember.Component.extend({
*/
functionNames: ['option', 'item', 'option_create', 'optgroup_header', 'optgroup'],
templateSuffix: 'Template',
viewSuffix: 'View',
componentSuffix: 'Component',
functionSuffix: 'Function',
renderOptions: computed(function() {
var functionNames = this.get('functionNames');
//this hash will contain the render functions
var renderFunctions = {};

functionNames.forEach(function(item) {
functionNames.forEach(item => {
// infer the function name by camelizing selectize's function and appending the function suffix (overridable)
var functionSuffix = this.get('functionSuffix');
var functionPropertyName = camelize(item) + functionSuffix;
var renderFunction = this.get(functionPropertyName);
// functions take precedence
if (renderFunction) {
renderFunctions[item] = renderFunction.bind(this.get('targetObject'));
renderFunctions[item] = (data, escape) => {
return renderFunction(data.data || data, escape);
};
} else {
// infer the view name by camelizing selectize's function and appending a view suffix (overridable)
var templateSuffix = this.get('templateSuffix');
var viewSuffix = this.get('viewSuffix');
var viewPropertyName = camelize(item) + viewSuffix;
var viewToRender = this.get(viewPropertyName);
var componentSuffix = this.get('componentSuffix');
var componentPropertyName = camelize(item) + componentSuffix;
var componentToRender = this.get(componentPropertyName);

var _this = this;
if (viewToRender) {
if (componentToRender) {
// we have a view to render. set the function.
renderFunctions[item] = function(data) {
return _this._viewToString(viewToRender, data.data);
renderFunctions[item] = data => {
return this._componentToDOM(componentToRender, data.data || data);
};
} else {
// there isn't a view to render. try to get a template.
// infer the template name by camelizing selectize's function and appending a template suffix (overridable)
var templatePropertyName = camelize(item) + templateSuffix;
var templateToRender = this.get(templatePropertyName);

if (templateToRender) {
// we have a template to render. set the function.
renderFunctions[item] = function(data) {
return _this._templateToString(templateToRender, data.data);
};
}
}
}
}, this);
});

return renderFunctions;
}),
Expand Down Expand Up @@ -790,46 +775,18 @@ export default Ember.Component.extend({
}
}),

_templateToString(templateName, data) {
var template = this.container.lookup('template:' + templateName);

if (!template) {
throw new TypeError('template ' + templateName + ' does not exist.');
}

var controller = Ember.Controller.create(Ember.typeOf(data) === 'instance' ? data : {data: data});
var view = this.createChildView(Ember.View, {
template: template,
controller: controller,
container: this.get('container')
});

return this._getStringFromView(view);
},

_viewToString(viewName, data) {
var viewClass = this.container.lookup('view:' + viewName);
_componentToDOM(componentName, data) {
var component = this.container.lookup('component:' + componentName);

if (!viewClass) {
throw new TypeError('view ' + viewName + ' does not exist.');
if (!component) {
throw new TypeError('component ' + componentName + ' does not exist.');
}

var controller = Ember.Controller.create(Ember.typeOf(data) === 'instance' ? data : {data: data});
var view = this.createChildView(viewClass, {
controller: controller
});
component.set('data', data);

return this._getStringFromView(view);
},
component.createElement();

/*
* Encapsulates the logic of converting a view to a string
*/
//FIX ME: this method does not work in Ember 1.8.0
//see http://git.io/VUYZ4g for more info
_getStringFromView(view) {
view.createElement();
return view.element.outerHTML;
return component.element;
},

_mergeSortField(options) {
Expand Down
4 changes: 2 additions & 2 deletions blueprints/ember-cli-selectize/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ module.exports = {
// not specified (since that doesn't actually matter
// to us
},

afterInstall: function() {
return this.addBowerPackageToProject('selectize');
return this.addBowerPackageToProject('selectize', 'miguelcobain/selectize.js#master');
}
};
2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"jquery": "1.11.3",
"loader.js": "ember-cli/loader.js#3.4.0",
"qunit": "~1.20.0",
"selectize": "~0.12.0"
"selectize": "miguelcobain/selectize.js#master"
},
"resolutions": {
"qunit-notifications": "~0.1.0"
Expand Down
58 changes: 58 additions & 0 deletions tests/unit/components/ember-selectize-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
moduleForComponent,
test
} from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';

moduleForComponent('ember-selectize', 'Unit | Component | ember-selectize', {
// Specify the other units that are required for this test
Expand Down Expand Up @@ -1015,3 +1016,60 @@ test('content from a Promise don\'t overwrite newer content once resolved', func
this.render();

});

test('renders components', function(assert) {

//this.register('template:components/foo-bar', hbs`Hi, Mom!`);
this.register('component:foo-bar', Ember.Component.extend({
layout: hbs`Hi, {{data.firstName}}!`
}));


var yehuda = Ember.Object.create({ id: 1, firstName: 'Yehuda' });
var tom = Ember.Object.create({ id: 2, firstName: 'Tom' });
var seb = Ember.Object.create({ id: 3, firstName: 'Seb' });

var component = this.subject();

Ember.run(function() {
component.set('optionComponent', 'foo-bar');
component.set('optionValuePath', 'id');
component.set('optionLabelPath', 'firstName');
component.set('content', Ember.A([yehuda, tom, seb]));
component.set('selection', tom);
});

this.render();

assert.deepEqual(component._selectize.items, ["2"]);
assert.equal(component._selectize.$dropdown_content.children().length, 3);
assert.equal(component._selectize.$dropdown_content.children().text(), 'Hi, Yehuda!Hi, Tom!Hi, Seb!');

});

test('renders function', function(assert) {

var yehuda = Ember.Object.create({ id: 1, firstName: 'Yehuda' });
var tom = Ember.Object.create({ id: 2, firstName: 'Tom' });
var seb = Ember.Object.create({ id: 3, firstName: 'Seb' });

var component = this.subject();

Ember.run(function() {
component.set('optionFunction', function(data) {
assert.equal(arguments.length, 2, 'arguments length');
return `<div>Hi, ${data.get('firstName')}!</div>`;
});
component.set('optionValuePath', 'id');
component.set('optionLabelPath', 'firstName');
component.set('content', Ember.A([yehuda, tom, seb]));
component.set('selection', tom);
});

this.render();

assert.deepEqual(component._selectize.items, ["2"]);
assert.equal(component._selectize.$dropdown_content.children().length, 3);
assert.equal(component._selectize.$dropdown_content.children().text(), 'Hi, Yehuda!Hi, Tom!Hi, Seb!');

});

0 comments on commit 47bfcaa

Please sign in to comment.