Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ namespace AutoCrudAdmin.Demo.SqlServer.Controllers;
using System.Linq;
using AutoCrudAdmin.Controllers;
using AutoCrudAdmin.Demo.Models.Models;
using AutoCrudAdmin.Attributes;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Mvc;

public class TasksController
: AutoCrudAdminController<Task>
Expand Down Expand Up @@ -37,6 +39,12 @@ protected override IEnumerable<string> DateTimeFormats
"MM/dd/yyyy hh:mm:ss tt",
};

[InjectScript("/js/tasks.js")]
public override IActionResult Index()
{
return base.Index();
}

protected override IQueryable<Task> ApplyIncludes(IQueryable<Task> set)
=> set.Include(x => x.Project);
}
4 changes: 4 additions & 0 deletions demo/AutoCrudAdmin.Demo.SqlServer/wwwroot/js/tasks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
(function(){
[...document.getElementsByTagName("td")]
.forEach((td,index)=> td.style.color = index % 2 === 0 ? "red" : "green")
})()
15 changes: 15 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,21 @@ protected override IEnumerable<string> HiddenFormControlNames {..}

See [API Reference](https://github.com/minkov/autocrudadmin/blob/master/src/AutoCrudAdmin/Controllers/AutoCrudAdminController.cs) for full customization options.

### Inject Scripts in endpoints

The InjectScript attribute allows modular JavaScript injection into views by specifying the script path in the controller action.

Usage example:
```csharp
[InjectScript("/js/tasks.js")]
public override IActionResult Index()
{
return base.Index();
}
```

See [Injecting Scripts](inject-script-attribute.md) for more details and options.

## Troubleshooting

Some common issues:
Expand Down
71 changes: 71 additions & 0 deletions docs/inject-script-attribute.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# The InjectScript attribute

The InjectScript attribute is an integral part of the AutoCrudAdmin framework, designed to enhance the EntityForm view by injecting custom JavaScript modules. This attribute allows developers to add client-side logic to the form without modifying the core files. It leverages the power of ASP.NET Core's MVC filters to inject the script automatically.

# Problem Statement

Customizing the behavior of an entity form usually requires modifications to the base HTML or JavaScript files, making it hard to manage and update. This traditional method is cumbersome, prone to errors, and not modular.

# Traditional Methods and Drawbacks

- Inline Scripting: Embedding JavaScript directly into the HTML. This is not modular and makes the HTML file bulky.

- Global Script Files: Adding custom logic in a global JavaScript file, making it hard to manage and leading to potential conflicts.

- Manual Injection: Manually adding `<script>` tags to each form, which is time-consuming and error-prone.

# Introducing InjectScript

The InjectScript attribute solves these issues by offering a way to inject a script module directly into the EntityForm view. This is done by adding the attribute to the controller action responsible for rendering the form.

## Syntax
```csharp
[InjectScript("/js/entities/users/edit.js")]
public override Task<IActionResult> Edit(IDictionary<string, string> complexId, string postEndpointName)
{
return base.Edit(complexId, postEndpointName);
}
```

## Parameters

- Script Path: The attribute takes a single parameter—the path to the JavaScript file to be injected. The path should start from the application's static files folder location.

- Correct: "/js/entities/manualJobs/create.js"

- Incorrect: "wwwroot/js/entities/manualJobs/create.js"

## Behavior

- Module Loading: The script is loaded as a JavaScript module, allowing for modular code.
- Scope: The script is scoped to the specific EntityForm where the attribute is used.

# How It Works

When the controller action is invoked, the attribute intercepts the call.

It then adds the script path to the `ViewData["AdditionalScriptPath"]` dictionary.

The EntityForm view checks this dictionary and injects the script accordingly.

# Usage Example

In EntityForm.cshtml:
```csharp
var additionalScriptPath = this.ViewData["AdditionalScriptPath"] as string;
```

# Real-World Scenario

Imagine you have an entity form where a user can select multiple options from a dropdown. You want to show or hide other form fields based on the user's selection. By using the InjectScript attribute, you can achieve this functionality without altering the EntityForm view.

# Future Outlook

The InjectScript attribute offers a modular approach to script injection, but its potential doesn't end there. Future implementations could include:

- Conditional script loading based on user roles or permissions.
- Support for injecting multiple scripts.

# Conclusion and Call to Action

The InjectScript attribute provides an elegant solution for enhancing entity forms in AutoCrudAdmin. It eliminates the need for cumbersome workarounds, making your development process more efficient. Consider using this attribute in your next project to experience its benefits firsthand.
52 changes: 52 additions & 0 deletions src/AutoCrudAdmin/Attributes/InjectScriptAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
namespace AutoCrudAdmin.Attributes
{
using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

/// <summary>
/// An attribute that injects a script into the EntityForm view.
/// The script is loaded as a module.
/// The script path starts from the currently running application static files folder location.
/// Correct value: "/js/entities/manualJobs/create.js".
/// Wrong value: "wwwroot/js/entities/manualJobs/create.js".
/// </summary>
public class InjectScriptAttribute : Attribute, IActionFilter
{
private readonly string scriptPath;

/// <summary>
/// Initializes a new instance of the <see cref="InjectScriptAttribute"/> class.
/// </summary>
/// <param name="scriptPath">The path to the script to inject.</param>
public InjectScriptAttribute(string scriptPath)
{
this.scriptPath = scriptPath;
}

/// <inheritdoc/>
public void OnActionExecuting(ActionExecutingContext context)
{
this.AddScriptToViewData(context);
}

/// <inheritdoc/>
public void OnActionExecuted(ActionExecutedContext context)
{
}

/// <summary>
/// Adds the script path to the view data. And the view will load the script as module.
/// </summary>
/// <param name="context">The action executing context.</param>
private void AddScriptToViewData(ActionExecutingContext context)
{
var controller = context.Controller as Controller;

if (controller?.ViewData != null)
{
controller.ViewData["AdditionalScriptPath"] = this.scriptPath;
}
}
}
}
7 changes: 6 additions & 1 deletion src/AutoCrudAdmin/Views/AutoCrudAdmin/EntityForm.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
@{
Layout = ViewBag.LayoutName;
var aspAction = Model.CustomActionName ?? $"Post{ViewContext.RouteData.Values["action"]}";
var additionalScriptPath = this.ViewData["AdditionalScriptPath"] as string;
}

<form enctype="multipart/form-data" asp-action="@aspAction" style="display: flex; flex-direction: column">
Expand All @@ -23,4 +24,8 @@
: $(`#${expandElementId}`).addClass('d-none');
});
});
</script>
</script>

@Html.Raw(additionalScriptPath != null
? $"<script type=\"module\" src=\"{additionalScriptPath}\"></script>"
: null)
5 changes: 5 additions & 0 deletions src/AutoCrudAdmin/Views/AutoCrudAdmin/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

@{
Layout = ViewBag.LayoutName;
var additionalScriptPath = this.ViewData["AdditionalScriptPath"] as string;
}

<!DOCTYPE html>
Expand Down Expand Up @@ -54,5 +55,9 @@
<script>
document.querySelectorAll(".mvc-grid").forEach(element => new MvcGrid(element));
</script>

@Html.Raw(additionalScriptPath != null
? $"<script type=\"module\" src=\"{additionalScriptPath}\"></script>"
: null)
</body>
</html>