【问题标题】:Use Data Variable as Vue Template Without Parent Wrapper使用数据变量作为没有父包装器的 Vue 模板
【发布时间】:2019-12-29 18:07:46
【问题描述】:

我正在尝试将 Codemirror 实例嵌入为 Vue 组件,类似于在此 project 中完成的操作。但是,不是在父级中返回 Codemirror 实例的模板

<div class="vue-codemirror" :class="{ merge }">
    <!-- Textarea will be replaced by Codemirror instance. -->
    <textarea ref="textarea" :name="name" :placeholder="placeholder" v-else>
    </textarea>
</div>

我只是想单独返回 Codemirror 实例。我不能简单地删除父div并拥有的原因

<!-- Textarea will be replaced by Codemirror instance. -->
<textarea ref="textarea" :name="name" :placeholder="placeholder" v-else>
</textarea>

是因为替换textarea的方法CodeMirror.fromTextArea()需要textarea有一个parentNode。否则会遇到空错误。

幸运的是,有一种方法可以在不使用文本区域的情况下创建 Codemirror 实例,CodeMirror()。这个实例有一个函数getWrapperElement(),它返回DOM节点:

<div class="CodeMirror cm-s-default">
   ...
</div>

我想使用 Vue template/render 函数输出与 Codemirror 实例相关的特定 DOM 节点。我创建实例的当前方式是在组件data 对象中对其进行初始化。

data: function () {
    // Create a CodeMirror instance.
    let cm = new CodeMirror(null, {
        ...
    })
    return {
        // Define a CodeMirror instance for each CodeBlockView.
        cm : cm,
        ...
    }
},

更新 1:我找到了一种方法来做到这一点,尽管它非常像黑客。我们使用通过 render 传入的 createElement 函数,并将我们要渲染的 DOM 节点的 innerHTML 传递给数据对象参数。 它似乎在视觉上工作,但 CodeMirror 实例不可编辑。

render: function (createElement) {
    return createElement('div', {
        class: this.dom.classList.value,
        domProps: {innerHTML: this.dom.innerHTML}
    })
}

更新 2:然而,这并没有传入实际的 DOM 节点,而只是浅层地复制它;这带来了一个问题,因为它不允许它是可编辑的,也不允许附加一个 CodeMirror 实例。

【问题讨论】:

  • 看来您需要隐藏 textarea 而不是完全删除:&lt;textarea ref="textarea" :name="name" :placeholder="placeholder" v-show="!merge"&gt;&lt;/textarea&gt;
  • textarea 的删除是在 CodeMirror 库中完成的;我宁愿不必更改它的原始代码。有没有办法使用getWrapperElement中的DOM节点作为模板来渲染?
  • 使用渲染是一种错误的方式来做你想做的事。更喜欢使用 ref 而不是 vuejs.org/v2/api/#ref
  • 现在我有template&lt;textarea ref="textarea"&gt;&lt;/textarea&gt;,CodeMirror的实例化方法在mounted。根据我在vuejs.org/v2/guide/instance.html 上阅读的内容,似乎this.$refs.textarea.parentNode 在挂载时不应该为空,因为this.$refs.textarea 应该在实际的DOM 节点上。出于某种原因,这导致mountedcreated 之间的连续循环,导致我的浏览器窗口崩溃。

标签: vue.js codemirror


【解决方案1】:

创建组件后,您无法再更改模板。据我所知,它不是组件的属性,而是传递给构造函数的参数。

因此,您需要在创建组件之前创建 CodeMirror 实例并注入它。

但是,我没有看到包装组件的问题,您能解释一下原因吗?

【讨论】:

  • 注入实例是什么意思?我更喜欢无包装组件,因为我试图复制 prosemirror.net/examples/codemirror 中所做的事情,contenteditable="false"CodeMirror 在同一个 div 上,主要是为了避免以后写像 div.childElement.CodeMirror.focus() 这样的东西。我对 Vue 很陌生,但希望避免添加另一个 div 会很简单。
  • 嗯,我发现在mounted 中设置this.$el = this.CodeMirror.getWrapperElement() 似乎会替换textarea 并向外渲染。
  • 我不知道 $el 引用是可设置的!有趣的。但可能是用另一个元素替换 DOM 元素会在某处产生一些负面影响。但我不确定是否最好替换元素而不是通过 id/class 引用元素。
