'CodeMirror binding not working in Blazor App
I am working on a Blazor Server application that uses a code editor. For code editor I am using CodeMirror. The text area is rendering fine on the Blazor page but the data binding is not working with the corresponding C# field.
My _Host.cshtml file is as follows:
@page "/"
@namespace BlazorApp1.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
Layout = null;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>BlazorApp1</title>
<base href="~/" />
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
<link href="css/site.css" rel="stylesheet" />
<script src="~/codemirror/codemirror.js"></script>
<link rel="stylesheet" href="~/codemirror/codemirror.css">
<script src="~/codemirror/mode/clike.js"></script>
<script src="~/codemirror/addon/display/fullscreen.js"></script>
<link rel="stylesheet" href="~/codemirror/addon/display/fullscreen.css">
<link rel="stylesheet" href="/codemirror/theme/monokai.css">
<script>
function loadCodeEditor() {
var codemirrorEditor = CodeMirror.fromTextArea(document.getElementById('codearea'), {
lineNumbers: true,
styleActiveLine: true,
matchBrackets: true,
mode: "text/x-csharp",
theme: "monokai",
extraKeys: {
"F11": function (cm) {
cm.setOption("fullScreen", !cm.getOption("fullScreen"));
},
"Esc": function (cm) {
if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false);
}
}
});
codemirrorEditor.setSize(900, 300);
}
</script>
</head>
<body>
<app>
<component type="typeof(App)" render-mode="ServerPrerendered" />
</app>
<div id="blazor-error-ui">
<environment include="Staging,Production">
An error has occurred. This application may no longer respond until reloaded.
</environment>
<environment include="Development">
An unhandled exception has occurred. See browser dev tools for details.
</environment>
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.server.js"></script>
</body>
</html>
And the razor page code is as follows:
@page "/"
@inject IJSRuntime JSRuntime;
<h1>Hello, world!</h1>
Welcome to your new app.
<textarea id="codearea" rows="20" @bind="UserCodeInput" @bind:event="oninput" style="height:100px;"></textarea>
<SurveyPrompt Title="How is Blazor working for you?" />
Input Code: @UserCodeInput
@code{
public string UserCodeInput { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("loadCodeEditor");
}
}
}
If I remove id="codearea" from the textarea attribute and comment out the JSInterop method call then the binding works fine.
Please suggest on how to fix the binding with CodeMirror.
Solution 1:[1]
Finally I am able to bind the field with the CodeMirror editor. Thanks to below threads:
How to update c# value in Blazor using javascript?
I had to pass the DotNet object reference while invoking the JavaScript function and then in JavaScript function use the onchange event of CodeMirror to invoke the C# method and pass the value of the code editor.
Below are the updated files with the working code:
_Host.cshtml
@page "/"
@namespace BlazorApp1.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
Layout = null;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>BlazorApp1</title>
<base href="~/" />
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
<link href="css/site.css" rel="stylesheet" />
<script src="~/codemirror/codemirror.js"></script>
<link rel="stylesheet" href="~/codemirror/codemirror.css">
<script src="~/codemirror/mode/clike.js"></script>
<script src="~/codemirror/addon/display/fullscreen.js"></script>
<link rel="stylesheet" href="~/codemirror/addon/display/fullscreen.css">
<link rel="stylesheet" href="/codemirror/theme/cobalt.css">
<link rel="stylesheet" href="/codemirror/theme/monokai.css">
</head>
<body>
<app>
<component type="typeof(App)" render-mode="ServerPrerendered" />
</app>
<div id="blazor-error-ui">
<environment include="Staging,Production">
An error has occurred. This application may no longer respond until reloaded.
</environment>
<environment include="Development">
An unhandled exception has occurred. See browser dev tools for details.
</environment>
<a href="" class="reload">Reload</a>
<a class="dismiss">?</a>
</div>
<script src="_framework/blazor.server.js"></script>
<script>
function loadCodeEditor(dontNetObjRef) {
var codemirrorEditor = CodeMirror.fromTextArea(document.getElementById('codearea'), {
lineNumbers: true,
styleActiveLine: true,
matchBrackets: true,
mode: "text/x-csharp",
theme: "monokai",
extraKeys: {
"F11": function (cm) {
cm.setOption("fullScreen", !cm.getOption("fullScreen"));
},
"Esc": function (cm) {
if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false);
}
}
});
codemirrorEditor.setSize(900, 500);
codemirrorEditor.on("change", editor => {
dontNetObjRef.invokeMethodAsync("UpdateField", editor.getValue());
console.log(editor.getValue());
});
}
</script>
</body>
</html>
The razor page:
@page "/"
@inject IJSRuntime JSRuntime;
<h1>Hello, world!</h1>
Welcome to your new app.
<textarea id="codearea"></textarea>
<SurveyPrompt Title="How is Blazor working for you?" />
<br />
<br />
Input Code: @UserCodeInput
@code{
public string UserCodeInput { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("loadCodeEditor", DotNetObjectReference.Create(this));
}
}
[JSInvokable("UpdateField")]
public Task UpdateField(string codeValue)
{
UserCodeInput = codeValue;
StateHasChanged();
return Task.CompletedTask;
}
}
Solution 2:[2]
You have to trigger the change event on client side, this invokes the data binding of blazor server and you receive the input text of the editor in the blazor component. This way you need no interop from client to server side and relay on blazor defaults.
JavaScript - Client Side
window.initCodeMirror = function () {
var textArea = document.getElementById('codemirror');
var editor = CodeMirror.fromTextArea(textArea);
editor.on('change', function () {
textArea.innerHTML = editor.getValue();
textArea.dispatchEvent(new Event('change', { bubbles: true }));
});
}
Blazor-Component
@inject JsRuntime _jsRuntime
<textarea id="codemirror" @bind="editorText"></textarea>
@code {
private string editorText;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await _jsRuntime.InvokeVoidAsync("window.initCodeMirror");
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | raghavranjan |
| Solution 2 |
