10 Replies Latest reply: Oct 2, 2018 9:53 AM by Erik Wetterberg RSS

    How to make Qlik Sense extensions built with AngularJS re-render on resize or when the data changes?

    Karthikeyan Default

      When using plain JavaScipt, we write the rendering logic inside the 'paint' function and it gets executed and the view gets re-rendered, whenever we resize the element, or when the data changes on applying filters.


      But when using AngularJS controller, re-rendering is not happening in both the cases. I've to refresh the browser to see the changes take effect.


      Please help me with this.

        • Re: How to make Qlik Sense extensions built with AngularJS re-render on resize or when the data changes?
          Stefan Walther

          Angular uses double-way binding, so there is no re-paint, AngularJS takes care of updating the corresponding DOM elements. Maybe this article helps as a starting point:

           

          The Angular Way: Basics | qliksite.io

           

          Hope this helps.

          Regards

          Stefan

          • Re: How to make Qlik Sense extensions built with AngularJS re-render on resize or when the data changes?
            Darius Pranskus

            I had the same issue, I was able to find a workaround. Not sure if this is the best solution or not, but it worked for me.

             

            What I wanted was to detect when the following events are triggered:

             

            1. Browser window has been resized

            2. Extension element has been resized

            3. Layout/properties of my extension has been modified

             

            The first might be achieved by subscribing to window resize event. With the jquery it is easy - just put it in your angular controller

             

            $(window).on("resize", function(event) {
                console.log("window.resize");
                console.dir(event);
                // Do whatever you want
            });
            

             

            Other two were a bit trickier.

             

            To sort the second issue I did the following:

             

            I created a dummy invisible component in my extension definition, injected a $rootScope into the controller and then created an event watcher:

            
            

             

            refresh: {
                component: {
                    template: "<span></span>",
                    controller: ["$rootScope", "$scope", function ($rootScope, $scope) {
                        $scope.$on("layoutchanged", function (event, data) {
                            $rootScope.$broadcast("myLayoutChanged", data);
                        });
                    }]
                }
            }
            

             

            Then in the main component controller I added another watcher:

            $scope.$on("myLayoutChanged", function (event, data) {
                console.log("myLayoutChanged");
                console.dir(event);
                console.dir(data);
                // Do wahterver you want
            });
            

             

            I tried to watch on the "layoutchanged" message directly on the main controller, but it did not work. Possibly that the message was not propagated to it.

             

            The solution to the third issue was to inject a $rootScope into main controller, add to the layout object a function to return the $rootScope object and then use it in the onResize() (non angularjs) event to broadcast a message which can be then catched in the main controller:

            resize: function($element, layout) 
            {
                if (layout.getRootScope) {
                    var $rootScope = layout.getRootScope();
                    $rootScope.$broadcast("myElementResize", $element);
                }
            },
            template: ngTemplate,
            controller: ["$rootScope", "$scope", "$element", "$attrs", function ($rootScope, $scope, $element, $attrs) {
            
                $scope.layout.getRootScope = function() {
                    return $rootScope;
                }
            
                $scope.$on("myElementResize", function (event, data) {
                    console.log("myElementResize");
                    console.dir(event);
                    console.dir(data);
                    // Do whatever you want
                });
                // The rest of the controller code goes here
            
            }]
            
            
            • Re: How to make Qlik Sense extensions built with AngularJS re-render on resize or when the data changes?
              Morne van Zyl

              I know this is an old thread but I was looking for some help on it and thought that I would add my 2 cent solution for future readers.

               

              TL;DR; use Angular Events to call your custom code when using a property on the $scope object.

               

              So as far as I understand it, once the cube is returned, the ngTemplate and the $scope are 2 way bound, which means that when the $scope.layout.qHyperCube is updated by Qlik, the template will re-render. This only works when your template references the cube, which is updated and the binding then renders to the Template.

               

              In my extension, I looped through the cube and massaged the data into the shape that I required and then added it as a property on the $scope object ($scope.myData = {...}), and my ngTemplate was bound to the myData property. In this scenario, the cube gets updated but my code doesn't get called to process the data into what I need.

               

              To fix this, I re-run my code whenever the extension's data is Validated by using an Angular Event.

               

              return {
                  initialProperties: initialProps,
                  definition: props,
                  template: ngTemplate,
                  controller: ['$scope', '$element', function ($scope, $element) {
              
              
                      function processData() {
                          var data = {};
                          // Do Stuff...
                          $scope.myData = data;
                      }
              
              
                      // Enable scrolling
                      $element.css("overflow-y", "auto");
              
              
                      processData();
              
              
                      /**
                       * Validated event.
                       * @description The data has been recalculated and new valid data is available.
                       */
                      $scope.component.model.Validated.bind(function () {
                          processData();
                      });
                  }]
              };
              
              
              
              

               

              Not sure if this is the correct way to do it but it seems to be working.

               

              (note: Using Qlik Sense Sept 2018)