Vaadin:RichTextArea中的样式?

时间:2013-05-07 15:47:26

标签: vaadin

我想知道是否可以在RichTextArea字段的iframe上添加样式表或样式规则?

我需要对默认样式进行一些CSS调整,但我无法通过我的应用程序样式表定位RichTextArea,因为它是在iframe中加载的。

5 个答案:

答案 0 :(得分:1)

Vaadin RichTextArea 组件的“问题”不仅在于编辑器字段位于iframe元素内,而且与所有元素一样在其他 Vaadin 组件中,您还必须记住,DOM ready回调时(例如$(document).ready(function() {})如果使用jQuery或回调绑定,您的组件将不可用将执行DOMContentLoaded事件。

这是因为,正如您所知,当Vaadin应用程序启动时,您实际上还没有在DOM内部组件,但 vaadin bootstrap 进程将请求并处理渲染您的用户界面。这实际上是GWT也适用的原则(参见How does GWT provide the correct Javascript code to every browser e.g. to carry out i18n and browser compatibility?)(毕竟Vaadin基于GWT)。

所以,例如如果您使用jQuery,并且在 vaadinBootstrap.js 脚本加载并执行后立即加载了这样的脚本:

$(function() {
   // this code will execute, but no components are available yet.
   var rTa = $(".v-richtextarea"); // this won't select your Rich text area
   var len = rTa.length // len will be 0 here, as no element matches the previous selector because as stated before, there is not an element with such a class in the DOM yet.
});

执行此代码后,创建UI组件和布局的“重”过程开始,您的widgetset和/或默认的加载,然后您就可以设置漂亮的UI并准备好与之交互用户。

为了定制现有的组件,例如RichTextArea,例如将一个样式元素添加到其iframe元素的主体中,您当然可以冒险进入GWT的深度并像在答案中一样使用JSNI,但在我看来,还有另一种方法可以实现它,更紧凑,更简单,不需要使用JSNI。

您需要做的就是在客户端为您的组件实现一个带有连接器的JavaScriptExtension(您可以只扩展Vaadin的RichTextArea),看看这个简单的代码示例:

#!java
package com.package.example;

@JavaScript({"vaadin://js/src/rich_text_area_connector.js"})
public class RichTextAreaExtension extends AbstractJavaScriptExtension {

    @Override
    public void extend(AbstractClientConnector connector) {
        super.extend(connector);
    }

}

这是扩展,然后你需要创建客户端连接器,它基本上是一个JavaScript文件,其函数名称基于扩展名的包名,当然还有扩展名的类名:< / p>

#!javascript
com_package_example_RichTextAreaExtension = function() {
    var connectorParentId = this.getParentId();
    var element = this.getElement(parentId); // this is the rich text area element, which at this point is 

    // If you are using jQuery, then you can just select your element like so:
    var jQueryElement = $(element);
    // and do whatever you would normally do with the element like 
    // when you are inside $(document).ready(function() {});

    // or you can add a style element to the head element inside the iframe, doing something like the following:
    $(element).find("iframe").contents().find('head')
         .append('<link rel="stylesheet" href="./VAADIN/themes/your_theme/style_for_richtextarea_body.css" type="text/css" />');
}

你完成了。另一个好处是,您可以看到,您不必为不同的浏览器(Mozilla,Chrome,IE)编写不同的代码,您只需使用jQuery,库就可以为您处理兼容性。最后一部分是扩展组件本身。正如我之前所说,你可以扩展Vaadin的RichTextArea:

public class RichTextAreaWithStyleOnBody extends RichTextArea {

    public RichTextAreaWithStyleOnBody(String caption, Property<?> dataSource) {
        super(caption);
        if (dataSource != null) 
            setPropertyDataSource(dataSource);
        new RichTextAreaExtension().extend(this);
    }

    public RichTextAreaWithStyleOnBody(String caption, Property<?> dataSource) {
        this(caption, dataSource);
    }

    public RichTextAreaWithStyleOnBody(String caption) {
        this(caption, null);
    }

    public RichTextAreaWithStyleOnBody(Property<?> dataSource) {
        this(null, dataSource);
    }

    public RichTextAreaWithStyleOnBody() {
        this(null, null);
    }

}

请注意主构造函数中JavaScript扩展的用法。最后,您可以在布局中使用它,就像使用任何其他组件一样:

// Inside your UI's class
@Override
protected void init(VaadinRequest request) {
        VerticalLayout layout = new VerticalLayout();
        layout.setMargin(true);
        layout.setSpacing(true);
        setContent(layout);

        RichTextArea rTa = new RichTextAreaWithStyleOnBody("A rich text area with a styled body");
        rTa.setStyleName("myRichTextArea"); // you can do whatever you'll like on the server side just because your rich text area extends a Vaadin server side component.
        rTa.setSizeFull();

        layout.addComponent(rTa);
}

答案 1 :(得分:0)

据我所知,这是不可能的。我遇到了同样的问题,并根据RichTextArea的vaadin代码副本编写了一个小插件。我添加了一些其他方法来设置font-family和font-size。不幸的是,我最近没有足够的时间来清理我的代码,发布新版本的附加组件。

附加组件的主要功能是将工具栏与区域分离。 您可以在 v7 分支中找到代码:https://gitorious.org/richtexttoolbar-vaadin-addon/richtexttoolbar-vaadin-addon

答案 2 :(得分:0)

继续搜索后,我发现了这个讨论,这让我得到了一个解决方案: https://groups.google.com/forum/#!msg/Google-Web-Toolkit/9kwAJNhnamY/1MVfFFRq8tUJ

我最终包装RichTextImpl *类,从父类克隆initElement()方法,并插入这些行......

...对于Mozilla / Safari:

_this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document.designMode = 'On';
var doc = _this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document;
head=doc.getElementsByTagName('head')[0];
link=document.createElement('link');
link.setAttribute('rel',"stylesheet");
link.setAttribute('href',"/path/to/richtext.css" );
link.setAttribute('type',"text/css");
head.appendChild(link);

...对于IE:

  var ct = "<html><head><style>@import url('/path/to/richtext.css');</style></head><body CONTENTEDITABLE='true'></body></html>" ;
  doc.write( ct );

...在我的RichTextArea字段中加载样式表。

答案 3 :(得分:0)

嗨伙计们,我不知道是否仍然很重要,但我做了不同的事情。

我有一个为我的richtextarea创建的令牌函数,并且对于在数据库中保存的令牌,只需使用我已经在我的&#34; styles.css&#34;中保存的样式类来保存它。声明。

一步一步是这样的,我正在使用样式表并查找我的令牌类,然后我在iframes标头中添加样式,以便在iframe中设置我的标记类。

&#13;
&#13;
function styleRichtextareaIframe(id, themeClass, tokenClass, tokenSelectedClass) {
  setTimeout(function() {
    // iframe by id selected
    var $iframe = $("#" + id).find(".gwt-RichTextArea");

    var $head = $iframe.contents().find("head");
    var $body = $iframe.contents().find("body");

    var classNameToken = "." + themeClass + " " + "." + tokenClass;
    var classNameToken_selected = "." + themeClass + " " + "." + tokenSelectedClass;
    var fontClass = "." + themeClass + ".v-app, " + "." + themeClass + " " + ".v-window";

    var styleSheets = window.document.styleSheets;
    var styleSheetsLength = styleSheets.length;

    var style = document.createElement('style');
    style.type = 'text/css';
    for (var i = 0; i < styleSheetsLength; i++) {
      var classes = styleSheets[i].rules || styleSheets[i].cssRules;
      for (var x = 0; x < classes.length; x++) {
        if (classes[x].selectorText == classNameToken) {
          style.appendChild(document.createTextNode(getClassFor(classes[x])));
        }
        if (classes[x].selectorText == classNameToken_selected) {
          style.appendChild(document.createTextNode(getClassFor(classes[x])));
        }
        if (classes[x].selectorText == fontClass) {
          style.appendChild(document.createTextNode(getClassFor(classes[x])));
        }

      }
    }

    function getClassFor(classObj) {
      if (classObj.cssText) {
        return classObj.cssText;
      } else {
        return classObj.style.cssText;
      }
    }

    //Adding the classes also to the body
    //of dourse you can change the output string to just give you the style class that you need.
    $body.addClass("v-app " + themeClass);
    $head.append(style);
  }, 200);
}
&#13;
&#13;
&#13;

由于&#34;问题&#34;我不得不插入超时功能。使用Vaadin RichTextArea组件不仅是因为编辑器字段位于iframe元素内,而且与所有其他Vaadin组件一样,您还必须记住,当DOM准备好时,您的组件将不可用, (组件不在DOM中)。

希望这对某人有所帮助,抱歉我的英语不好。

干杯。

答案 4 :(得分:0)

我想出的最简单的方法是扩展RichTextArea组件并使用JavaScript设置自定义样式:

public class MyRichTextArea extends RichTextArea {

public MyRichTextArea(String className) {
    setStyleName(className);
    String js = "var iframeContainer = document.getElementsByClassName('" + className + "')[0];" +
                "var iframe = iframeContainer.getElementsByTagName('iframe')[0];" +
                "var iframeBody = iframe.contentDocument.getElementsByTagName('body')[0];" +
                "iframeBody.style.fontFamily='Arial';";
    JavaScript.getCurrent().execute(js);
}

}

用法:

MyRichTextArea field = new MyRichTextArea("fieldClassName");

请注意,所有主流浏览器都应支持 iframe.contentDocument ,但为了更好的支持,应添加其他调整 - 请参阅Getting the document object of an iframe