【解决方案2】:

我测试了很多方法,我发现最简单的模板是保留文本区域,之后组件应该接收选项和值并发出新值。

you can test the component here

我不是 codeMirror 专家,我导入了很多东西以使 codemirror 组件更加灵活,您可以根据需要调整导入。

//component codeMirror.vue
<template>
  <textarea ref="myCm"></textarea>
</template>

<script>
import CodeMirror from 'codemirror';

// language
import 'codemirror/mode/javascript/javascript';
// theme css
import 'codemirror/lib/codemirror.css';
import 'codemirror/theme/monokai.css';
// require active-line.js
import 'codemirror/addon/selection/active-line';
// styleSelectedText
import 'codemirror/addon/selection/mark-selection';
import 'codemirror/addon/search/searchcursor';
// hint
import 'codemirror/addon/hint/show-hint';
import 'codemirror/addon/hint/show-hint.css';
import 'codemirror/addon/hint/javascript-hint';
// highlightSelectionMatches
import 'codemirror/addon/scroll/annotatescrollbar';
import 'codemirror/addon/search/matchesonscrollbar';

import 'codemirror/addon/search/match-highlighter';
// keyMap
import 'codemirror/mode/clike/clike';
import 'codemirror/addon/edit/matchbrackets';
import 'codemirror/addon/comment/comment';
import 'codemirror/addon/dialog/dialog';
import 'codemirror/addon/dialog/dialog.css';

import 'codemirror/addon/search/search';
import 'codemirror/keymap/sublime';
// foldGutter
import 'codemirror/addon/fold/foldgutter.css';
import 'codemirror/addon/fold/brace-fold';
import 'codemirror/addon/fold/comment-fold';
import 'codemirror/addon/fold/foldcode';
import 'codemirror/addon/fold/foldgutter';
import 'codemirror/addon/fold/indent-fold';
import 'codemirror/addon/fold/markdown-fold';
import 'codemirror/addon/fold/xml-fold';

export default {
  name: 'codeMirror',
  props: {
    value: String, // give to the component a start value
    options: Object, // give the codeMirror options
  },
  mounted() {
    const myCodemirror = new CodeMirror.fromTextArea(this.$refs.myCm, this.options);
    myCodemirror.setValue(this.value);
    myCodemirror.on('change', (cm) => {
      if (this.$emit) {
        this.$emit('input', cm.getValue());
      }
    });
  },
};

</script>

之后很容易使用这个组件进行多实例渲染codeMirror,只读,恢复数据,多个模板,没有限制。例如:

<template>
  <div>
    Read Only, default theme, value multiple line
    <codeMirror :value="value0" :options="noChanges"></codeMirror>
    Value Dynamic, theme monokai, other extra fancy things
    <codeMirror :value="value" :options="monokai" @input="onCmCodeChange"></codeMirror>
    Only for debug, shows the modifications made inside codemirror
    {{ value }}
  </div>
</template>

<script>
// @ is an alias to /src
import codeMirror from '@/components/codeMirror.vue';

export default {
  name: 'pageEditor',
  components: {
    codeMirror,
  },
  data() {
    return {
      value0: `let choco: "bombon";
        let type: 'caramel;
        choco + '  ' + type;`,
      value: 'let frankie= "It\'s alive"',
      noChanges: {
        theme: 'default',
        readOnly: true,
        tabSize: 2,
        line: true,
        lineNumbers: true,
      },
      monokai: {
        tabSize: 2,
        styleActiveLine: true,
        lineNumbers: true,
        line: true,
        foldGutter: true,
        styleSelectedText: true,
        mode: 'text/javascript',
        keyMap: 'sublime',
        matchBrackets: true,
        showCursorWhenSelecting: true,
        theme: 'monokai',
        extraKeys: { Ctrl: 'autocomplete' },
        hintOptions: {
          completeSingle: false,
        },
      },

    };
  },
  methods: {
    onCmCodeChange(newValue) {
      this.value = newValue;
    },
  },
};
</script>

希望对你有帮助

【讨论】:

    猜你喜欢
    • 2021-04-04
    • 1970-01-01
    • 1970-01-01
    • 2020-07-31
    • 2018-01-23
    • 1970-01-01
    • 2013-11-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多