Angular blocks break when reordering in content area

Hi,

We have a set of custom blocks in which we use AngularJS to fetch and render content, and have encountered a peculiar problem in edit mode. When there are multiple blocks in a content area, the Angular views break when we reorder them (by using drag and drop). The blocks get reordered, but all the interpolated scope variables "lose" their values, showing only raw, Angular markup. 

After some investigation we saw that the EPiServer frontend does an AJAX POST request to /EPiServer/CMS/PropertyRenderer when we reorder the blocks. It seems to me that we have to somehow hook into this event and manually invoke $digest or $apply after the post is complete. Is there some sort of Dojo event that we can hook into? We are also open to other workarounds.

May 20, 2015 9:18
  • Hi William 

    I think the message service pool may suit your needs: http://world.episerver.com/documentation/Items/Developers-Guide/EPiServer-Framework/7/User-Interface/Message-Service-Pool/

    You should be able to listen out for changes to a specific content area property and update Angular as appropriate.

    David

    May 26, 2015 22:24
  • Hi David,

    I tried using the following, but it didn't work:

    var x = epi.dependency.resolve("epi.shell.MessageService");
    x.observe({ contextTypeName: "ContentArea" }, function() { alert('hi'); })

    I'm guessing my query isn't correct, but there isn't that much info on what format the different parameters should be in (typeName, contextTypeName, contextId, etc.). 

    Could you provide an example on how to listen for changes to content area (e.g. during resize or drag and drop)?

    May 27, 2015 14:01
  • Hi William

    I just tried this and its possible to get a hook that allows you to listen our for page change events:

    require(["dojo/aspect", "dojo/_base/lang", "epi/dependency"],
        function (aspect, lang, dependency) {
    
            var processChange = function (id, item) {
                console.log("Item on the page has changed");
                console.log("id: " + item[0]);
                console.log("item page name: " + item[1].properties.icontent_name);
            };
    
            var registry = dependency.resolve("epi.storeregistry");
            var contentDataStore = registry.get("epi.cms.contentdata");
    
            aspect.after(contentDataStore, "onItemChanged", lang.hitch(this, processChange));
        }
    );

    The caveat here is that the event is fired each time anything on the page is changed. You can probably do something clever in processChange to work out if the content area(s) you are intersted in have actually changed rather than using $apply each time.

    Hope it helps, let me know how you get on.

    David

    May 29, 2015 16:43
  • Thanks, I tried putting your code in a JS file just to try it out, but I keep getting "Uncaught ReferenceError: require is not defined". Since I'm in edit mode (where dojo is loaded by the CMS) I don't really understand why I get this error. From what I can see this only occurs when dojo.js isn't loaded.

    Jun 02, 2015 13:35
  • If you put your script inside a JS thats used in your content template then dojo isn't loaded by default, you need to extend the EPiSever UI to ensure it runs as part of EPiServer UI where dojo is loaded. Hope that makes sense.  

    Jun 03, 2015 17:15
  • I thought as much... Is there a way of doing this without having to create a widget? Since we only want to emit some sort of DOM event when content is changed a widget feels kind of out of place. It would also imply that this behavior (i.e. broadcasting edit change events) would become optional and require additional configuration/setup.

    Jun 04, 2015 14:45
  • Hi William

    You can achive this with two changes. Merge the following into your module.config file in the site root:

    <?xml version="1.0" encoding="utf-8"?>
    <module>
    
      <dojo>
        <paths>
          <add name="angularFix" path="Scripts" />
        </paths>
      </dojo>
    
      <clientModule initializer="angularFix.initMe">
        <moduleDependencies>
          <add dependency="CMS" type="RunAfter" />
        </moduleDependencies>
        <requiredResources>
          <add name="angularFix.initMe"/>
        </requiredResources>
      </clientModule>
      
    </module>
    

    Then create a js file in this location this in the \ClientScripts\Scripts\initMe.js :

    define(
        ["dojo", "dojo/aspect", "dojo/_base/lang", "epi/dependency", "epi-cms/_ContentContextMixin"],
        function (dojo, aspect, lang, dependency, contentContextMixin) {
            return dojo.declare([], {
    
                initialize: function () {
                    this.inherited(arguments);
    
                    // We need to wait for EPiServer to initiliase the UI to get the epi.cms.contentdata registry so this is a little hacky...
                    setTimeout(function (that, processChange) {
                        var registry = dependency.resolve("epi.storeregistry");
                        var contentDataStore = registry.get("epi.cms.contentdata");
    
                        aspect.after(contentDataStore, "onItemChanged", lang.hitch(that, processChange));
                    }, 5000, this, this._processChange);
    
                },
                
                _processChange: function (id, item) { 
                    console.log("Item on the page has changed");
                    console.log("id: " + item[0]);
                    console.log("item page name: " + item[1].properties.icontent_name);
                }
    
        });
    });

    Jun 04, 2015 17:26
  • Thank you David! We finally managed to get it working. In order to get the Angular views to refresh we created a directive that listens for a custom content change event (fired in the dojo script) and runs the $compile service on the new content.

    Edited, Jun 08, 2015 9:02
  • Hi William

    Great that you got it solved! 

    The timeout is only used to wait until the EPiServer UI has fully initialised (and the rest stores have been registered) before attaching _processChange. So _processChange should always fire immediately when content is changed by editors.

    However I wasn't overly satisfied with the timeout so will check if there is a better way of waiting for the EPiServer UI to initialise. 

    David 

    Jun 08, 2015 12:01
  • Hi William

    Good news. We can avoid the timeout by addiing a Require on the module dependency:

    <?xml version="1.0" encoding="utf-8"?>
    <module>
     
      <dojo>
        <paths>
          <add name="angularFix" path="Scripts" />
        </paths>
      </dojo>
     
      <clientModule initializer="angularFix.initMe">
        <moduleDependencies>
          <add dependency="CMS" type="Require RunAfter" />
        </moduleDependencies>
        <requiredResources>
          <add name="angularFix.initMe"/>
        </requiredResources>
      </clientModule>
       
    </module>

    The code for \ClientScripts\Scripts\initMe.js can then be simplified:

    define(
        ["dojo/_base/declare", "dojo/aspect", "dojo/_base/lang", "epi/dependency"],
        function (declare, aspect, lang, dependency) {
            return declare([], {
    
                initialize: function () {
                    this.inherited(arguments);
    
                    var registry = dependency.resolve("epi.storeregistry");
                    var contentDataStore = registry.get("epi.cms.contentdata");
    
                    aspect.after(contentDataStore, "onItemChanged", lang.hitch(this, this._processChange));
    
                },
                
                _processChange: function (id, item) { 
                    console.log("Item on the page has changed");
                    console.log("id: " + item[0]);
                    console.log("item page name: " + item[1].properties.icontent_name);
                }
    
        });
    });

    David

    Jun 08, 2015 12:47
  • That's awesome, thanks for investigating :)

    Jun 08, 2015 12:50
  • Hi again David,

    We've encountered some problems with the dojo script - it seems that injecting into the onItemChanged action breaks the EPiServer UI. For example, when changing pages in the page tree the site title gets updated, but the page doesn't refresh. When we comment the row

    aspect.after(contentDataStore, "onItemChanged", lang.hitch(this, this._processChange));

    everything works as normal again. I'm guessing we have to do some additional stuff (maybe return something or trigger some type of event) in the processChange method?

    Also, adding the "Require" to the module.config didn't work either, as it caused the UI to not render the page at all.

    Jun 10, 2015 11:19
  • Hello William

    Sorry its taken a while to get back to you! You can try subscribing to the context changed topic and see if that resolves your issue (I've not tested this):

    define(
        ["dojo/_base/declare", "dojo/aspect", "dojo/_base/lang", "dojo/topic", "epi/dependency"],
        function (declare, aspect, lang, topic, dependency) {
            return declare([], {
    
                initialize: function () {
                    this.inherited(arguments);
    
                    topic.subscribe("/epi/shell/context/changed", lang.hitch(this, "_onContextChanged"));
                },
    
                _onContextChanged: function (ctx, callerData) {
                    console.log("Item on the page has changed");
                }
    
            });
        });

    Jul 03, 2015 11:14
First   1   Last