slab / quill

Quill is a modern WYSIWYG editor built for compatibility and extensibility
https://quilljs.com
BSD 3-Clause "New" or "Revised" License
43.63k stars 3.39k forks source link

Sample to integrate into ASP MVC or Blazor #4294

Open papyr opened 4 months ago

papyr commented 4 months ago

Hello can you please help with a sample to integrate quill to replace tinymce in asp blazor or ASP mvc

Linkens commented 3 months ago

Use the .js webified libray with cdn :

{
  "library": "quill@2.0.2",
  "destination": "wwwroot/lib/quill/",
  "files": [
    "quill.js",
    "quill.snow.css"
  ]
},

Add That to your main file (.net 8.0 app.razor)

<html>
<head>

    <link rel="stylesheet" href="lib/quill/quill.snow.css">

</head>

<body>

    <script src="lib/quill/quill.js"></script>
    <script src="lib/FabricFunctions.js"></script>

</body>

</html>

In FabricFunctions.js create a function to use it

function InitializeQuill(quillElement){
        let options = {
            debug: 'info',
            modules: {
                toolbar: '#toolbar',
                history: {
                    delay: 2000,
                    maxStack: 500,
                },
            },
            placeholder: '...',
            readOnly: false,
            theme: 'snow'
        };
        let MainQuill = new Quill(quillElement, options);
}

And in your page

  <div @ref="divQuill">
  </div>
@code {
    protected ElementReference divQuill;
    [inject] IJSRuntime JS;
    protected async override Task OnInitializedAsync()
    {
      //Do Stuff
               StateHasChanged();
               await Task.Delay(100);
               await JS.InvokeVoidAsync("InitializeQuill",divQuill);
    }
}
papyr commented 2 months ago

hello i was trying to integrate into CMS, added a ref to quill maybe it will help others, I dont know how to make it a nuget package with options to add on..

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="8.0.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0" />
    <PackageReference Include="Quill" Version="1.3.7" />
  </ItemGroup>
</Project>

# model

namespace QuillIntegration.Models
{
    public class ContentModel
    {
        public int Id { get; set; }
        public string Content { get; set; }
    }
}

ContentController.cs

using Microsoft.AspNetCore.Mvc;
using QuillIntegration.Models;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using QuillIntegration.Data;

namespace QuillIntegration.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class ContentController : ControllerBase
    {
        private readonly QuillIntegrationContext _context;

        public ContentController(QuillIntegrationContext context)
        {
            _context = context;
        }

        // GET api/content
        [HttpGet]
        public async Task<ActionResult<IEnumerable<ContentModel>>> GetContents()
        {
            return await _context.ContentModels.ToListAsync();
        }

        // GET api/content/5
        [HttpGet("{id}")]
        public async Task<ActionResult<ContentModel>> GetContent(int id)
        {
            var content = await _context.ContentModels.FindAsync(id);

            if (content == null)
            {
                return NotFound();
            }

            return content;
        }

        // POST api/content
        [HttpPost]
        public async Task<ActionResult<ContentModel>> PostContent(ContentModel content)
        {
            _context.ContentModels.Add(content);
            await _context.SaveChangesAsync();

            return CreatedAtAction(nameof(GetContent), new { id = content.Id }, content);
        }

        // PUT api/content/5
        [HttpPut("{id}")]
        public async Task<IActionResult> PutContent(int id, ContentModel content)
        {
            if (id != content.Id)
            {
                return BadRequest();
            }

            _context.Entry(content).State = EntityState.Modified;

            try
            {
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!await ContentExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return NoContent();
        }

        // DELETE api/content/5
        [HttpDelete("{id}")]
        public async Task<IActionResult> DeleteContent(int id)
        {
            var content = await _context.ContentModels.FindAsync(id);

            if (content == null)
            {
                return NotFound();
            }

            _context.ContentModels.Remove(content);
            await _context.SaveChangesAsync();

            return NoContent();
        }

        private async Task<bool> ContentExists(int id)
        {
            return await _context.ContentModels.AnyAsync(e => e.Id == id);
        }
    }
}

using Microsoft.EntityFrameworkCore;
using QuillIntegration.Models;

namespace QuillIntegration.Data
{
    public class QuillIntegrationContext : DbContext
    {
        public QuillIntegrationContext(DbContextOptions<QuillIntegrationContext> options)
            : base(options)
        {
        }

        public DbSet<ContentModel> ContentModels { get; set; }
    }
}

my test view Views/Home/TestIndex.cshtml

how can I improve this to be rich editor?

@{
    ViewData["Title"] = "Home Page";
}

<div id="editor" style="height: 300px;"></div>

<script src="~/lib/quill/dist/quill.min.js"></script>
<script>
    var quill = new Quill('#editor', {
        modules: {
            toolbar: [
                [{ header: [1, 2, false] }],
                ['bold', 'italic', 'underline'],
                ['image', 'code-block']
            ]
        },
        placeholder: 'Type your content here...',
        theme: 'snow'
    });

    document.getElementById('save-button').addEventListener('click', function() {
        var content = quill.getContents();
        var contentString = JSON.stringify(content);
        fetch('/api/Content', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({ Content: contentString })
        })
        .then(response => response.json())
        .then(data => console.log(data))
        .catch(error => console.error('Error:', error));
    });
</script>

using my Startup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using QuillIntegration.Data;

namespace QuillIntegration
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddDbContext<QuillIntegrationContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseRouting();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}