Search code examples
angularjstooltipangular-uipopover

Issue with Popover AngularJS


I have a bunch of table rows which include inputs and buttons, namely. I would like to have a Popover display to the right of an input for a row if the value isn't matching the requirements defined. The button will also be disabled until the value of the input is correct.

Relevant HTML:

<div class="row col-md-4">
    <table ng-controller="TestController" style="width: 100%">
        <tr ng-repeat="element in model.InvoiceNumbers">
            <td><input ng-model="element.id"
                    popover="Invoice must match ##-####!"
                    popover-placement="right"
                    popover-trigger="{{ { false: 'manual', true: 'blur'}[!isValidInvoice(element.id)] }}" 
                    popover-title="{{element.id}}"/></td>
            <td>{{element.id}}</td>
            <td><button ng-disabled="!isValidInvoice(element.id)">Approve</button></td>
        </tr>
    </table>
</div>

Relevant JavaScript:

app.controller("TestController", function ($scope) {
    $scope.model = {
        InvoiceNumbers : [
            { id: '12-1234' },
            { id: '12-1235' },
            { id: '1234567' },
            { id: '1' },
            { id: '' }],
    };

    $scope.isValidInvoice = function (invoice) {
        if (invoice == null) return false;
        if (invoice.length != 7) return false;
        if (invoice.search('[0-9]{2}-[0-9]{4}') == -1) return false;
        return true;
    };
});

The button gets disabled correctly on my local solution. However, I can't get the Popover to work; it behaves as if the model in its scope isn't getting updated. So, I looked through several links here (though most were from 2013 so I'd imagine a bit has changed) and their problems seemed to be solved by removing primitive binding. That didn't fix anything here. I added some console.log() lines in the function getting called from the Popover, and it was getting the correct value from the model each time. I also added a title to the Popover to show that its seeing the right value from the model.After seeing the log showing that it should be working correctly, I've run out of ideas.

The issue is element.id isn't updating dynamically within the trigger (it keeps its initial value, unlike popover-title which updates with the model). Is there something I did wrong?

Also, I've only been working with angular for a day so if you all have any suggestions on better ways to accomplish this, I'm open to suggestions.

Plunker: http://plnkr.co/edit/tiooSxSDgzXhbmIty3Kc?p=preview

Thanks


Solution

  • Found a solution on the angular-ui github page that involved adding these directives:

    .directive( 'popPopup', function () {
      return {
        restrict: 'EA',
        replace: true,
        scope: { title: '@', content: '@', placement: '@', animation: '&', isOpen: '&' },
        templateUrl: 'template/popover/popover.html'
      };
    })
    
    .directive('pop', function($tooltip, $timeout) {
        var tooltip = $tooltip('pop', 'pop', 'event');
        var compile = angular.copy(tooltip.compile);
        tooltip.compile = function (element, attrs) {
          var parentCompile = compile(element, attrs);
          return function(scope, element, attrs ) {
            var first = true;
            attrs.$observe('popShow', function (val) {
              if (JSON.parse(!first || val || false)) {
                $timeout(function () {
                  element.triggerHandler('event');
                });
              }
              first = false;
            });
            parentCompile(scope, element, attrs);
          }
        };
        return tooltip;
    });
    

    And here's the changes I made to the controller and view to make it work like I wanted in the original question:

    <div class="row col-md-4">
        <table ng-controller="TestController" style="width: 100%">
            <tr ng-repeat="element in model.InvoiceNumbers">
                <td><input ng-model="element.id"
                    pop="Invoice must match ##-####!"
                    pop-placement="right"
                    pop-show="{{element.showPop}}"
                    ng-blur="isValidInvoice($index, $event)" /></td>
                <td>{{element.id}}</td>
                <td><button ng-disabled="!isValidInvoice($index)">Approve</button></td>
            </tr>
        </table>
    </div>
    

    JavaScript:

    app.controller("TestController", function ($scope) {
        $scope.model = {
            InvoiceNumbers: [
                { id: '12-1234', showPop: false },
                { id: '12-1235', showPop: false },
                { id: '1234567', showPop: false },
                { id: '1', showPop: false },
                { id: '', showPop: false }]
        };
    
        $scope.isValidInvoice = function ($index, $event) {
            var obj = $scope.model.InvoiceNumbers[$index];
    
            var isValid = function () {
                if (obj.id === null) return false;
                if (obj.id.length != 7) return false;
                if (obj.id.search('[0-9]{2}-[0-9]{4}') == -1) return false;
                return true;
            };
    
            if ($event != null && $event.type == "blur") obj.showPop = !isValid();
            return isValid();
        };
    });
    

    Plunker: http://plnkr.co/edit/5m6LHbapxp5jqk8jANR2?p=preview