I just implemented CodeMirror (with some plugins) as a JSF 2.0 composite component (see code below). This component is working just fine as long as it's not placed inside some tab or dialog (anything that is not visible just after the page is loaded). When in tab or dialog, it seems like component is rendered, but content and line numbers are not visible until the component is clicked.
Before click:
After click:
Here's my JSF composite component template code:
<!-- INTERFACE -->
<cc:interface>
<cc:attribute name="styleClass" type="java.lang.String" required="true" />
<cc:attribute name="value" type="java.lang.Object" />
<cc:attribute name="converter" type="java.lang.String" />
<cc:attribute name="mode" type="java.lang.String" />
<cc:attribute name="theme" type="java.lang.String" default="eclipse" />
<cc:attribute name="lineNumbers" type="java.lang.String" default="false" />
<cc:attribute name="matchBrackets" type="java.lang.String" default="false" />
<cc:attribute name="matchTags" type="java.lang.String" default="false" />
<cc:attribute name="closeBrackets" type="java.lang.String" default="false" />
<cc:attribute name="closeTag" type="java.lang.String" default="false" />
<cc:attribute name="activeLine" type="java.lang.String" default="false" />
<cc:attribute name="indentUnit" type="java.lang.Integer" default="2" />
<cc:attribute name="smartIndent" type="java.lang.Boolean" default="true" />
<cc:attribute name="readOnly" type="java.lang.Boolean" default="false" />
<cc:attribute name="electricChars" type="java.lang.Boolean" default="true" />
<cc:attribute name="lineWrapping" type="java.lang.Boolean" default="false" />
<cc:attribute name="firstLineNumber" type="java.lang.Integer" default="1" />
<cc:attribute name="tabIndex" type="java.lang.Integer" default="0" />
<cc:attribute name="undoDepth" type="java.lang.Integer" default="40" />
<cc:attribute name="pollInterval" type="java.lang.Integer" default="100" />
<cc:attribute name="extraKeys" type="java.lang.String" default="{}" />
</cc:interface>
<!-- IMPLEMENTATION -->
<cc:implementation>
<!-- required imports -->
<h:inputTextarea id="cmTextArea" value="#{cc.attrs.value}"
styleClass="#{cc.attrs.styleClass}">
<c:if test="#{not empty cc.attrs.converter}">
<f:converter converterId="#{cc.attrs.converter}" />
</c:if>
</h:inputTextarea>
<script type="text/javascript">
$(function() {
var cmTextArea = $('.#{cc.attrs.styleClass}'),
myCodeMirror = CodeMirror.fromTextArea(cmTextArea[0], {
value: cmTextArea.val(),
mode: '#{cc.attrs.mode}',
theme: '#{cc.attrs.theme}',
lineNumbers: #{cc.attrs.lineNumbers},
matchBrackets: '#{cc.attrs.matchBrackets}',
matchTags: {bothTags: #{cc.attrs.matchTags}},
autoCloseBrackets: #{cc.attrs.closeBrackets},
autoCloseTags: #{cc.attrs.closeTag},
styleActiveLine: #{cc.attrs.activeLine},
indentUnit: #{cc.attrs.indentUnit},
smartIndent: #{cc.attrs.smartIndent},
readOnly: #{cc.attrs.readOnly},
electricChars: #{cc.attrs.electricChars},
lineWrapping: #{cc.attrs.lineWrapping},
firstLineNumber: #{cc.attrs.firstLineNumber},
tabindex: #{cc.attrs.tabIndex},
undoDepth: #{cc.attrs.undoDepth},
pollInterval: #{cc.attrs.pollInterval},
extraKeys: #{cc.attrs.extraKeys}
});
});
</script>
Example of using composite component:
<t:codeMirror mode="text/xml" theme="cobalt"
value="#{bean.content}" tabIndex="1" lineNumbers="true"
converter="escapeConverter" electricChars="true" lineWrapping="true"
extraKeys="{ 'F11': function(cm) {cm.setOption('fullScreen', !cm.getOption('fullScreen'));}, 'Ctrl-J': 'toMatchingTag','Ctrl-Space': 'autocomplete' }"
matchBrackets="true" closeBrackets="true" matchTags="true"
closeTag="true" styleClass="sampleClass" />
As I said, I cannot understand, why there's no content displayed (immediately) in my component (without clicking on it), placed in a (second - invisible at page load) tab while component, placed outside Primefaces' tabView is rendered properly (with content and line numbers).
Any help would be much appreciated. Thanks!
EDIT:
I guess I need to call refresh
when codemirror object gets visible (on tab show). Great, it works that way, but is there any way to include that call in a composite component itself?
See CodeMirror's refresh
method. The editor needs to be visible in order to draw itself, and if you initialize it in a hidden element, you have to call refresh
the first time it becomes visible.