Developer forum

Forum » Dynamicweb 10 » Limits of extending

Limits of extending

August Schnell
Reply

Hi DW

I would like to develop an App that extends some current form functionality.

On the form paragraph i would like a dropdown with some options.


On the form paragraph itself i would like a dropdown with some options.

And on specific form fields, i would like an extra text field.




Obviously i would like to have access to the data that has been written/chosen in these new fields somehow. E.g in OnAfterSubmitSave subscriber. 

Is this even possible at the moment in DW10?

BR August


Replies

 
Nicolai Pedersen Dynamicweb Employee
Nicolai Pedersen
Reply

Hi August

To some degree it is possible, but not fully documented I guess.

You can manipulate a screen using Screen Injectors.

You want to change the content of an edit screen - you can use an EditScreenInjector for that: https://doc.dynamicweb.dev/api/Dynamicweb.CoreUI.Screens.EditScreenInjector-2.html

Here is an example injecting on an overview screen: https://github.com/dynamicweb/Summit2022_ExtendingTheUI/blob/main/src/ExpressDelivery/Injectors/OrderOverviewInjector.cs

You can get information of the screen types and elements here: https://doc.dynamicweb.dev/documentation/extending/administration-ui/screentypes.html

So to inject your own editor into edit field screen, you need to create an EditScreenInjector for the FieldDataModel so that means you have to implement an injector for that: 
public sealed class MyEditFormFieldInjector: EditScreenInjector<FieldEditScreen, FieldDataModel>

BR Nicolai

 
Jeppe Eriksson Agger Dynamicweb Employee
Jeppe Eriksson Agger
Reply

Hi August,

From your description, it hard for me to tell what you're actually trying to achieve, but I'll take a guess and you can inform me if I'm mistaken 🙂

I would guess, you want to set a value on a paragraph and somehow tie that together with a field on a form? Couldn't that be solved with items and a template? You'd create (or customize) an item type that can be use on a paragraph to add your field. You'd then extend (or create) the form with a new hidden field and your new text field. You create (or change) the template to read the value from the item and set it as the value of the hidden field. The value is now passed from the paragraph to the form with the text field using no custom code (other than the template).

You don't need an app for that, except if you want to show/manage the data, like having a list of all forms with this value or something along those lines. You could create a new list screen or overview to show these.

To not write a long essay about creating screens since I'm guessing that the situation you're trying to solve, I won't go too far down that path. If you want to know more about that or it's what you're looking for, please let me know.

- Jeppe

 
August Schnell
Reply

Hi Jeppe

Actually i just want on extra text field on the form field itself

And a new dropdown on the form with some predetermined options. I don't wan't on the paragraph itself, apologize for the confusion.

The idea is that in the dropdown i can choose a mail system e.g ActiveCampaign or mailchimp and so on.
And in the text field i write the id of the corresponding field in ActiveCampaign or mailchimp in order to be able to map it correctly.

Knowing which system is selected changes the way the form data gets handled when using OnAfterSubmitSave.

The reason i want to add this functionality to an app is that want to the ability to reuse the functionality more easily in other projects. 

Makes sense?

 

 
Jeppe Eriksson Agger Dynamicweb Employee
Jeppe Eriksson Agger
Reply

Hi August,

Now I understand what you're trying to accomplish. Unfortunately, it's not easily doable.

You can easily extend the screen to include those fields, but the main issue is the model being edited. There currently isn't a way to extend it, meaning the data would be lost when sent to the server. In addition, even if you could extend the model, there's no way to intercept the command to save so you wouldn't be able to save the data in your custom fields.

If you don't need it to be part of the form edit screen, you can create your own relation as something separate. In fact, it would be something very similar to this example project: https://github.com/dynamicweb/Summit2022_ExtendingTheUI. Here, we're creating a separate entity called ExpressDelivery which is related to an order. You can see examples on how to create the edit and list screens, the models and queries, and the commands necessary to work with custom entities, even updates to the database for storing the entities. You can also see how to extend an existing tree to add tree nodes to link to your screens. Be aware, this project doesn't compile against newer versions of DynamicWeb 10, but you can use it as inspiration.

