DavidVollmers / Ignis

The Blazor framework for building modern web applications.
https://ignis.dvolper.dev
MIT License
137 stars 8 forks source link

Unable to Build #13

Closed jkears closed 10 months ago

jkears commented 10 months ago

When I attempted to build the solution, I get the follow build exception ...

Severity Code Description Project File Line Suppression State Error Cannot find module 'tailwindcss/defaultTheme' Ignis.Website C:\NextWare\GitRepos\Ignis\website\Ignis.Website\EXEC 1

image

DavidVollmers commented 10 months ago

This is because you are missing some project setup you need to do before you can build the solution.

What you should do to sucessfully build the solution is:

In order to build the content of the Ignis.Website project you need to run the Ignis.Website.Generator project in the Tools solution folder, which transforms all markdown files in the docs folder into html files.

To generate the latest heroicons for the Ignis.Components.HeroIcons package you need to run the Ignis.Components.HeroIcons.Generator project in the Tools solution folder.

I hope this short guide helps you to get around in the repository. I will add detailed contribution guides for this project soon.

jkears commented 10 months ago

Thank you @DavidVollmers. I will close this issue.

Our UseCase to adoption of Ignis is derived from an internal App Builder we are creating to allow us to quickly author Blazor apps using a visual designer.

To that end, and as illustrated in the attached diagram, we are wrapping an number of 3rd party JavaScript components that will run as WASM components whilst the balance of the controls will run as ServerRendered interactive Blazor components, such as HTML Editor, Monaco (Code Editor), GraphiQL (GraphQL IDE).

Our framework further provides an ability to write method, function-call-back, and event handler logic in either JS or C# and in the later case we need to compile the edited script in the app so that we can invoke the script dynamically.
This can't be done in a full WASM app due to security constraints, so instead we will build those elements as Server based components, which has no issues with dynamic compilation.

We also created an extensive Dynamic Object framework, along with supporting services and use Roslyn to code-generate wrapper components over-arching any 3rd party (or in house) Blazor component library such that we generate a wrapper component that when invoked will register all public properties, methods, function call-backs and events with our binding services.
Every property or parameter of a method, function call-back or event are converted into Dynamic Objects and we use RxNet (Reactive UI) for binding changes across these dynamic properties/parameters.
We register these code-generated wrapper components as web-components within Blazor Server such that we can simply add that HTML web component tag into the the 3rd Party HTML editor, and Blazor will spin up the correct Blazor wrapper component.

Here is a high-level overview of the approach ...

image

Summing this all up, we are allowing the HTML Editor WASM component that wraps the JS HTML Editor, to target a div section in the Page Editor (Server-Side component) which works well, that is until Blazor wishes to re-render its content. To ensure we keep everything in sync, we plan to push the HTML doc that the Editor is controlling to the Page Editor, which we are thinking to use your Ignis framework to manage.

With this approach we can mix both static and interactive elements within our designer and preview will allow us to invoke GraphQl queries to acquire data from any GraphQL based end-point (we developed a separate DSL Modeling tool that captures any Domain Model following the key tenants of Domain Driven Design, and code-generates the underlying microservices which include GraphQL gateways that we can consume from within the designer).

Aside from controlling the rendering life-cycle using your framework, and without a ton of knowledge on how your framework works, it seems that your dynamic fragment would be a good fit as well.

I would appreciate any additional insight on your framework and whether you also feel the Fragments is a good fit to manage the HTML content within the Page Editor component, given the nature of the content (some static HTML, some interactive Blazor components, all being layout via the Page Editor).

Cheers John

DavidVollmers commented 10 months ago

