├── README.md └── helper └── render-component.js /README.md: -------------------------------------------------------------------------------- 1 | # Warning! 2 | 3 | It's better to check repo https://github.com/ciena-blueplanet/ember-spread - goals which we were leaded by whyle creating this helper totally covered with that addon, and it's definitely better that our approach. So code saved just for really hard cases (but think more than twice before starting use it ) 4 | 5 | # ember-render-helper 6 | Render helper for Ember.js ( Allow render components dynamically ) 7 | 8 | ##Motivation 9 | Sometimes we need to render component by name, which stores at some property. For such cases there are several workarounds. 10 | 11 | You can read next links: 12 | 13 | http://discuss.emberjs.com/t/programmatically-rendering-ember-components/6986 14 | 15 | http://stackoverflow.com/questions/25946805/how-to-dynamically-load-ember-components-by-name-in-a-template 16 | 17 | https://stackoverflow.com/questions/18972202/how-can-i-invoke-an-ember-component-dynamically-via-a-variable 18 | 19 | etc. 20 | 21 | ##Solutions 22 | Of course at newer [Ember component helper](http://emberjs.com/blog/2015/03/27/ember-1-11-0-released.html#toc_component-helper) 23 | But current version and it's documentation doesn't describe how to pass arguments/paraments. Also it is not available for older versions of Ember. So here is another one '*bicycle*' 24 | 25 | ##Examples 26 | All this examples describe at file comments. Here i just duplicate them 27 | 28 | {{render-component 'image-component' src="" class="image"}} 29 | 30 | {{#render-component 'btn-component' action="addSection"}} 31 | 32 | {{#render-component componentName _param='btn-component' action="addSection"}} 33 | 34 | or 35 | 36 | {{#render-component 'btn-component' action="addSection"}} 37 | Add section 38 | {{/render-component}} 39 | 40 | or even 41 | 42 | {{#render-component 'render-component' _param='btn-component' action="addSection"}} 43 | Add section 44 | {{/render-component}} 45 | 46 | You also can use ___params to define all properties via hash like 47 | instead of 48 | 49 | {{render-component 'pluralize-component' count=ungrouped.content.meta.total single="Object"}} 50 | 51 | you can use 52 | 53 | {{render-component 'pluralize-component' ___params=hash}} 54 | 55 | and hash is 56 | 57 | hash = { count:ungrouped.content.meta.total, single:"Object"} 58 | 59 | For cases when we need pass into component not only attributes but param too 60 | When we need this? 61 | for example you want to render 62 | 63 | 64 | {{componentName paramName someOption=someOptionValue}} 65 | 66 | You can do 67 | 68 | 69 | {{#render-component 'componentName' someOption=someOptionValue}} 70 | 71 | **BUT! You can't do ( you can't pass more than one argument into component )** 72 | 73 | 74 | {{#render-component 'componentName' paramName someOption=someOptionValue}} 75 | 76 | so for such cases you can use '_param' ( at line param or at hash property ) 77 | 78 | 79 | {{#render-component 'componentName' _param=paramName someOption=someOptionValue}} 80 | 81 | or 82 | 83 | 84 | {{#render-component 'componentName' __params=paramsHash}} 85 | 86 | where paramsHash is 87 | 88 | 89 | paramsHash = { _param:paramName, someOption=someOptionValue } 90 | 91 | 92 | ##Tests 93 | Sadly for me, but right now there are no tests for helper ( yep, I know it's not good ). But I hope to fix this in future. 94 | 95 | #[Welcome with feedback and your proposals](https://github.com/vvs-code/ember-render-helper/issues) 96 | -------------------------------------------------------------------------------- /helper/render-component.js: -------------------------------------------------------------------------------- 1 | const { Logger, isArray, merge, get, $: { each }, __loader } = Ember; 2 | 3 | // import from ember-htmlbars/system/lookup-helper 4 | // todo: find more correct way to import it 5 | const { default: lookupHelper } = __loader.require('ember-htmlbars/system/lookup-helper'); 6 | 7 | /** 8 | * Render component by name 9 | * @param componentPath 10 | * @param options 11 | * 12 | * Examples: 13 | * 14 | {{render-component 'image-component' src="" class="image"}} 15 | {{render-component 'pluralize-component' count=ungrouped.content.meta.total single="Object"}} 16 | {{#render-component 'btn-component' action="addSection"}} 17 | {{#render-component componentName _param='btn-component' action="addSection"}} 18 | or 19 | {{#render-component 'btn-component' action="addSection"}} 20 | Add section 21 | {{/render-component}} 22 | or even 23 | {{#render-component 'render-component' _param='btn-component' action="addSection"}} 24 | Add section 25 | {{/render-component}} 26 | You also can use ___params to define all properties via hash like 27 | instead of 28 | {{render-component 'pluralize-component' count=ungrouped.content.meta.total single="Object"}} 29 | you can use 30 | {{render-component 'pluralize-component' ___params=hash}} 31 | and hash is 32 | hash = { count:ungrouped.content.meta.total, single:"Object"} 33 | 34 | For cases when we need pass into component not only attributes but param too 35 | When we need this? 36 | for example you want to render 37 | {{componentName paramName someOption=someOptionValue}} 38 | You can do 39 | {{#render-component 'componentName' someOption=someOptionValue}} 40 | BUT! You can't do ( you can't pass more than one argument into component) 41 | {{#render-component 'componentName' paramName someOption=someOptionValue}} 42 | so for such cases you can use '_param' ( at line param or at hash property) 43 | {{#render-component 'componentName' _param=paramName someOption=someOptionValue}} 44 | or 45 | {{#render-component 'componentName' __params=paramsHash}} 46 | where paramsHash is 47 | paramsHash = { _param:paramName, someOption=someOptionValue } 48 | */ 49 | export default function(componentPath, options = {}) { 50 | // default value to prevent call errors 51 | options.hashContexts = options.hashContexts || {}; 52 | options.hashTypes = options.hashTypes || {}; 53 | // extract variables 54 | const { data: { view } } = options; 55 | const { container } = view; 56 | let { hash } = options; 57 | // util-functions to read value 58 | const resolvePath = (path) => (view.getStream ? view.getStream(path).value() : path); 59 | // util-function to get helper/component by name 60 | const getHelper = (container, helperName) => lookupHelper(helperName, view, Ember.Handlebars).helperFunction; 61 | 62 | // check if we can read property by passed value 63 | let component = resolvePath(componentPath) || componentPath; 64 | // rallback if resolved component path is not a string 65 | component = (typeof component !== 'string') ? componentPath : component; 66 | 67 | // get component/helper by name 68 | const helper = getHelper(container, component); 69 | 70 | // Allow to pass params as hash-object 71 | if ('___params' in hash) { 72 | let params = resolvePath(hash.___params); 73 | delete hash.___params; 74 | hash = merge(params, hash); 75 | options.hash = hash; 76 | } 77 | // Allow to specify which params should be resolved 78 | each(hash, (key, value) => { 79 | let newKey = key.replace('__', ''); 80 | if ((key.indexOf('__') === 0)) { 81 | options.hash[newKey] = view._getBindingForStream(value);// resolvePath(value); 82 | options.hashContexts[newKey] = get(view, 'controller'); 83 | options.hashTypes[newKey] = 'ID'; 84 | } 85 | }); 86 | 87 | // For cases when we need pass into component not only attributes but param too 88 | // When we need this? 89 | // for example you want to render 90 | // {{componentName paramName someOption=someOptionValue}} 91 | // You can do 92 | // {{#render-component 'componentName' someOption=someOptionValue}} 93 | // BUT! You can't do ( you can't pass more than one argument into component) 94 | // {{#render-component 'componentName' paramName someOption=someOptionValue}} 95 | // so for such cases you can use 96 | // {{#render-component 'componentName' _param=paramName someOption=someOptionValue}} 97 | // or 98 | // {{#render-component 'componentName' __params=paramsHash}} 99 | // where paramsHash is 100 | // paramsHash = { _param:paramName, someOption=someOptionValue } 101 | let param; 102 | if ('_param' in hash) { 103 | param = view._getBindingForStream(hash._param).value() || hash._param; 104 | if (!isArray(param)) { 105 | param = [param]; 106 | } 107 | delete hash._param; 108 | } 109 | 110 | if (!helper) { 111 | Logger.warn(`render-component can\'t find handler for "${component}"`); 112 | } else if (param) { 113 | helper.call(this, param, options, options, options); // this strange params for instanceHelper: function(newView, hash, options, env) { at ember.debug.js 114 | } else { 115 | helper.call(this, [], hash, options, options, options); 116 | } 117 | } 118 | --------------------------------------------------------------------------------