I hope that helps.

- Jeppe

 
August Schnell
Reply

Hi Jeppe

The ability to save the data is obviously important, is this something that will be possible in the future? 

Also what can injections be used for in that case, other than having some non saveble fields in the frontend, when not creating a separate entity?

BR August

 
Jeppe Eriksson Agger Dynamicweb Employee
Jeppe Eriksson Agger
Reply

Hi August,

Well, I'm not necessarily sure I agree that it should be possible to extend the existing models. They are tightly coupled to the domain models that -- for the most part -- cannot be extended either. You could argue that it could be useful to piggyback on the edit screen and save command to keep editing simpler for the user by having standard and custom fields in the same screen, and I would be inclined to agree. I cannot guarantee that something like this will make it in, but we will discuss it internally. You can, however, accomplish a lot of that already in a slightly different manner.

Since it's not possible to extend the Form model or the Form entity, you have to create the campaign relation separately anyway. That means you create a new table in the database to store the data, a new domain API if you need to work with these outside the UI, a data model for the UI along with queries and commands to fetch and persist them. Also, you need to create screens for the data model (at least an edit screen). Then you create an injector that adds a button on the form edit screen that opens a slideover with your relation edit screen or shows a prompt asking which campaign to associate with the form. Since the relation needs an existing form, you can choose to only show the button once the form has been saved. You can choose if you want the relation to be at the top of the page next to the Actions menu or if you want it further down the page near a specific field.

As mentioned in my previous post, you can see a very similar example of that in the project that I linked to. Particularly the injector for OrderOverviewScreen where a button to relate an order to an express delivery setting is added.

- Jeppe

 
August Schnell
Reply

Hi Jeppe

 

I've tried using the example you're refering to get something working.

public sealed class OrderOverviewInjector : ScreenInjector<FormSettingsEditScreen>
{
    public override void OnAfter(FormSettingsEditScreen screen, UiComponentBase content)
    {
     
        if (content is ScreenLayout layout && layout.Root is Form form)
        {
            if (form.Content is TabContainer tabContainer)
            {
                tabContainer.Tabs.ToList()[0].Section.Groups.ToList()[0].Name = "Test";
                var tab2 = tabContainer.Tabs.ToList()[0].Section.Groups.ToList()[1];
                
                tab2.WithComponent(new Dynamicweb.CoreUI.Displays.ShowScreen()
                {
                    Load = Dynamicweb.CoreUI.Displays.ShowScreen.LoadMethod.Async,
                    Value = typeof(TestScreen)
                });

                
            }
        }
    }
 
}

I'm currently getting this error: 


Where should my view be located? I have tried in the places that first came to mind, in "Website" root folder, inside wwwroot, inside Files and also inside my extensibility projects root folder. 
So something like this:
\SolutionName\Webiste\Views\Shared\Components\UiComponentWrapper\ExpressDelivery.Components.TestView.cshtml

I havent been able to find documentation regarding this.

I'm aware of the fact that I don't have a query at the moment, but i just want to get something working for a start.

BR August

 
Jeppe Eriksson Agger Dynamicweb Employee
Jeppe Eriksson Agger
Reply

Hi August,

Are you using any custom components other than a screen injector? If not, you shouldn't have to worry about views.

If you are using a custom component, there are a few things you need to make sure of.

  1. The project file is set to include Razor views and it's set to generate a manifest file
    • I've included the updated csproj file below
      You can adjust the ring version to the one that you need: Ring1, Ring2, Ring3 or Ring4
  2. The view is in the correct folder structure: <root of project>/Views/Shared/Components/UiComponentWrapper
  3. The view is named after the component in the correct way: <Name with namespace>.cshtml
    • If the component is MyProject.Components.MyCustomComponent.cs, the file name must be MyProject.Components.MyCustomComponent.cshtml
      Full path: <root of project>/Views/Shared/Components/UiComponentWrapper/MyProject.Components.MyCustomComponent.cshtml
      I know it's a very long and verbose path and it's something I'd like to change, but it has to be like that for now
  4. Any static files you need to include, such as JavaScript or CSS, should go into the wwwroot folder -- it's up to you how you want to structure it from there
  5. Ensure that you're implementing the IRenderingBundle just as the RenderingBundle.cs in the root of the existing project