So when it comes to rendering, Ignis provides more control on when this is done then just regular Blazor components. But keep in mind that Ignis components will still automatically render when a parameter is changing. (https://ignis.dvolper.dev/docs/components/lifecycle)

Every other change (e.g. events) will not proc a rerender (only if you call the Update method). Which in most cases is the biggest problem with complex Blazor components. For this I also recommend to look into the RenderSection component of the Ignis reactivity system (https://ignis.dvolper.dev/docs/components/reactivity). This will allow you to further control which section of a larger component are rendered when.

The dynamic component concept (https://ignis.dvolper.dev/docs/components/dynamic) is heavily inspired by reacts as property (https://www.robinwieruch.de/react-as-prop/). It allows to create components which render as a different component or element and you can still add attributes, events, captures and content to it as if you would build a static component. You can couple this with the Fragment component (which is inspired by react again, https://react.dev/reference/react/Fragment) to allow rendering a dynamic component without a root element.

This is boiled down in the Dynamic component coming from the Ignis.Components project, which allows you to render any element or component by supplying a string or type value to the AsElement or AsComponent parameter.

Reading your approach my biggest concern is that if you use the dynamic component concept, depending on the implementation, you will not know "what" (element or component) to wrap, since this is decided in runtime.

I hope this answers most questions.

jkears commented 10 months ago

This is wonderful, thank you for this community contribution and for further explanation of how it works!

My main issue is ensuring that the Server-Side Editor component, only renders when we know that it has to. That would be any time the user drops one of our registered web component (Blazor wrapper components) into the DOM as its then that we want Blazor to re-render that component.
To the HTML Editor it's a simple HTML web component tag, but Blazor will render that tag out to the equivalent Blazor component as is registered against that tag. This works very well.
Thus we only want Blazor to do that when that type of element is added into the DOM or repositioned by the HTML Editor. So my thought is that is when we set the set the Document Content parameter of our Page Editor to kick it to render, and have Blazor recognize and spin those web components up as a Server Side rendered Blazor component.
Once Blazor kicks that off, each wrapper component will register all of it's properties, methods, etc. with our existing Dynamic Framework which then allows us to dynamical bind to any property, method, func call-back and event on these wrapper components via our binding services.
When a property of a component is registered with our Property Binding service, under the covers we generate a reactive DynamicObject per each property.
This then allows us to bind other components to those properties (directly within the Page Builder tool), plus it we can write and execute Code Function scripted logic (JS via interop, or compiled C#).

We created a RxCommand which is a Reactive Command that defines optional input and return parameters also of type DynamicObject, plus an Execute or ExecuteAsync endpoint, that is also bindable which provides the ability to bind events and functions from other sources to serve as triggers to these Code Functions. We utilize our Property Binding service to further establish bind on any property to any other component's property or to an input/output parameter on a Code Function on any other component, all of which runs within the designer at runtime.

We also provide the ability to define DataSource endpoints for (GraphQL) as well as Create/Edit queries directly in the tool. Under the covers we generate a RxCommand that reflects it's types from the GraphQL Schema a reflected from the endpoint.

This makes any Query a fully bindable product. This for example might be paired with a Function Call-Back method from a Data Table component that passes the state of Paging, Searching and Order to said Query and the data is fetched and returned back to the Data Table component. This provides the user the ability to control the shape of the data and using generated binding paths, point to the specific data the wish to bind to a target component.

We will likely utilize a dumbed down version of our App Builder for our internal CMS capabilities however, we plan to code generate the App from the meta information as created within the designer, essentially providing a single design that is Reactive App that Reflows and we can select the type of components to generate (Server Side Static, Streaming, Server Interactive or Client Interactive) and whether to be Web, Native and/or PWA.

I will be integrating with Ignis today, so will let you know how it all works out for us.

Thanks again!

DavidVollmers commented 10 months ago

Sounds like a complicated but interesting project! Let me know if Ignis could help you and if not of course feel free to open up a new issue 😃

jkears commented 10 months ago

@DavidVollmers ... This works like a charm!

We ended up with the following design which allows for new web components to be inserted via the Page Editor, which in turn get spun up by Blazor. The HTML Editor is writing to the DIV element inside the Ignis Server-Side wrapper which is running as an Ignis Server component, which never updates, which is what we want, as such, Blazor does not wipe out any changes made by the HTML Editor.

image

The neat part is that in .Net 8 we are now able to mix Server and WASM components together, and thus when a web component tag is added to the Page Editor Canvas via the WASM HTML Editor, those web components are then automatically rendered as their server side component component equivalent's and as such we don't (currently) need to control life-cycle (so not Ignis based), and if we alter any property on those rendered components, they are updated by Blazor, and since they are inside the Ignis Server component, they live and breath as normal server-side Blazor components, thus not violating the rendering rules in Blazor .Net 8.

Although I am illustrating us using MUD components, these can be any Blazor, REACT, VUE or Web Component-based components, including Ignis.

We also tested modifying visual properties such as the Color property of a Mud Button component via our Property Binding Service and we were able to alter the color of that component via one-way bindings, which updated its color automatically.

It seems that it works even though the parent, Page Editor Canvas (Ignis managed), does not Update.

Thus our HTML Editor can relocate components within the Page Editor Canvas i.e. change layout, compose with static elements, and Blazor will update changes on the Blazor web components, when any of their state changes.

Another and more important aspect is that we were able to add/edit backing logic in the form of C# or JS script which is compiled into a dynamic Assembly, and bound to the Blazor components event/method or function call-back. This was/is only possible if it is running server side.

Similarly, we can dynamically compose GraphQL queries and execute those as remote functions.

In .Net 7 this only ran as a full WASM app which removed the ability to compile script into dynamic assemblies, so between the new Blazor rendering modes that are now available in .Net 8 RC1, and the Ignis framework, we think we have the perfect solution although there is still a lot of stuff to build & test, but so far this is all looking very promising!

Once again, thank you David for this wonderful contribution, it's awesome!

DavidVollmers commented 10 months ago

Thank you very much! I love to hear that Ignis is put to good use :)