As the title, I am looking to recreate the basic functionality of the WP columns block. The reason for this is-
WP adds a number of controls that I do not want the user to have (variations, width slider)
The allowed blocks, a column, lets the user add any sort of content they like. I am looking to have control over this with an allowed block list
I have created the following edit function:
( function( wp ) {
var registerBlockType = wp.blocks.registerBlockType;
var el = wp.element.createElement;
var __ = wp.i18n.__;
const { RadioControl, PanelBody, RangeControl } = wp.components;
const { useBlockProps, InspectorControls, InnerBlocks } = wp.blockEditor;
const allowedBlocks = [ 'core/paragraph', 'core/button' ];
registerBlockType( 'wpboiler-core/columns', {
apiVersion: 2,
title: __(
'Columns',
'columns'
),
description: __(
'A block for displaying content in columns',
'columns'
),
category: 'design',
icon: 'schedule',
supports: {
html: false,
},
attributes: {
columnselect: {
type: 'number',
default: 2,
},
},
edit: function(props) {
const { attributes, setAttributes } = props;
const { columnselect } = attributes;
const onChangeColumnRange = value => setAttributes({ columnselect: value });
let columnsContainer = [];
for(var n = 1; n <= columnselect; n++) {
columnsContainer.push(
el(
'div',
null,
el(
InnerBlocks, {
allowedBlocks: allowedBlocks,
}
)
)
);
};
return el(
'section',
useBlockProps(attributes),
// INSPECTOR CONTROL BEGIN
el(
InspectorControls,
null,
el(
PanelBody,
{
title: "Columns",
},
el(
RangeControl, {
min: 2,
max: 4,
value: columnselect,
onChange: onChangeColumnRange,
}
),
),
),
// INSPECTOR CONTROL END
el(
'div',
{ className: 'columns__container' },
columnsContainer
),
);
},
save: function() {
return null;
},
} );
}(
window.wp
) );
The issue I am coming up against is multiple InnerBlocks. The function creates a list of InnerBlock areas. However, editing one area changes them all.
I believe one method to get around this would be to create a custom column block which contains an InnerBlock component, and render that in the for loop instead of InnerBlock. So something along the lines of...
for(var n = 1; n <= columnselect; n++) {
columnsContainer.push(
{RENDER_COLUMN_BLOCK}
);
};
But if I did this, would I still come up with the same issue? That editing one of the columns would in fact edit them all? Does each instance need to have an ID to know which is being edited?
And I am also struggling to find out how I render another custom component inside a block when not using a build-step (es5).
Any help on this task would be appreciated.
Update
A 'solution' I have created after coming across the following post is as follows, where it basically turns off the block appender once the desired column count is reached. Each new instance creates a custom block called column which has its own set of allowed blocks.
// COLUMNS index.js
( function( wp ) {
var registerBlockType = wp.blocks.registerBlockType;
var el = wp.element.createElement;
var __ = wp.i18n.__;
const { useSelect } = wp.data;
const { useBlockProps, InnerBlocks } = wp.blockEditor;
const allowedBlocks = [ 'wpboiler-core/column' ];
registerBlockType( 'wpboiler/columns', {
apiVersion: 2,
title: __(
'Columns',
'columns'
),
description: __(
'Displays content in columns',
'columns'
),
category: 'design',
icon: 'schedule',
supports: {
html: false,
},
edit: function(props) {
const { attributes, clientId } = props;
const innerBlockCount = useSelect((select) => select('core/block-editor').getBlock(clientId).innerBlocks);
return el(
'section',
useBlockProps(attributes),
__( 'Add columns by pressing the + icon. Maximum 4 columns', 'columns' ),
el(
'div',
{ className: 'columns__container' },
innerBlockCount.length > 3 ?
el(
InnerBlocks, {
allowedBlocks: allowedBlocks,
renderAppender: false
}
)
:
el(
InnerBlocks, {
allowedBlocks: allowedBlocks,
}
),
)
);
},
save: function() {
return el(
'section',
{ className: 'columns' },
el(
'div',
{ className: 'columns__container' },
el(
InnerBlocks.Content, {},
),
),
);
},
} );
}(
window.wp
) );
// COLUMN - INDIVIDUAL index.js
( function( wp ) {
var registerBlockType = wp.blocks.registerBlockType;
var el = wp.element.createElement;
var __ = wp.i18n.__;
const { useBlockProps, InnerBlocks } = wp.blockEditor;
const allowedBlocks = [ 'core/heading', 'core/paragraph', 'core/button', 'core/list' ];
registerBlockType( 'wpboiler/column', {
apiVersion: 2,
title: __(
'Column',
'column'
),
description: __(
'Displays an individual column',
'column'
),
category: 'widgets',
icon: 'schedule',
supports: {
html: false,
},
parent: [ 'wpboiler-core/columns' ],
edit: function() {
return el(
'div',
useBlockProps(),
el(
'div',
{ className: 'column' },
el(
InnerBlocks,
{
allowedBlocks: allowedBlocks,
},
),
),
);
},
save: function() {
return el(
'div',
{ className: 'column' },
el(
InnerBlocks.Content, {},
),
);
},
} );
}(
window.wp
) );
However, doing it this way removes the need for the range control/columns select. But it does act in a similar (although not identical) way to the native columns block.
Again, other suggestions are welcomed.
You are correct in that you cannot have multiple <InnerBlocks> within a single block. Your best option is, as you suggest, to use two blocks: a columns wrapper block with a single <InnerBlocks> component that can only contain column blocks. Each column block can then have its own <InnerBlocks> component.
You will not need to loop over anything, since the <InnerBlocks> component will take care of rendering all the column blocks for you. Essentially you will have a columns block that outputs:
<InnerBlocks
allowedBlocks={ ['my/columns'] }
orientation="horizontal"
/>
Then your column block will just output:
<InnerBlocks/>
This is exactly how the WordPress core columns block works. I have also successfully used this myself to replace the built-in columns block.
Finally, although it seems like a lot of extra work to add a build step, I highly encourage it. It makes the code significantly easier to read and work with.
Related
I've been thinking about how to do this for days and if you could help me.
I expose you, I have followed the CKEditor 5 tutorial to the point of including the mentions, this is where my problem begins.
Following the tutorial we come to the part of the output of the mention, this as they do in the tutorial I have transformed it from <span> to <a> together with its class, its URL and its data. Well the editor shows it fine until you want to edit the post.
That is, imagine this message:
Hello world I am the first code of #undercover
Well when I include it in the database everything is correct, but when we return that same message to the editor it becomes:
Hello world I am the first code of #undercover
Investigating and as my Javascript is quite low I have been trying things.
The conversion. I've tried but there is something I can't understand and it's like passing the values to the function. Let me explain, when I pass that <a> that I save in the database, if I transform it into a <span> and then insert it if it tries to make the change to mention but the class attribute and the href attribute are "orphaned".
Well, I have 3 ideas and I can't do any of them at some point I get stuck, so I ask you for help.
My idea is to return the text I have in the database and the editor reads it fine.
Idea 1: Put the text in Javascript and identify and exchange the mentions that are in the database by the function of the mentions command, this is really complicated for me because it is very abstract, even so I am still looking for how to do it.
Idea 2: Save the value in the database in another way, this has been a last idea, how to search and put the of the mention but with the custom values. Even if {mention: {id: #undercover}} were saved in the database, I wouldn't care as long as it was later transformed correctly in the editor.
Idea 3: The use of conversions, I have managed to understand this and it has cost me that its function is to identify the mention within the editor and exchange it for the data you want. In this idea I can't understand how to pass the values other than manually, that is, how to pass the class and href attributes.
Here I leave you the section of the code, I hope you can give me a hand and thank you very much.
function MentionCustomization( editor ) {
// The upcast converter will convert <a class="mention" href="" data-user-id="">
// elements to the model 'mention' attribute.
editor.conversion.for( 'upcast' ).elementToAttribute( {
view: {
name: 'a',
key: 'data-mention',
classes: 'mention',
attributes: {
href: true,
'data-user-id': true,
}
},
model: {
key: 'mention',
value: viewItem => {
// The mention feature expects that the mention attribute value
// in the model is a plain object with a set of additional attributes.
// In order to create a proper object, use the toMentionAttribute helper method:
const mentionAttribute = editor.plugins.get( 'Mention' ).toMentionAttribute( viewItem, {
// Add any other properties that you need.
link: viewItem.getAttribute( 'href' ),
userId: viewItem.getAttribute( 'data-user-id' )
} );
return mentionAttribute;
}
},
converterPriority: 'high'
} );
// Downcast the model 'mention' text attribute to a view <a> element.
editor.conversion.for( 'downcast' ).attributeToElement( {
model: 'mention',
view: ( modelAttributeValue, { writer } ) => {
// Do not convert empty attributes (lack of value means no mention).
if ( !modelAttributeValue ) {
return;
}
return writer.createAttributeElement( 'a', {
class: 'group-color-'+modelAttributeValue.group,
'data-mention': modelAttributeValue.id,
// 'data-user-id': modelAttributeValue.userId,
'href': '/member/profile/'+modelAttributeValue.user_id,
}, {
// Make mention attribute to be wrapped by other attribute elements.
priority: 20,
// Prevent merging mentions together.
id: modelAttributeValue.uid,
} );
},
converterPriority: 'high'
} );
}
$.ajax({
type: "POST",
dataType: "json",
url: "/members/list_json",
success: function(info){
ClassicEditor
.create( document.querySelector( '#comment' ), {
extraPlugins: [ MentionCustomization ],
updateSourceElementOnDestroy: true,
language: 'es',
toolbar: [ 'bold', 'italic', '|' , 'link', '|', 'bulletedList'],
mention: {
feeds: [
{
marker: '#',
feed: getFeedItems,
minimumCharacters: 2,
itemRenderer: customItemRenderer,
}
]
}
} )
.then( editor => {
window.editor = editor;
/*
*/
} )
.catch( err => {
console.error( err.stack );
} );
let list_members = [];
for(let i = 0; i < info.length; i++){
var member = info[i];
list_members.push(member);
}
function getFeedItems( queryText ) {
return new Promise( resolve => {
setTimeout( () => {
const itemsToDisplay = list_members
.filter( isItemMatching )
.slice( 0, 10 );
resolve( itemsToDisplay );
}, 100 );
} );
function isItemMatching( item ) {
const searchString = queryText.toLowerCase();
return (
item.username.toLowerCase().includes( searchString )
);
}
}
},
});
function customItemRenderer( item ) {
const itemElement = document.createElement( 'span' );
const avatar = document.createElement( 'img' );
const userNameElement = document.createElement( 'span' );
itemElement.classList.add( 'mention__item');
avatar.src = `${ item.avatar }`;
avatar.classList.add('image-fluid', 'img-thumbnail', 'rounded-circle');
userNameElement.classList.add( 'mention__item__user-name' );
userNameElement.style.cssText = 'color: '+ item.group_color +';';
userNameElement.textContent = item.id;
itemElement.appendChild( avatar );
itemElement.appendChild( userNameElement );
return itemElement;
}
I am adding styles when registering my block:
styles: [
{ name: "my-style-1", label: "Style Name" }
{ name: "my-style-2", label: "Style Name 2" }
],
In the edit() and save() function how can I see which style/classname was selected?
I tried for example:
edit( { attributes, setAttributes, styles } ) {
const blockProps = useBlockProps();
const { quote, name, title } = attributes;
console.log(styles);
console.log(blockProps.styles);
...
But it returns undefined.
I need to use the styles for conditions for example...
if (style == 'my-style-1') {
// do something if style 1 was selected
}
The selected Block Style name as defined in your styles[{...}] is available in the edit() function as className:
edit({ attributes, setAttributes, className}) {
console.log(className);
...
}
I'd suggest if you want to reorder elements based on their style, create Block Styles and use CSS flexbox to manage the reordering, eg display:flex for your wrapper div and order: ... for the child elements (like <img> and <p>). By using styles, when the content is saved the underlying HTML markup doesn't change so less change of getting the dreaded 'block validation' error (plus you get a preview of the style in the Editor). Make sure to save blockProps in the save() so the selected class is applied, eg:
edit({ attributes, setAttributes, className }) {
const blockProps = useBlockProps();
console.log(className);
return (
<div {...blockProps}>
<h2>Hello</h2><img />
</div>
);
},
save({ attributes }) {
const blockProps = useBlockProps.save();
return (<div {...blockProps}><h2>Hello</h2><img /></div>)
}
The generated class applied to the <div> will be .wp-block-myblock-name .is-style-my-style-1
I would recommend you to use Block Variations instead of Block Styles. When creating a variation you can assign attribute values.
For example:
index.php
registerBlockType('xy/yourBlock', {
title: 'xy',
description: 'xy',
attributes: {
quote: {
type: 'string'
},
name: {
type: 'string'
},
title: {
type: 'string'
},
style: {
type: 'string'
}
},
variations: [
{
name: 'my-style-1',
isDefault: true,
title: 'Style Name',
attributes: { style: 'my-style-1' },
scope: 'transform',
},
{
name: 'my-style-2',
title: 'Style Name 2',
attributes: { style: 'my-style-2' },
scope: 'transform',
},
],
})
With scope: 'transform' you can select your variation in the block settings on the right side. Once a variation is selected you can access it in your edit and save file like any other attribute.
edit( { attributes, setAttributes } ) {
const { quote, name, title, style } = attributes;
console.log(style);
if (style == 'my-style-1') {
// do something if style 1 was selected
}
In the editor where the block is generated I create a wp.element, below is the code used.
edit: function( props ) {
function AddPolygons()
{
var coOrdInputvalue = props.attributes.polygonArrayViewAdd.length;
var i = (coOrdInputvalue/5);
coOrdlabel = wp.element.createElement("p", null, "Map point Co-Ords" );
coOrdtextbox = wp.element.createElement("input", { type: "text", value: "", onChange: onChangeMapPoint });
coOrdbutton = wp.element.createElement("button", {className: "buttonPolygonData", id:"mapPoint"+i, onClick: setActivePolygon}, "Set");
Quotelabel = wp.element.createElement("p", null, "Set Quotation");
Quotetextbox = wp.element.createElement("input", { type: "text", value: "", id:"Quote"+i, onChange: onChangeQuote });
var arrayAdded = [coOrdlabel, coOrdtextbox, coOrdbutton, Quotelabel, Quotetextbox];
polygonArrayViewAdd = polygonArrayViewAdd.concat(arrayAdded);
props.setAttributes({ polygonArrayViewAdd: polygonArrayViewAdd });
console.log("Add to view");
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
return wp.element.createElement(
wp.element.Fragment,
null,
wp.element.createElement(
InspectorControls,
null,
wp.element.createElement(PanelBody, {
title:'Poloygon List',
initialOpen: true
},
button = wp.element.createElement("button", {onClick: AddPolygons}, "Add Polygons"),
wp.element.createElement(
wp.editor.RichText, {
tagName: 'div',
id: 'polydiv',
key: 'editable',
value: props.attributes.polygonArrayViewAdd
}
),
button = wp.element.createElement("button", {onClick: ClearPolygons}, "Clear Polygons"),
)
),
wp.element.createElement('img', {src: props.attributes.mediaURL,style: {display:"none"}}),
);
},
The views are displayed in the RichText element, can I put the input boxes inside a RichText as im getting this error
react-dom.min.js?ver=16.13.1:32 Uncaught TypeError: s is not a function,
below is the block with the input fields been generated from clicking the add polygon button, the input boxes appear but don't allow text to be inputted and therefore saved.
All that needed to be changed was the RichText element to have more parameters to it
wp.element.createElement( wp.blockEditor.RichText, Object.assign( props, {
value: props.attributes.polygonArrayViewAdd,
onChange: function(content) {
var boxesCoOrdTextBox = document.getElementsByClassName("CoOrdTextBox");
},
onClick: function (event) {
console.log(event.target.id);
}
if(event.target.id.includes("mapPoint"))
{
props.setAttributes({ CurrentMap: event.target.id });
return;
}
},
} ) ),
I successfully created a PDF using a JavaScript plug-in (pdfmake) and it was great.
But when I try to render an ~8,000-row inventory/ledger printout, it freeze for over a minute.
This is how I usually declare my docDefinition
var docDefinition = {
pageOrientation: orientation,
footer: function(currentPage, pageCount) { return {text: currentPage.toString() + ' / ' + pageCount, fontSize:8, alignment:'center'}; },
content:[
printHeader,
{ fontSize: 8, alignment: 'right', style: 'tableExample',
table: {
widths: width,
headerRows: 1, body: arr },
layout: 'lightHorizontalLines' }] }
where
var printHeader = [ { text: 'COMPANY NAME',alignment:'center' },
{ text: 'Address 1',alignment:'center' },
{ text: 'Address 2',alignment:'center' },
{ text: 'Additional Details,alignment:'center' },
{ text: 'document title',alignment:'center' }];
and
var arr = [[{"text":"","alignment":"left"},"text":"Date","alignment":"left"},
{"text":"Trans #","alignment":"left"},{"text":"Description","alignment":"left"},
{"text":"Ref #","alignment":"left"},{"text":"Debit","alignment":"left"},
{"text":"Credit","alignment":"left"},{"text":"Amount","alignment":"left"},
{"text":"Balance","alignment":"left"}],[{"text":"ACCOUNT : Merchandise Inventory","alignment":"left","colSpan":8},"","","","","","","",
{"text":"1,646,101.06"}],["","10/13/2015","ST#0094",{"text":"","alignment":"left"},{"text":"","alignment":"left"},"546.94","0.00","546.94","1,646,648.00"],[{"text":"Total","alignment":"left","bold":true},"","","","",
{"text":"546.94","alignment":"right","bold":true},
{"text":"0.00","alignment":"right","bold":true},
{"text":"","alignment":"right","bold":true},
{"text":"546.94","alignment":"right","bold":true}],[{"text":"ACCOUNT : Accounts Payable-Main","alignment":"left","colSpan":8},"","","","","","","",
{"text":"-1,741,953.62"}],["","10/13/2015","ST#0094",
{"text":"","alignment":"left"},
{"text":"","alignment":"left"},"0.00","546.94","-546.94","-1,742,500.56"],
[{"text":"Total","alignment":"left","bold":true},"","","","",
{"text":"0.00","alignment":"right","bold":true},
{"text":"546.94","alignment":"right","bold":true},
{"text":"","alignment":"right","bold":true},
{"text":"-546.94","alignment":"right","bold":true}]
generated .
I searched about web workers and see that it can solve this UI freezing problem.
So I tried to create a web worker for it:
$('#makepdf').click(function(){
var worker = new Worker("<?php echo URL::to('/'); ?>/js/worker.js");
worker.addEventListener('message',function(e){
console.log('Worker said: ',e.data);
},false);
worker.postMessage(docDefinition);
//worker.js
self.addEventListener('message', function(e) {
self.postMessage(e.data);
}, false);
Output from console.log():
Worker said: Object {pageOrientation: "portrait", content: Array[7]}
its logging correctly the json structure.
So far so good.
But after I added pdfmake.min.js and vfs_font.js to the worker, I get the error Uncaught TypeError: Cannot read property 'createElementNS' of undefined.
I get the error before I even started using the worker.
Is it possible to implement web workers with the pdfmake plug-in?
Simple answer
Just provide a dummy constructor:
var document = { 'createElementNS': function(){ return {} } };
var window = this;
importScripts( 'pdfmake.min.js', 'vfs_fonts.js' );
Alternatively, if you think it is too dirty, import XML-JS (only 60k) and create a virtual document for pdfmake.
importScripts( 'tinyxmlw3cdom.js' );
var window = this;
var document = new DOMDocument( new DOMImplementation() );
importScripts( 'pdfmake.min.js', 'vfs_fonts.js' );
Explanation
pdfmake is known to be incompatible with worker.
By itself, pdfmake does not use createElementNS.
However its minified script pdfmake.min.js do, apparently to create a download link.
We don't need download link anyway, so just give it a dummy to keep it happy (for now).
If in the future it needs a real DOM,
bad news is document is unavailable in web worker.
Good news is we have a pure javascript implementation.
Download XML-JS,
extract and find tinyxmlw3cdom.js,
import it and you can create a functional document.
In addition to the document, since vfs_fonts.js get pdfmake through the window variable, we need to introduce window as an alias for global.
For me, these steps make pdfmake works in web worker.
To save the file, you need to convert the base64 data provided by pdfmake into a binary download.
A number of scripts are available, such as download.js.
In the code below I am using FileSaver.
Code
My worker is a Builder that can be cached.
If you compose the worker on server side,
you can get rid of the stack and build functions and directly call pdfmake,
making both js code much simpler.
Main HTML:
<script src='FileSaver.min.js'></script>
<script>
function base64ToBlob( base64, type ) {
var bytes = atob( base64 ), len = bytes.length;
var buffer = new ArrayBuffer( len ), view = new Uint8Array( buffer );
for ( var i=0 ; i < len ; i++ )
view[i] = bytes.charCodeAt(i) & 0xff;
return new Blob( [ buffer ], { type: type } );
}
//////////////////////////////////////////////////////////
var pdfworker = new Worker( 'worker.js' );
pdfworker.onmessage = function( evt ) {
// open( 'data:application/pdf;base64,' + evt.data.base64 ); // Popup PDF
saveAs( base64ToBlob( evt.data.base64, 'application/pdf' ), 'General Ledger.pdf' );
};
function pdf( action, data ) {
pdfworker.postMessage( { action: action, data: data } );
}
pdf( 'add', 'Hello WebWorker' );
pdf( 'add_table', { headerRows: 1 } );
pdf( 'add', [ 'First', 'Second', 'Third', 'The last one' ] );
pdf( 'add', [ { text: 'Bold value', bold: true }, 'Val 2', 'Val 3', 'Val 4' ] );
pdf( 'close_table' );
pdf( 'add', { text: 'This paragraph will have a bigger font', fontSize: 15 } );
pdf( 'gen_pdf' ); // Triggers onmessage when it is done
// Alternative, one-size-fit-all usage
pdf( 'set', { pageOrientation: 'landscape', footer: { text: 'copyright 2015', fontSize: 8, alignment:'center'}, content:[ "header", { fontSize: 8, alignment: 'right', table: { headerRows: 1, body: [[1,2,3],[4,5,6]] } }] } );
pdf( 'gen_pdf' );
</script>
Worker:
//importScripts( 'tinyxmlw3cdom.js' );
//var document = new DOMDocument( new DOMImplementation() );
var document = { 'createElementNS': function(){ return {} } };
var window = this;
importScripts( 'pdfmake.min.js', 'vfs_fonts.js' );
(function() { 'use strict';
var doc, current, context_stack;
function set ( data ) {
doc = data;
if ( ! doc.content ) doc.content = [];
current = doc.content;
context_stack = [ current ];
}
set( {} );
function add ( data ) {
current.push( data );
}
function add_table ( template ) {
if ( ! template ) template = {};
if ( ! template.table ) template = { table: template };
if ( ! template.table.body ) template.table.body = [];
current.push( template ); // Append table
push( template.table.body ); // Switch context to table body
}
function push ( data ) {
context_stack.push( current );
return current = data;
}
function pop () {
if ( context_stack.length <= 1 ) return console.warn( "Cannot close pdf root" );
context_stack.length -= 1;
return current = context_stack[ context_stack.length-1 ];
}
function gen_pdf() {
pdfMake.createPdf( doc ).getBase64( function( base64 ) {
postMessage( { action: 'gen_pdf', base64: base64 } );
} );
}
onmessage = function( evt ) {
var action = evt.data.action, data = evt.data.data;
switch ( action ) {
case 'set': set( data ); break;
case 'add': add( data ); break;
case 'add_table' : add_table( data ); break;
case 'close_table': pop(); break;
case 'gen_pdf': gen_pdf(); break;
}
};
})();
What is the apropriate aproach to setup a view in a Backbone.Marionete environment to have a list of subviews, without manually rendering them, and consume as least as possible memmory.
The view with child views is rendered based on a template, and is a part of a tab control tabs. The tamplete for the tab view has divs, which are used as a placholders for child controls ( two collection views and two helper controls )
Several aproaches I've made already:
1) Create view instances in render method and, attach them to a propper el hardcoding the selectors in render method.
2) Extend a marionete layout and declare a regions for each view.
var GoalsView = Marionette.Layout.extend({
template: '#goals-view-template',
regions: {
content: '#team-goals-content',
homeFilter: '#team-goals-home-filter',
awayFilter: '#team-goals-away-filter'
},
className: 'team-goals',
initialize: function () {
this.homeFilterView = new SwitchControlView({
left: { name: 'HOME', key: 'home' },
right: { name: 'ALL', key: 'all' },
});
this.awayFilterView = new SwitchControlView({
left: { name: 'AWAY', key: 'away' },
right: { name: 'ALL', key: 'all' },
});
this.сontentView = new GoalsCollecitonView({
collection: statsHandler.getGoalsPerTeam()
});
},
onShow: function () {
this.content.show(this.сontentView);
this.homeFilter.show(this.homeFilterView);
this.awayFilter.show(this.awayFilterView);
}
});
This is the cool way, but I am worried about the overhead for maintaing regions collection which will always display single view.
3) I extended marionette item view with the following logic:
var ControlsView = Marionette.ItemView.extend({
views: {},
onRender: function() {
this.bindUIElements();
for (var key in this.ui) {
var view = this.views[key];
if (view) {
var rendered = view.render().$el;
//if (rendered.is('div') && !rendered.attr('class') && !rendered.attr('id')) {
// rendered = rendered.children();
//}
this.ui[key].html(rendered);
}
}
}
});
Which allowed me to write following code
var AssistsView = ControlsView.extend({
template: '#assists-view-template',
className: 'team-assists',
ui: {
content: '#team-assists-content',
homeFilter: '#team-assists-home-filter',
awayFilter: '#team-assists-away-filter'
},
initialize: function () {
this.views = {};
this.views.homeFilter = new SwitchControlView({
left: { name: 'HOME', key: 'home' },
right: { name: 'ALL', key: 'all' },
});
this.views.awayFilter = new SwitchControlView({
left: { name: 'AWAY', key: 'away' },
right: { name: 'ALL', key: 'all' },
});
this.views.content = new AssistsCollecitonView({
collection: statsHandler.getAssistsPerTeam()
});
}
});
But it will leak memmory for sure, and I not feel like I will be able to write proper code to handle memmory leaks.
So in general, what I want, is to have a nice declarative way to create a view with other views as controls on it, with protection agains memmory leaks and least memmory consumption possible...
P.S. sorry for the wall of text
Why don't you simply use a layout and display your views within the layout's regions? You can see an example here: https://github.com/davidsulc/marionette-gentle-introduction/blob/master/assets/js/apps/contacts/list/list_controller.js#L43-L46