It seems you're trying to add components to the Form edit screen directly, as you previously mentioned. This will not work due to the issues discussed above. Instead, you should consider adding a button to the FormSettingsEditScreen. If you're trying this out to see how it works with extending screens and creating custom components, I'm 100% for that; I just want to make sure that you're not going down a path that cannot lead to success.

To add a button to the screen, find the layout similar to what you already do. Then call

layout.AddAction(new()
{
    Name = "Add campaign",
    Icon = Dynamicweb.CoreUI.Icons.Icon.PlusSquare,
    NodeAction = OpenSlideOverAction
        .To<CampaignSelectPromptScreen>()
        .With(new CampaignAttachQuery() { FormId = screen?.Model?.Id })
});

 

On the CampaignSelectPromptScreen, you create reference a command that can create or update the relation between the form and the campaign. Let me know if you need more information on this.

- Jeppe

 

Updated csproj file:

<Project Sdk="Microsoft.NET.Sdk.Razor">
 
    <PropertyGroup>
        <TargetFramework>net8.0</TargetFramework>
        <Nullable>enable</Nullable>
        <ImplicitUsings>enable</ImplicitUsings>
        <AddRazorSupportForMvc>true</AddRazorSupportForMvc>
        <GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
    </PropertyGroup>
 
    <ItemGroup>
        <EmbeddedResource Include="wwwroot/**/*" />
    </ItemGroup>
 
    <ItemGroup>
        <FrameworkReference Include="Microsoft.AspNetCore.App" />
    </ItemGroup>
 
    <ItemGroup>
        <PackageReference Include="Dynamicweb.Suite.Ring1" Version="*" />
        <PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.8" />
        <PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="8.0.8" />
    </ItemGroup>
 
</Project>
 
August Schnell
Reply

Hi Jeppe

Thanks for that, i have managed to add the button to the screen, and also parsing the form id using the query,

However when pressing my "Add campaign" button, i get the following js error in the console:

Helpers.js:802 TypeError: Cannot read properties of undefined (reading 'bind')
    at OpenSlideOver (Actions.js:423:84)
    at async Object.DWActionHandler (Helpers.js:800:17)
    at async HTMLDocument.<anonymous> (Shell.ts:43:13)

It's worth noting that the html inside my view actually gets added to the dom in overlay-container when pressing the button, but does not actually open a "SliderOver"

I am able to open the the view with the following link:

/Admin/UI/Apps/CampaignSelector?FormId=1&Type=CampaignAttach&ScreenType=slideOver

As i said my formid gets parsed as hoped, since i'm able to output it and see it using the link above. 

If possible i would like some more information on creating/updating the relation between the form and the campaign.

BR August
 

 

 
Jeppe Eriksson Agger Dynamicweb Employee
Jeppe Eriksson Agger
Reply

Hi August,

I've updated the example project to be compatible with the latest versions of DynamicWeb. You can find it here: https://github.com/dynamicweb/Samples

You shouldn't have to parse any ids. Overriding ScreenInjector.OnAfter gives you access to the screen which has the form model which in turn has the id you're looking for. The example screen injector also does that.

I haven't seen that error before, so I'm not sure what's going on there. I'm also not sure what you mean when you say "...my view gets added to the dom in overlay-container". You should use OpenSlideOverAction.To<TScreen>().With(DataQueryBase) to show the slide over. This is almost the same as the example injector does. It uses a OpenDialogAction instead, but that's effectively the same. The only difference is how the screen is displayed.

- Jeppe

 

You must be logged in to post in the forum