Skip to main content
Woohoo! Qlik Community has won “Best in Class Community” in the 2024 Khoros Kudos awards!
Announcements
Join us at Qlik Connect for 3 magical days of learning, networking,and inspiration! REGISTER TODAY and save!
cancel
Showing results for 
Search instead for 
Did you mean: 
Not applicable

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

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.

11 Replies
Stefan_Walther
Employee
Employee

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

Not applicable
Author

No Stefan, update is happening neither when resized (positioning of the elements inside, based on the new width and height after resize) nor when the data changes. I've to refresh the page to see the new data and positioning.

d_pranskus
Partner - Creator III
Partner - Creator III

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

}]

d_pranskus
Partner - Creator III
Partner - Creator III

Sorry you need to switch places my answers 2 and . Also for data change event you may try the similar approch to layout change, but use "datachanged" message instead.

ygfletch
Contributor II
Contributor II

Hey Darius,

I'm having this issue when building AngularJS style extensions, I'm not really seeing the value in creating these style extensions when the DOM doesn't actually get updated with layout changes. swr‌ can you help explain why it is like this with AngularJS extensions?

I can't get your solution to work, have you found another way of updating the extensions DOM when the layout is changed at all? Do you know if this method is still working in November 2017?

Many thanks

Graham

ErikWetterberg

Hi!

You probably need to publish some code snippet to explain your problem. There are lots of angular-based extensions where this works, so it must be some special case you have come across.

You could of course use paint() and resize() even in an angular-based extension, but in most cases this should not be needed.

Erik Wetterberg

Extending Qlik – Use Qlik in your own apps and sites

ygfletch
Contributor II
Contributor II

Thanks for the reply Erik. I've played around with it after what you've said as I thought I must have been missing something. I was saving the values from the layout directly onto $scope in the controller, then using these variables within an ng-class expression. I've changed the template to use the layout.variable version and this does update.

Cheers Erik

Anonymous
Not applicable
Author

Yes Angular does take care of most variables by two-way binding but that works only with variables declared in the scope.

In a scenario where you are giving the user some filtering option by using some custom property on the side panel, those aren't updated by Angular, so you have to use the paint method to render those changes in the view. I prefer linking view to the controller with a variable and then when the side panel filter is applied, I used the paint function to just simply update my scope variable, Angular will take care of the rest!

organgrindingmo
Partner - Contributor III
Partner - Contributor III

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)