DNN has been plagued with slow editing experience since DNN 3 - mostly because of the WebForms Postback system. 2sxc has fixed most of this by now, getting all UI actions to flow in less than 1 second; and Content-updates all reload immediately using AJAX. But for most DNN modules and 2sxc-Apps this is more complex but easy. I've lost many sleepless nights getting this to work, and hope that I can help you by sharing this.
The Challenges of Complex-Content (Modules/App)-AJAX
You may be surprised that DNN doesn't do AJAX and that 2sxc only supported AJAX for Content but not for Apps until now. This was because content usually has a simple content+template mechanism. It rarely needs JavaScript - and that's where the Apps are usually different. Apps very often have JavaScript when initializing, and that requires more control. Note that this can also be the case for more advanced content-templates, like to align the heights of items in a row. So here are the core challenges:
- Loading Mechanisms
- The ones in charge when your module or App is added to the page
- The ones in charge of updating the screen when the editor makes changes to content or configuration
- JavaScript Resources
- Loading JS resources when the app is added normally
- Loading javascript resources when the app is added by ajax
- Loading Javascript resources when re-loading the app by ajax (so maybe it was already in the page)
- Preventing problems with multiple loading of JS resources (like if other modules also had the resources loaded)
- JavaScript Initializations
- Starting the JS initializer, because document.ondocumentready already happened a while before
- Preventing previously initialized items to not double-initialize and do something unexpected
Step 1: Loading Mechanism
The loading mechanism is possibly the most difficult, because there are so many strategies you can use. Basically you need something around your module/app/view/template, which handles the AJAX aspects. We'll look at these cases
- Dynamic Content (templated dynamic content-items or content-lists using 2sxc & Razor/Tokens)
- Apps (bundles of data / views / logic / web-api in 2sxc using Razor / Tokens)
- SPA Apps (JS based Single Page Applications in 2sxc using any JS framework like AngularJS)
- DNN SPA Modules (only in DNN 8+)
- DNN Modules (mostly WebForms based, possibly with MVC)
1.1 Standard Dynamic Content AJAX
This is the freebie. Because 2sxc since version 7.0 automatically loads all Dynamic Content views/templates using AJAX within fractions of a second.
1.2 Apps AJAX (Token or Razor Based)
This is added in 2sxc 8.4.4 and also a freebie. All you have to do is enable the AJAX switch in the App Configuration and you're good to go (see image).
Note that this setting is disabled by default because many apps need some optimizations / testing. So we wanted to be sure that the stable behavior is the default.
1.3 SPA Apps AJAX
For standard SPA Apps there are various possibilities. The trivial way is to just enable the AJAX like for Apps. If you SPA was any good to start with, it will automatically support this use case as well, since any good SPA is meant to load properly and initialize the correct state based or URL-Routing.
But there are two cases where you may want something else:
- If you want to get your SPA to perform AJAX by itself on content-changes, and not just reload the entire app, then your SPA must register itself in 2sxc to handle the AJAX. This is not yet standardized but will be implemented soon.
- If your SPA doesn't behave correctly because it doesn't place its state in the URL then the AJAX-reload will often reload a view which isn't the one you expected. The basic recommendation is to fix your SPA to track its state in the URL - as you should for any modern SPA because of SEO and more. If you don't want to do this, then you too should register in 2sxc to handle the AJAX reloads yourself - which will be added soon.
1.4 DNN 8+ SPA Modules (AngularJS, etc.)
This is fairly easy, as you will usually provide JavaScript based dialogs to edit data, and will be able to provide this with callbacks which reload the data. The exact methods will vary a bit depending on your framework, but if you're already doing SPA, you won't need any additional help for this :).
1.5 DNN Modules (WebForms / MVC)
This is a LOT of work - sorry :(. DNN itself simply doesn't provide much to get you going quickly. DNN offers some basics, but implementing it can easily take 2-10 days, depending on the scale of your module. Here's what you'll have to do:
- Start with a server side service (WebAPI) which delivers the content-parts you want to reload by AJAX. Depending on your solution you'll want to provide rendered HTML or JSON data (if you just want to load the data). You can imitate much what 2sxc does - the HTML-rendering is currently done in the [todo] while the module-level data is provided by the [todo]. Note that this may require a LOT of refactoring under the hood to get this to work reliably, and you'll have to invest some time with security issues.
- Rework your editing / dialog cycle, as the default cycle opens DNN pages (containing server controls) which when complete, will re-load the page. You will probably want to:
- …switch to JS based dialogs (also a good thing to prepare for .net Core) - but you can skip this if it's too much work for now
- …open the dialogs in a way that doesn't call the page-reload but some of your own code, to then do the AJAX refresh
- Create an own JavaScript based refresh-API on the client to load your modules, views etc. This is fairly complex but you can copy much of what 2sxc does from [todo].
- Note that re-loading can be very complicated, so do spend some time with the 2sxc implementation. For example, there are cases where only some HTML is replaced, while in other cases everything incl. the module-state in the browser must be flushed
1.5+ Alternatives if you're using WebForms or MVC
- You're probably much faster to just convert your module to a SPA-App or a 2sxc App and ride the existing infrastructure. But in case you want to stick to WebForms (not supported by 2sxc) then this manual path is the way to go.
- There is also a secret trick to bypass everything using jQuery.load - which allows you to actually load the entire page but then only pick the parts you want and insert it into the existing DOM. It's fairly messy and not recommended, but could be a temporary option
- There is another secret trick where you can load a page with only one module (the one you want to AJAX) and leave away the skin and everything - using an mid=[moduleid] parameter and a bunch of stuff, which looks a bit like this
[your page]?mid=[your mid]&dnnprintmode=true&SkinSrc=%5bG%5dSkins%2f_default%2fNo+Skin&ContainerSrc=%5bG%5dContainers%2f_default%2fNo+Container
this can be combined with jQuery.load to build a viable AJAX solution, but it's fairly hacky.
Step 2: JavaScript Resources must Behave
2.1 Getting JavaScript Resources to Load/Behave Correctly with AJAX
The main thing you should cover is that basically all JS things should check if they had already been initialized, and if yes, they should not initialize again. There are many libraries to do things like this, but the very basic aspect is to check if something exists, which the JS creates, and only run the code if this is missing. Something like this (wrapped in an IIFE as best practice):