Angular blocks break when reordering in content area

Member since: 2010

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.

#121894 May 20, 2015 9:18
  • Member since: 2008

    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

    #122120 May 26, 2015 22:24
  • Member since: 2010

    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)?

    #122168 May 27, 2015 14:01
  • Member since: 2008

    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

    #122309 May 29, 2015 16:43
  • Member since: 2010

    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.

    #122416 Jun 02, 2015 13:35
  • Member since: 2008

    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.  

    #122517 Jun 03, 2015 17:15
  • Member since: 2010

    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.

    #122540 Jun 04, 2015 14:45
  • Member since: 2008

    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);
                }
    
        });
    });

    #122545 Jun 04, 2015 17:26
  • Member since: 2010

    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.

    #122613 Edited, Jun 08, 2015 9:02
  • Member since: 2008

    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 

    #122617 Jun 08, 2015 12:01
  • Member since: 2008

    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

    #122620 Jun 08, 2015 12:47
First   1 2   Last