Angular ng-show not working consistently

113 views Asked by At

The Problem

I'm trying to integrate the Adobe Creative SDK into my Angular app. The editor is initialized from a photo viewer modal by clicking a button, which triggers the function below. Once the editor has been initialized, the onReady callback hides the photo viewer elements and the editor is appended to the modal. This part works great.

In my modal DOM I'm trying to use ng-show="editorOpen" and ng-show="!editorOpen" on the elements I wish to show or hide, depending on whether or not the editor is open. The first time I trigger $scope.editPhoto, ng-show works as expected. When I close the editor however, ng-show fails to update the DOM. The editor disappears, however the hidden photo viewer elements remain hidden.

I'm no expert on Angular's digest cycle, but from what I've been able to find out from my research, I believe the problem is that the hidePreview() and showPreview() functions are being called during a digest cycle, causing ng-show not to register the change to $scope.editorOpen.

Once I close the editor and ng-show fails to update the DOM, I can still trigger it manually by doing another action within the app that triggers a digest cycle, such as using the left/right arrow keys to try and navigate to the next photo in the viewer modal.

It gets weirder from here though. Once I've closed the editor and manually triggered a digest, everything seems normal. If I re-open the editor a second time, ng-show stops working altogether, even if I try to manually trigger a digest using the method I just described.

Solutions I've Tried

I've tried a number of possible fixes, including every solution offered in this discussion to solve the problem, all of which have failed.

  1. I tried wrapping $scope.editorOpen = true and $scope.editorOpen = false in a $timeout function, since that should trigger a digest. I tried setting the delay to 0ms and 10ms. Both failed.
  2. I created a scoped function $scope.toggleEditor to toggle $scope.editorOpen and passed that into the Adobe Creative SDK image editor's onSave, onReady, and onClose callbacks, instead of the showPreview() and hidePreview() functions I have in my code below. That also failed.
  3. I tried wrapping my entire $scope.editPhoto function in a $timeout function. Also failed.
  4. I tried the more direct approach of manually calling $scope.$apply() as well as $scope.$digest() within my showPreview() and hidePreview() functions. That typically worked only once before raising a $rootScope:inprog error, and also smells like bad practice.
  5. Finally, getting more desperate, I tried if (!$scope.$$phase) $scope.$apply(), just to see if it would solve the problem, despite being a terrible practice. Unsurprisingly it also failed just like #4.

The Code

In my controller I have the following function:

$scope.editPhoto = function(img, key, currentIndex) {
    var index = currentIndex;
    var photo = img;
    var apiKey = key;
    var ImgID = angular.element('#image-' + photo.id).children().first();
    var ImgSrc = ImgID.attr('src');
    if(!$scope.featherEditor) {
        $scope.featherEditor = new Aviary.Feather({
            apiKey: angular.element('#view_photo').attr('key'),
            appendTo: angular.element('#view_photo'),
            enableCORS: true,
            displayImageSize: true,
            showWaitIndicator: true,
            onLoad: function() {
                launchEditor(ImgID, ImgSrc);
            },
            onReady: hidePreview(),
            onSave: function(imageID, newURL) {
                angular.element(photo).css('background-image','url("' + newURL + '")');
                $http.put('/photos/' + photo.id + '.json',{image: newURL}).then(function(resp){
                    $scope.results[index] = resp.data.photo;
                    photosAPI.resizeColumns();
                    $scope.featherEditor.close();
                });
            },
            onClose: function(isDirty) {
                showPreview();
            },
            onError: function(errorObj) {
                alert('Oops! There seems to have been a problem with the Image Editor! Please let us know if the issue persists!');
                console.log('APIErrorCode: ' + errorObj.code);
                console.log('APIErrorMessage: ' + errorObj.message);
                console.log('APIErrorArgs: ' + errorObj.args);
                showPreview();
            }
        });
    } else {
        $scope.featherEditor.launch({
            image: ImgID,
            url: ImgSrc
        });
    }

    function launchEditor(id, src) {
        $scope.featherEditor.launch({
            image: id,
            url: src
        });
        return false;
    };

    function showPreview() {
        $scope.editorOpen = false;
        angular.element('#close').show()
    };

    function hidePreview() {
        $scope.editorOpen = true;
        angular.element('#close').hide()
    };
};

In my view I have the following div, which lives in a modal:

<div id="view_photo">
    <div ng-show="!editorOpen">
        <div class="photo-preview" id="{{'image-' + results[currentIndex].id}}" ng-style="{'background-image': 'url(' + results[currentIndex].original_image_url + ')'}"></div>
        <div class="photo-controls">
            <div class="btn btn-default pull-left" style="margin-right:5px;" ng-click="editPhoto(results[currentIndex], '<%= ENV['ADOBE_CLIENT_ID'] %>', currentIndex)">
                <i class="fa fa-magic"></i> Edit
            </div>
        </div>
    </div>
</div>
0

There are 0 answers