jQuery UI Widgets Forums Dialogs and Notifications Window jqxWindow cleanup in AngularJS

This topic contains 1 reply, has 2 voices, and was last updated by  Dimitar 5 years, 12 months ago.

Viewing 2 posts - 1 through 2 (of 2 total)
  • Author
  • jqxWindow cleanup in AngularJS #72959

    tm_dev
    Participant

    Hi,

    We are using jqWidgets with AngularJS and have experienced a few problems. We were able to reproduce one of them with an inline single page app. It is described below.

    Problem

    In our application, jqxWindow DOM elements are not being cleaned up after a page transition, which often causes an empty window to display. In these cases, one window is updated but the empty window is displayed.

    Please advise us on how to handle or prevent this issue.

    Code Description

    The application is using the ui.router state provider to move from page to page. Some of the pages are using jqxWindows as popups presented to the user. The windows are part of a ui-view that is dynamically returned based on the routing configuration. In this particular case, the pages are using $state.transitionTo to implement page changes after a button click.

    The attached page contains a script to allow for a transition between 2 pages by clicking the Next Page button. It simulates what the full application is doing.

    The table contains the count of jqxWindows with the specified ID that are actually part of the DOM. The count will increase whether you open the window or not.

    Note: The attached file contains the exact script shown below.

    <!DOCTYPE HTML>
    <html>
    <head>
    <meta http-equiv=”X-UA-Compatible” content=”IE=edge,chrome=1″>
    <title>EMS – Angular / jqWidgets / jqxWindow test</title>
    <base href=”http://localhost:63342/Evaluation/” />

    <link rel=”stylesheet” href=”//www.jqwidgets.com/jquery-widgets-demo/jqwidgets/styles/jqx.base.css”>
    <link rel=”stylesheet” href=”//jqwidgets.com/public/jqwidgets/styles/jqx.energyblue.css”>
    <link rel=”stylesheet” href=”//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css”>

    <style>
    body {
    margin:0; padding:0
    }

    table, th, td {
    border: 1px solid black;
    }

    .col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {
    padding-left: 0px;
    padding-right: 0px;
    /* background-color: lightblue; */
    line-height: 30px;
    }

    .form-group .form-control, .form-group-lg .form-control {
    height: 22px;
    padding: 0px;
    margin-top: 2px;
    font-size: 12px;
    line-height: 1.3333333;
    border-radius: 3px;
    }

    .header {
    margin-bottom: 10px;
    font-weight: bold;
    }

    .open-window-button {
    margin-bottom: 10px;
    }

    .controller-view {
    margin-bottom: 10px;
    }

    .stats, .dependencies, .instructions {
    margin-top: 20px;
    margin-bottom: 10px;
    }

    </style>
    </head>
    <body>

    <div id=”main” ng-app=”simpleApp”>
    <div id=”page-title” class=”header”>jqxWindows Test – Home</div>
    <input type=”button” value=”Open Window” id=’jqxButton’ class=”open-window-button”/>
    <div ng-controller=”homeController” class=”controller-view”>
    <div ui-view=”home”></div>
    </div>
    <div ng-controller=”page2Controller” class=”controller-view”>
    <div ui-view=”page2″></div>
    </div>

    <div class=”stats”>
    <table>
    <tr>
    <th>Window</th>
    <th>Num instances</th>
    </tr>
    <tr>
    <td>addRateEntryWindow</td>
    <td id=”numRateEntryWindows”></td>
    </tr>
    <tr>
    <td>page2Window</td>
    <td id=”numPage2Windows”></td>
    </tr>
    </table>
    </div>

    <div class=”dependencies”>
    <div class=”header”>Dependencies</div>

    • jQuery
    • Bootstrap
    • AngularJS
    • AngularJS UI Router
    • jqxWidgets

    </div>

    <div class=”instructions”>
    <div class=”header”>Instructions</div>
    <p>Click on Open Window. Click on the Next Page button.
    Then click on Open Window. Then click on the Next Page button. Observe the DOM instance count.
    </p>
    <p>The bottom line is jqxWindow instances are not being destroyed automatically upon AngularJS page transitions</p>
    </div>

    </div>

    <script src=”//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js”></script>
    <script src=”//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js”></script>
    <script src=”//ajax.googleapis.com/ajax/libs/angularjs/1.2.17/angular.js”></script>
    <script src=”//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.js”></script>
    <script src=”//jqwidgets.com/public/jqwidgets/jqx-all.js”></script>

    <script>
    var numClickEvents = 0;

    var homeView = [
    ‘<div><div jqx-toggle-button id=”nextButtonPage2″ class=”btn btn-primary btn-sm”>Next Page</div></div>’,
    ‘<div id=”addRateEntryWindow” class=”home-entry-window” style=”display: none;”>’,
    ‘ <div id=”addRateEntryWindowHeader”>   </div>’,
    ‘ <div>’,
    ‘ <div id=”addRateEntryTitle”> <h4>Add Data</h4> </div>’,
    ‘ <form class=”container-fluid form-group-lg” role=”form” id=”addRateEntryForm” action=”./”>’,
    ‘ <div class=”col-xs-12 col-sm-12 col-md-12 col-lg-12″ id=”addRateTableEntriesGridDiv”>’,
    ‘ <div class=”form-group”>’,
    ‘ <div>’,
    ‘ <div id=”addRateTableEntriesGrid”></div>’,
    ‘ </div>’,
    ‘ </div>’,
    ‘ </div>’,
    ‘ <div class=”col-xs-12 col-sm-12 col-md-12 col-lg-12″>’,
    ‘ <div class=”form-group”>’,
    ‘ <label class=”col-md-offset-1 col-md-5 col-lg-offset-1 col-lg-4 control-label”>Entry:</label>’,
    ‘ <div class=”col-md-6 col-lg-6″>’,
    ‘ <jqx-input class=”form-control” id=”newRateValueForAdd”></jqx-input>’,
    ‘ </div>’,
    ‘ </div>’,
    ‘ </div>’,
    ‘ <div class=”col-xs-12 col-sm-12 col-md-12 col-lg-12″>’,
    ‘ <div class=”form-group”>’,
    ‘ <label class=”col-md-offset-1 col-md-5 col-lg-offset-1 col-lg-4 control-label”>Source:</label>’,
    ‘ <div class=”col-md-6 col-lg-6″>’,
    ‘ <div class=”form-control” id=”sourceAndVersionForAdd”></div>’,
    ‘ </div>’,
    ‘ </div>’,
    ‘ </div>’,
    ‘ <div>’,
    ‘ <div class=”col-xs-12 col-sm-12 col-md-12 col-lg-12 text-center”>’,
    ‘ <div>’,
    ‘ <div jqx-toggle-button id=”gridBtnNewEntriesSave”‘,
    ‘ class=”btn btn-primary btn-sm”>Ok</div>’,
    ‘ <div jqx-toggle-button id=”gridBtnNewEntriesCancel”‘,
    ‘ class=”btn btn-primary btn-sm”>Cancel</div>’,
    ‘ </div>’,
    ‘ </div>’,
    ‘ </div>’,
    ‘ </form>’,
    ‘ </div>’,
    ‘</div>’
    ].join(‘\n’);

    var page2View = [
    ‘<div><div jqx-toggle-button id=”nextButtonHome” class=”btn btn-primary btn-sm”>Next Page</div></div>’,
    ‘<div id=”page2Window” class=”page2-entry-window” style=”display: none;”>’,
    ‘ <div id=”page2WindowHeader”>   </div>’,
    ‘ <div>’,
    ‘ <div id=”page2Title”> <h4>Dummy Window</h4> </div>’,
    ‘ <form class=”container-fluid form-group-lg” role=”form” id=”page2Form” action=”./”>’,
    ‘ <div class=”col-xs-12 col-sm-12 col-md-12 col-lg-12″>’,
    ‘ <div class=”form-group”>’,
    ‘ <label class=”col-md-offset-1 col-md-5 col-lg-offset-1 col-lg-4 control-label”>Data:</label>’,
    ‘ <div class=”col-md-6 col-lg-6″>’,
    ‘ <jqx-input class=”form-control” id=”page2Edit”></jqx-input>’,
    ‘ </div>’,
    ‘ </div>’,
    ‘ </div>’,
    ‘ <div>’,
    ‘ <div class=”col-xs-12 col-sm-12 col-md-12 col-lg-12 text-center”>’,
    ‘ <div>’,
    ‘ <div jqx-toggle-button id=”page2WindowSave”‘,
    ‘ class=”btn btn-primary btn-sm”>Ok</div>’,
    ‘ <div jqx-toggle-button id=”page2WindowCancel”‘,
    ‘ class=”btn btn-primary btn-sm”>Cancel</div>’,
    ‘ </div>’,
    ‘ </div>’,
    ‘ </div>’,
    ‘ </form>’,
    ‘ </div>’,
    ‘</div>’
    ].join(‘\n’);

    var simpleApp = angular.module(“simpleApp”, [‘ui.router’]);
    simpleApp.config(function($stateProvider, $urlRouterProvider) {

    $urlRouterProvider.otherwise(‘/home’);

    //http://localhost:63342/Evaluation/tests/angularjs/jqwidgets/jqxWindow_uiRouter.html/#/home
    $stateProvider.state(‘home’, {
    url: ‘/home’,
    views: {
    home: {
    template: homeView,
    controller: ‘homeController’
    }
    }
    })
    //http://localhost:63342/Evaluation/tests/angularjs/jqwidgets/jqxWindow_uiRouter.html/#/page2
    .state(‘page2’, {
    url: ‘/page2’,
    views: {
    page2: {
    template: page2View,
    controller: ‘page2Controller’
    }
    }
    });
    })
    .controller(“homeController”, function($scope, $stateParams, $state) {
    // setup grid (end)
    var unbindFn = $scope.$on(“$stateChangeStart”, function(eventName, to, toParams, from, fromParams) {
    if (to.name == “home”) {

    var onSuccessUnbind = $scope.$on(“$stateChangeSuccess”, function() {
    $(‘#jqxButton’).jqxButton({
    theme: ‘energyblue’
    });
    $(‘#jqxButton’).off(‘click’).on(‘click’, function () {
    $(‘#addRateEntryWindow’).jqxWindow(‘open’);
    });

    $(‘#gridBtnNewEntriesSave’).off(‘click’).on(‘click’, function () {
    $(“#numRateEntryWindows”).text($(“.home-entry-window”).length);
    $(‘#addRateEntryWindow’).jqxWindow(‘close’);
    });
    $(‘#gridBtnNewEntriesCancel’).off(‘click’).on(‘click’, function () {
    $(“#numRateEntryWindows”).text($(“.home-entry-window”).length);
    $(‘#addRateEntryWindow’).jqxWindow(‘close’);
    });

    $(‘#addRateEntryWindow’).jqxWindow({
    width: 940,
    height: 400,
    position: “center”,
    showCloseButton: true,
    autoOpen: false
    });

    // setup dropdown (start)
    var sourceVersionSource = {
    datatype: “json”,
    datafields: [
    { name: ‘name’ },
    { name: ‘key1’ }
    ],
    async: false
    };

    sourceVersionSource.localdata = [
    { name:”Source1″, key1: “k1″ },
    { name:”Source2”, key1: “k2″ },
    { name:”Source3”, key1: “k3” }
    ];
    var sourceVersionDataForAddAdapter = new $.jqx.dataAdapter(sourceVersionSource);
    sourceVersionDataForAddAdapter.dataBind();
    $(“#sourceAndVersionForAdd”).jqxDropDownList({
    selectedIndex: 0,
    displayMember: “name”,
    valueMember: “key1”,
    width: “100%”,
    source: sourceVersionDataForAddAdapter
    });
    // setup dropdown (end)

    // setup grid (start)
    var displayColumns = [
    {text: “Name”, dataField: “name”, editable: false, width: 200},
    {text: “Value”, dataField: “value”, editable: true, width: 200}
    ];

    var gridData = [
    { name:”Name1″, value: “v1″ },
    { name:”Name2”, value: “v2″ },
    { name:”Name3”, value: “v3” }
    ];
    var tablesDataFields = [
    {name : “name”, type : “string”},
    {name : “value”, type : “string”}
    ];
    var rateTableStructureJqxSource = {
    datatype: “json”,
    datafields: tablesDataFields,
    localdata: gridData
    };

    var rateTableStructureDataAdapter = new $.jqx.dataAdapter(rateTableStructureJqxSource);

    $(“#addRateTableEntriesGrid”).jqxGrid({
    width: 420,
    source: rateTableStructureDataAdapter,
    editable:false,
    pageable: true,
    autoheight: true,
    sortable: true,
    filterable: true,
    altrows: true,
    enabletooltips: true,
    columnsresize:true,
    selectionmode: ‘singlerow’,
    editmode: ‘click’,
    columns: displayColumns,
    pagesize: 5
    });

    $(“#numRateEntryWindows”).text($(“.home-entry-window”).length);

    $(“#page-title”).text(“jqxWindows Test – Home”);

    $(‘#nextButtonPage2’).off(‘click’).on(‘click’, function () {
    console.log(“nextButton clicked home”);
    $state.transitionTo(“page2”, {});
    });
    onSuccessUnbind();
    });

    $scope.$on(‘$locationChangeStart’, function () {
    console.log(“$locationChangeStart home”);
    });
    $scope.$on(‘$locationChangeSuccess’, function () {
    console.log(“$locationChangeSuccess home”);
    });
    $scope.$on(‘$destroy’, function () {
    console.log(“scope destroyed home”);
    });
    }

    });

    })
    .controller(“page2Controller”, function($scope, $stateParams, $state) { // Note: both controllers are being called

    var unbindFn = $scope.$on(“$stateChangeStart”, function(eventName, to, toParams, from, fromParams) {
    if (to.name == “page2”) {
    var onSuccessUnbind = $scope.$on(“$stateChangeSuccess”, function() {
    $(‘#jqxButton’).off(‘click’).on(‘click’, function () {
    $(‘#page2Window’).jqxWindow(‘open’);
    });

    $(‘#page2WindowSave’).off(‘click’).on(‘click’, function () {
    $(“#numPage2Windows”).text($(“.page2-entry-window”).length);
    $(‘#page2Window’).jqxWindow(‘close’);
    });
    $(‘#page2WindowCancel’).off(‘click’).on(‘click’, function () {
    $(“#numPage2Windows”).text($(“.page2-entry-window”).length);
    $(‘#page2Window’).jqxWindow(‘close’);
    });

    $(‘#page2Window’).jqxWindow({
    width: 600,
    height: 200,
    position: “center”,
    showCloseButton: true,
    autoOpen: false
    });

    $(“#numPage2Windows”).text($(“.page2-entry-window”).length);

    $(“#page-title”).text(“jqxWindows Test – Page 2”);

    $(‘#nextButtonHome’).off(‘click’).on(‘click’, function () {
    console.log(“nextButton clicked page2”);
    $state.transitionTo(“home”, {});
    });
    onSuccessUnbind();
    });

    $scope.$on(‘$locationChangeStart’, function () {
    console.log(“$locationChangeStart page2”);
    });
    $scope.$on(‘$locationChangeSuccess’, function () {
    console.log(“$locationChangeSuccess page2”);
    });
    $scope.$on(‘$destroy’, function () {
    console.log(“scope destroyed page2”);
    });
    }

    });
    });

    // Events
    /*
    $locationChangeStart
    $locationChangeSuccess
    $destroy
    */
    </script>

    </body>
    </html>

    jqxWindow cleanup in AngularJS #72975

    Dimitar
    Participant

    Hi tm_dev,

    Your code is too extensive and your project too specific for us to test. If you could reproduce the issue on a smaller scale, for example in a JSFiddle or a JS Editor, we will try to assist you.

    Best Regards,
    Dimitar

    jQWidgets team
    http://www.jqwidgets.com/

Viewing 2 posts - 1 through 2 (of 2 total)

You must be logged in to reply to this topic.