microsoft / monaco-editor

A browser based code editor
https://microsoft.github.io/monaco-editor/
MIT License
40.43k stars 3.6k forks source link

Monaco Editor doesn't display automatically in hidden containers such as `tabs` #2294

Closed summercms closed 3 years ago

summercms commented 3 years ago

Using the latest version v0.21.2 but this issue has been happening from day one.

Using a Windows 10, browser Canary to test.

When you add Monaco Editor to a tab, such as tab 2 and load the page from say tab 1 and go to tab 2 the editor is blank.

We have been using a hack method by resizing the editor after the page loads in the tab to force Monaco editor to be displayed in the tab.

It would be nice if Monaco Editor would work out-of-the-box when being displayed in a tab (or any other hidden container), when the page gets loaded.

To reproduce the bug, just add the Monaco editor inside a html tab e.g. tab 2. Then load the page from tab 1 and go to tab 2 and you will see the Monaco editor being blank and not displaying the code inside. The only way to force the code is using a resize hack.

For example:

image

Then force it to be displayed using the resize hack:

image

alexdima commented 3 years ago

@ayumi-cloud Do you have a standalone repro case for the bug that you have discovered? For example, this website uses tabs to render editors and it works fine: https://microsoft.github.io/monaco-editor/playground.html

image

summercms commented 3 years ago

@alexdima example code.

<!DOCTYPE html>
<html>
    <head>
        <title>browser-amd-editor</title>
        <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
    </head>
    <body>
        <div id="exTab3" class="container">
            <ul class="nav nav-pills">
                <li class="active"><a href="#1b" data-toggle="tab">Editor 1</a></li>
                <li><a href="#2b" data-toggle="tab">Editor 2</a></li>
            </ul>

            <div class="tab-content clearfix">
                <div class="tab-pane active" id="1b">
                    <div id="container1" style="width: 800px; height: 600px; border: 1px solid grey;"></div>
                </div>
                <div class="tab-pane" id="2b">
                    <div id="container2" style="width: 800px; height: 600px; border: 1px solid grey;"></div>
                </div>
            </div>
        </div>

        <script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.22.3/min/vs/loader.js"></script>

        <script>
            require.config({ paths: { vs: "https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.22.3/min/vs" } });

            // Instance 1
            require(["vs/editor/editor.main"], function () {
                var editor1 = monaco.editor.create(document.getElementById("container1"), {
                    value: ["function x() {", '\tconsole.log("Hello world!");', "}"].join("\n"),
                    language: "javascript",
                });
            });

            // Instance 2
            require(["vs/editor/editor.main"], function () {
                var editor2 = monaco.editor.create(document.getElementById("container2"), {
                    value: ["function x() {", '\tconsole.log("Hello world!");', "}"].join("\n"),
                    language: "javascript",
                });
            });
        </script>

        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" />
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
    </body>
</html>
alexdima commented 3 years ago

@ayumi-cloud Normally, to implement tabs, I would recommend to use a single editor instance and just instantiate two models and then call setModel to change the model as the tabs are changed. But for your sample, if you really want to have two editor instances, it looks like the second instance is instantiated at a time when its container has a width x height = 0 x 0. The editor will read its container size when being instantiated and then will remember that. When the container size is changed, you need to call editor.layout() to tell the editor to read again the size of the container. If you don't want to do that, then you can use the option automaticLayout: true and the editor will use some DOM observer to scan the container size, but this has a perf implication cost (that is why it is not the default).

So a working sample:

<!DOCTYPE html>
<html>
    <head>
        <title>browser-amd-editor</title>
        <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
    </head>
    <body>
        <div id="exTab3" class="container">
            <ul class="nav nav-pills">
                <li class="active"><a href="#1b" data-toggle="tab">Editor 1</a></li>
                <li><a href="#2b" data-toggle="tab">Editor 2</a></li>
            </ul>

            <div class="tab-content clearfix">
                <div class="tab-pane active" id="1b">
                    <div id="container1" style="width: 800px; height: 600px; border: 1px solid grey;"></div>
                </div>
                <div class="tab-pane" id="2b">
                    <div id="container2" style="width: 800px; height: 600px; border: 1px solid grey;"></div>
                </div>
            </div>
        </div>

        <script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.22.3/min/vs/loader.js"></script>

        <script>
            require.config({ paths: { vs: "https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.22.3/min/vs" } });

            // Instance 1
            require(["vs/editor/editor.main"], function () {
                var editor1 = monaco.editor.create(document.getElementById("container1"), {
                    value: ["function x() {", '\tconsole.log("Hello world!");', "}"].join("\n"),
                    language: "javascript",
                    automaticLayout: true
                });
            });

            // Instance 2
            require(["vs/editor/editor.main"], function () {
                var editor2 = monaco.editor.create(document.getElementById("container2"), {
                    value: ["function x() {", '\tconsole.log("Hello world!");', "}"].join("\n"),
                    language: "javascript",
                    automaticLayout: true
                });
            });
        </script>

        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" />
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
    </body>
</html>
summercms commented 3 years ago

@alexdima Perfect, thanks for listing all the various options! 👍

Normally, to implement tabs, I would recommend to use a single editor instance and just instantiate two models and then call setModel to change the model as the tabs are changed

Will follow your advice and refactor the code.