Search code examples
javascripthtmlangularjsangularjs-scopeangularjs-components

Scope Not Updating When Using ng-click Within a Component


I have the issue that my scope is not updating when using ng-click within a component. The scope also does not update if I use $scope.$apply or $timeout so I am not sure what the issue is.

The idea is that when I click on my second button within my component (qdcPopover.html) that $scope.showGetData should be updated to true which will then update the div within my index.html.

Here is my code:

index.html:

<html lang="en" qva-bootstrap="false">
<head>
  <title>Data Prep</title>
  <script data-main="data-prep" src="/resources/assets/external/requirejs/require.js"></script>
</head>
<body>
  <div ng-controller="AppCtrl as ctrl">
    <button id="qdpCreateApp" class="lui-button  lui-button--rounded">Create App</button>
    <qdp-popover></qdp-popover>
    <div>Hello {{showGetData}} + {{showAppCreate}}</div>
  </div>
</body>
</html>

qdpPopver.html (component)

<div ng-show="showAppCreate" class="lui-popover" style="width: 400px;">
  <div class="lui-popover__header">
    <div class="lui-popover__title">{{qdpPopover.title}}</div>
  </div>
  <div class="lui-popover__body">
    {{qdpPopover.body}}
    <input id="{{qdpPopover.inputId}}" ng-show="qdpPopover.showInput" class="lui-input"/>
  </div>
  <div class="lui-popover__footer">
    <button class="lui-button  lui-popover__button" ng-click="showAppCreate = false">{{qdpPopover.button1}}</button>
    <button class="lui-button  lui-popover__button" ng-click="createApp()">{{qdpPopover.button2}}</button>
  </div>
</div>

data-prep-module.js

// define the angular module with its controller
var app = angular.module('qlikDataPrepModule', []);

// define angular components
app.component('qdpPopover', {
    templateUrl: 'qdpPopover.html',
    controller: 'AppCtrl'
})

var qdpAppNameInput = "qdpAppNameInput";
// Controller
app.controller('AppCtrl', function ($scope, $location, $http, $timeout) {
    $scope.qdpPopover = {
        title: "Create new App",
        body: "Name of my app:",
        button1: "Cancel",
        button2: "Create",
        showInput: true,
        inputId: qdpAppNameInput
    };
    $scope.showGetData = false;
    $scope.showAppCreate = false;

    // Function for app creation
    $scope.createApp = function () {
        $scope.showGetData = true;
        $scope.$apply();
    }
})

Solution

  • This is a very simple example, i messed up a bit the function names and functionalities so that is probably not what you intended, but it's just an example.

    From the docs it seems that components scopes are always isolated, so even with the same controller, you actually have 2 different instances. There are examples with using require to communicate between components but couldn't get it to work.. (doc is a bit sparse there).

    However something that works fine is by using a service. The service shares the data between all instances of you controller.

    Hope that helps, good luck!

    // define the angular module with its controller
    var app = angular.module('qlikDataPrepModule', []);
    
    // define angular components
    app.component('qdpPopover', {
        template: `<div ng-show="getShowAppCreate()" class="lui-popover" style="width: 400px;"><div>Hello {{getShowGetData()}} + {{getShowAppCreate()}}</div>
      <div class="lui-popover__header">
        <div class="lui-popover__title">{{qdpPopover.title}}</div>
      </div>
      <div class="lui-popover__body">
        {{qdpPopover.body}}
        <input id="{{qdpPopover.inputId}}" ng-show="qdpPopover.showInput" class="lui-input"/>
      </div>
      <div class="lui-popover__footer">
        <button class="lui-button  lui-popover__button" ng-click="showAppCreate(false)">{{qdpPopover.button1}}</button>
        <button class="lui-button  lui-popover__button" ng-click="showGetData(true)">{{qdpPopover.button2}}</button>
      </div>
    </div>`,
        controller: 'AppCtrl',
    })
    
    var qdpAppNameInput = "qdpAppNameInput";
    // Controller
    app.controller('AppCtrl', function ($scope, $location, $http, dataService) {
        $scope.qdpPopover = {
            title: "Create new App",
            body: "Name of my app:",
            button1: "Cancel",
            button2: "Create",
            showInput: true,
            inputId: qdpAppNameInput
        };
    
        // Function for app creation
        $scope.showGetData = function (val) {
            dataService.showGetData = val;
        }
        $scope.getShowGetData = function () {
            return dataService.showGetData;
        }
        
        // Function for show popover
        $scope.showAppCreate = function (val) {
            dataService.showAppCreate = val;
        }
        $scope.getShowAppCreate = function () {
            return dataService.showAppCreate;
        }
    })
    
    app.factory('dataService', function(){
        var data = {};
        
        data.showGetData = false;
        data.showAppCreate = false;
        
        return data;
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
    <html lang="en" qva-bootstrap="false">
    <head>
      <title>Data Prep</title>
      <script data-main="data-prep" src="/resources/assets/external/requirejs/require.js"></script>
    </head>
    <body ng-app="qlikDataPrepModule">
      <div ng-controller="AppCtrl as ctrl">
        <button id="qdpCreateApp" class="lui-button  lui-button--rounded" ng-click="showAppCreate(true)">Create App</button>
        <qdp-popover></qdp-popover>
        <div>Hello {{getShowGetData()}} + {{getShowAppCreate()}}</div>
      </div>
    </body>
    </html>