Closed felinepc closed 3 years ago
Ref: INT-2589
Hi @felinepc
Configuration elements that are not functions can be created as c# objects and they will get parsed into JS objects. In your case for the formats
options, a string will be passed as a string instead of the actual object, so in order to achieve your goal you will have to create a similar object: E.G: (You can one line the whole thing or separate in as many parts)
private static Dictionary<string, object> alignleft = new Dictionary<string, object>{ { "selector", "p,h1,h2"}, {"styles", new Dictionary<string, object>{{"textAlign", "left"}}} };
private static Dictionary<string, object> formats = new Dictionary<string, object>{{"alignleft", alignleft}, {"alignright", alignleft}};
private Dictionary<string, object> conf = new Dictionary<string, object> { { "toolbar", "customButton | undo redo | bold italic"}, { "width", 400}, {"formats", formats} };
Using functions can be a bit more problematic. I didn't want to use eval
to pass parameters unless there was no other way around. I believe that you can build the necessary functions in the JS host. However, you can certainly use eval
to pass a configuration to your JS to setup a configuration before the editor loads.
window.evalConf = (conf) => {window.eval.conf = eval(conf);};
window.switchConf = (id) => {
window.switch.conf = page === 'x' { <conf 1> } : { <conf 2>};
};
And setup the configurations on init
@inject IJSRuntime JSRuntime
<TinyMCE.Blazor.Editor JsConfSrc="<evalConf | switchConf>" />
protected override Task OnInitializedAsync() {
JSRuntime.InvokeVoidAsync("evalConf", "<eval conf>");
JSRuntime.InvokeVoidAsync("switchConf", "x");
}
Let me know if this helps
@jscasca Thanks for the instructions. The array configuration now makes perfect sense, but I'm still having trouble with the function configuration, as I have very limited JS skills (hence going with Blazor haha).
Basically this is what I need:
I put together a custom image upload handler function
images_upload_handler: function custom_upload_handler(blobInfo, success, failure, progress)
{
//rest of uploading codes omitted
xhr.setRequestHeader('x-xsrf-token', my_xsrfToken)
}
As you can see, my uploader needs to set a xsrf token header, but that token needs to be passed in by Blazor at runtime (my Blazor components have an injected BlazorTokenProvider
which will have access to the token, i.e., BlazorTokenProvider.XsrfToken
).
Now if I put this uploader function in _Host.cshtml
and use JsConfSrc
, I don't know how I can pass in the token from TinyMCE.Blazor.Editor
.
Can you please help me out here? Thanks again!
@felinepc There are multiple ways to achieve this and since it's a very common use case I have been trying to put together some sample codes for people to get started. I think the easiest way to get this done is by handling the image_upload in the C# side. To do this you can expose a public function that you can consume from your JS image_upload_handler. E.G:
In your _host
: (You can pass multiple parameters, I only saved the blobInfo.blob()
)
const upload_handler = (blobInfo, success, failure, progress) => {
DotNet.invokeMethodAsync("SampleBlazorServer", "UploadHandler", <value that you want to save>).then((data) => {
success(data);
});
};
window.my = {
conf: {
toolbar: 'image',
plugins: 'image',
images_upload_handler: upload_handler,
...
And then, in your component:
private static Func<string, Task<string>> UploadHandlerAsync;
private Task<string> LocalUploadHandler(string val) {
// Handle the upload here.... and then return the new location
return Task.FromResult("https://i.imgur.com/GSGew1V.jpeg");
}
[JSInvokable]
public static Task<string> UploadHandler(string val) {
return UploadHandlerAsync.Invoke(val);
}
protected override Task OnInitializedAsync() {
UploadHandlerAsync = LocalUploadHandler;
return base.OnInitializedAsync();
}
Let me know if this helps
@jscasca Thanks I finally figured it out. I realized that in order to get something like this to work properly, I had to at least grasp the basics of Blazor JS Interop (something I hoped I'd never need to use myself hehe).
So after reading through the docs and then came back to your two solutions, I realized that I could just pass the variable to the _Host.cshtml
JS configuration via the InvokeVoidAsync
call (using your first approach, but no need for eval
), like this:
protected async override Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("setXsrfToken", BlazorTokenProvider.XsrfToken);
}
}
My _Host.cshtml
would look like this:
<script>
var xsrfToken = '';
window.setXsrfToken = (token) => { xsrfToken = token;};
window.tinyConf = {
images_upload_handler: //Use the token as needed in my uploader function etc
};
</script>
Now when I first saw your second approach, I thought it would be a great idea to call C# function directly from JS for upload logic (instead of using XHR to post to an upload endpoint). But upon further reading, I suspect it might not work well for my scenario because I'm using Blazor Server instead of WASM, and JS calls to invoke .NET methods are subject to message size limits which can be easily exceeded by file uploads.
Since I now have a fully functional setup (my JS upload handler posts to a Razor page protected by [Authorize] and XSRF token
in the same Blazor Server project), I have not given that "JS invoking .NET" method a try. Do you think the message size limit would be a problem here, or the blobInfo.Blob
passed over is merely a reference and won't actually carry the file data yet?
Glad you got it working. Yes, for the aforementioned approach you would need to chunk large images and handle sending those chunks over. By default it only allows 32Kb of data if I remember correctly. The wrapper uses chunking to send large pieces of content over 32K.
I will close the ticket but feel free to open a new case if you run into any bug or have feature requests.
The component exposes a
Conf
property of typeDictionary<string, object>
for init configuration. This works well for simple string/int/boolean properties such astoolbar
andheight
.But what if we want to pass configurations with array and function types such as:
and
I tried adding these as strings to the dictionary but they don't work.
I know the component also takes the
JsConfSrc
base configuration which can be done in plain js added to _Host.cshtml. But there are some specific configurations that I'd like to add dynamically based on the page the editor is used on, and therefore theConf
property needs to be used.Assistance with this would be greatly appreciated!