Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stubs for the classes for widget based on a model #22

Closed
fasteddys opened this issue Feb 10, 2023 · 5 comments
Closed

Stubs for the classes for widget based on a model #22

fasteddys opened this issue Feb 10, 2023 · 5 comments

Comments

@fasteddys
Copy link

Hello, this is a fav. lib of mine, I am trying to create an auto generate function that creates the classes for the widgets and the dashboard, can you share what are the classes I should be generating.

Or would it be better to have a base class with all the classes and relationships and derive from it?

@jdanylko
Copy link
Owner

jdanylko commented Feb 11, 2023

Since the widgets are simply ViewComponents, you have a couple of options:

  1. Create a BaseViewComponent (or BaseWidget) and using dependency injection, inject what you need into the component. For example, I have an Approval widget in my CMS for assets that need approval from other people. I automatically inject a service into the component to grab the approvals.
    [ViewComponent(Name="approval")]
    public class ApprovalViewComponent : ViewComponent
    {
        private readonly IApprovalService _approvalService;

        public ApprovalViewComponent(IApprovalService approvalService)
        {
            _approvalService = approvalService;
        }

        public async Task<IViewComponentResult> InvokeAsync(WidgetPlacement placement)
        {
            var approvals = await _approvalService.GetApprovalSummaryAsync();

            var model = new ApprovalModel
            {
                Placement = placement,
                Approvals = approvals.ToList()
            };

            foreach (var item in model.Approvals)
            {
                item.Uri = item.ContentName == "Assets" 
                    ? new Uri(Url.AssetsUrl(), UriKind.Relative) 
                    : new Uri(Url.ContentTypesUrl(item.ContentTypeId), UriKind.Relative);
            }

            return View("Default", model);
        }
    }

Once I have the data access class (IApprovalService), I can populate it as necessary. Your BaseWidget could inject everything you need for descendant widgets.

  1. Once you have a generic widget created, you could easily create a item template. In Visual Studio (proper, not Code), you can do "Project / Export Template..." for your BaseWidget class and then perform a File / New... and pick your BaseWidget from the list (Ref: How to: Create item templates )

  2. and there's always the "poor man's code generator", the T4. This could automatically create code for you based on a template? 5 Methods of Generating Code Using Microsoft Tools

I hope this helps.

@fasteddys
Copy link
Author

side note, wanted to share, but didn't want to do a PR for something incomplete, below is an approach I was taking to make it reusable for all the developers here, to allow the user to select a table and bind this as a widget

public class TableWidget : Widget
{
    private readonly ITableDataService _tableDataService;

    public TableWidget(ITableDataService tableDataService)
    {
        _tableDataService = tableDataService;
    }

    public override void Build()
    {
        var tableData = _tableDataService.GetData();

        var table = new HtmlTable();
        table.AddHeaderRow(new HtmlTableRow("Id", "Column1", "Column2", "Column3"));

        foreach (var data in tableData)
        {
            table.AddBodyRow(new HtmlTableRow(data.Id.ToString(), data.Column1, data.Column2, data.Column3));
        }

        Content = table.ToHtml();
    }
}

then in the controller

public class TableWidgetController : WidgetController
{
    private readonly ITableDataService _tableDataService;

    public TableWidgetController(ITableDataService tableDataService)
    {
        _tableDataService = tableDataService;
    }

    public IActionResult Index(int id)
    {
        var widget = new TableWidget(_tableDataService);
        widget.Build();

        var model = new TableWidgetModel
        {
            Id = id,
            Title = "Table Widget",
            Content = widget.Content
        };

        return View(model);
    }
}

is there a better way to do this generically on any table, since you are the architect here :)

@jdanylko
Copy link
Owner

If your data is generic enough, I would encapsulate it more at the ViewComponent level.

In your ViewComponent (Widget?), I would pass the data into the ViewComponent's "Default.cshtml" View. I like to do things declaratively as opposed to writing code to create a UI (like the HTMLTable) If I need to make a UI change, I like to make it without re-compiling. ;-)

public class TableWidget : Widget
{
    private readonly ITableDataService _tableDataService;

    public TableWidget(ITableDataService tableDataService)
    {
        _tableDataService = tableDataService;
    }

    public IViewComponentResult Invoke()
    {
        return this.WidgetView(new TableWidgetModel{ Content = _tableDataService.GetData() } );
    }
}

In the Default.cshtml, add the Model.

@model TableWidgetModel

And then create your table using HTML.

One example

I was told a while ago that ViewComponents are like "mini-controllers." Yes, you can pass things into them, but it makes more sense to contain everything in each ViewComponent.

So your Controller can be whittled down to a View instead of passing data. Your Controller's View will have your markup and a ViewComponent widget:

<div class="widget">
   <vc:TableWidget />
</div>

I hope this helps and thank you so much for the support.

The references I've been using for building ViewComponents for Tuxboard:

@fasteddys
Copy link
Author

Amazing man, its been a learning curve generally by playing around with your framework, so a big 🤟 thanks.
I was looking at metabase , https://github.com/Smashing/smashing , orchard (OSS) etc or Stimul s0ft dashboard (back when had community ver), they don't have a clear or consistent concept of widgets and the relationship dashboard. So, I like this better its simpler and native.

Maybe just me, but I get confused by which to use, VC, Tag Helper, Partials.. and every time they introduce a new concept.
Another common problem, on how to restrict data visibility inside a widget. . There must be an easy way and repeatable way, my friend and I looked at your site its cool, lot of good nuggets, we have seen it before, its actually known in the asp community of developers. Sorry, I was saying, if I have to put in based on roles or should I use claims, that confuses on how do you lock down visibility of data to area specific to data. Here is common example, for e.g. if we had two managers, who are in different countries/depts/stores. They should not be able to see budgets, salaries across. I was building a website for a family off-grid dairy farm, who had different prices for regular customers, walk in, store, and bulk. It was not easy for me here..

I was going to write a small collection and push a PR on a few items, tables, charts, graphs to your

@jdanylko
Copy link
Owner

Awesome! Glad to hear it's coming together for you.

For permissions, I use Microsoft Identity to identify user types coming into the system and what widgets they can add/see/use onto a dashboard.

I've even adapted a similar strategy for any type of menu system using Microsoft Identity; What menu items can you show/hide based on a user's role.

If you look at the HomeController.cs for the "kitchen sink" demo, you'll see a GetCurrentUser() to retrieve the current user logged in. Once you know who this is, you can read the tables and their roles. This allows you to pull certain widgets based on roles. This is where the database tables WidgetPlan, DashboardDefault, and Dashboard tables come into play. They can be renamed to WidgetRoles for roles instead, but with a couple of projects I wrote in the past, they used User Plans (like multi-tier subscriptions).

Hope this helps. :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants