Charlie Stanard
Charlie Stanard

Reputation: 1058

Angular view not updating on scope change after form submit in ngDialog

I'm new to Angular and I'm attempting to build a robust todo app for learning purposes. There's probably something basic I'm doing wrong, but can't figure it out. I assume it's something with the way I have my controllers and templates configured. I've tried some of the recommendations found in similar questions but none that solve my issue. I've tried using $scope.$apply() but I get the "apply/digest already in progress" error.

I'm using ngRoute so when someone goes to /profile it calls the profileController and loads in templates/profile.html. This controller doesn't really do anything other than setup the current user and attach to $rootScope. Inside of the profile view I have a nested controller called listController. I'm passing in the List resource to listController to make http requests. Everything is working correctly as far as routing and the get/post requests.

Here's a simplified example of my controller:

var myApp = angular.module('todo', ['ngRoute', 'ngResource'])
    // config stuff...
    .controller('listController', ['$scope', 'List', 'ngDialog', function ($scope, List, ngDialog) { 
        // This correctly returns all lists for the current user
        $scope.lists = List.query();

        // Open modal form function
        $scope.openModal = function () {
            ngDialog.open({
                template: 'templates/partials/new-list.html',
                className: 'ngdialog-theme-default'
            });
        };

        $scope.createList = function () {
            List.save({}, {
                name: $scope.list.name,
                description: $scope.list.description
            }).$promise.then(function(result) {
                $scope.lists.push(result);
            });
        };
    }]);

Here's the relevant part of templates/profile.html which correctly displays all lists retrieved from List.query():

<div class="todo-sidebar" ng-controller="listController">
    <h3>Your lists <a href="#" ng-click="openModal()"><span>New list</span></a></h3>
        <ul>
            <li ng-repeat="list in lists">
                <a href="#"><span class="list-name">{{list.name}}</span></a>
        </li>
    </ul>
</div>

The problem is when I call the createList function from inside my templates/partials/new-list.html partial:

<div class="dialog-contents" ng-controller="listController">
    <h3>New list</h3>
    <form ng-submit="createList()">
        <div class="form-group">
            <label>Name</label>
            <input type="text" name="name" ng-model="list.name">
            <textarea name="description" ng-model="list.description" rows="10"></textarea>
            <button type="submit" class="button">Create list</button>
        </div>
    </form>
</div>

The form posts correctly and I can see the new list in my database, but the view does not update in real-time. When I console.log($scope) inside the promise it appears that my new list has been added to the scope.

I have a feeling it's not best practice to attach a controller to multiple elements/templates, but I'm not sure how else to structure this.

Upvotes: 3

Views: 1040

Answers (2)

Mark Hill
Mark Hill

Reputation: 1839

Okay so after a little research I think I have a solution for you. So what is happening is that you're not resolving the close of the dialog box correctly.

For reference here is the link I used:

https://github.com/likeastore/ngDialog#api

Scope

What you're dealing with here is a scope issue. When the dialog box is opened up, it is generating a separate module with it's own controller. This controller's scope is a one-way binding, and will not be seen by the parent scope upon resolution. So this is the why to the reason that your scope is not updating on the parent scope level.

For the how, please see the next section regarding promises.

Promises

To manage the scope at the parent level you're going to need something called a promise function, or in simpler terms a function that happens after the close of the dialog resolves fully. When dealing with the promise function there will be some kind of initial callback that allows you to pass data to the promise, and when you initialize the promise this is when you'll be able to access that data, which will then cooperate with the parent-level scope variables.

For this solution I have provided you a bit of updated code that might be helpful for you.

updated Code

If I'm understanding how your code works along with how the module works your updated functions should look something like the following. I would give this a shot:

var myApp = angular.module('todo', ['ngRoute', 'ngResource'])
// config stuff...
.controller('listController', ['$scope', 'List', 'ngDialog', function     ($scope, List, ngDialog) { 
    // This correctly returns all lists for the current user
    $scope.lists = List.query();

    // Open modal form function
    $scope.openModal = function () {
        var dialog = ngDialog.open({
            template: 'templates/partials/new-list.html',
            className: 'ngdialog-theme-default',
            controller : function($scope){  
                $scope.createList = function(){
                    dialog.close($scope.list); // where data is the list data
                };
            }
        });
        dialog.closePromise.then(function(data){
            // at this point data is your list and then you can update the value as you see with like normal
            List.save({}, {
               name: $scope.list.name,
                description: $scope.list.description
            }).$promise.then(function(result) {
               $scope.lists.push(result);
            });
        });
    };
}]);

Again this is just a rough template for what you'd need to do, but this is how you would implement it nonetheless.

I hope this helps, please ask if you have any more questions.

Upvotes: 1

nham
nham

Reputation: 161

You're dealing with a $scope issue I think. The values are there, but you're not referencing what you think you are. Referencing the "lists" via controllerAs, like so...

<li ng-repeat="list in MyController.lists"> 

should fix your problem.

Hope that helps.

This will explain it far better than I will be able to. https://github.com/angular/angular.js/wiki/Understanding-Scopes#ngRepeat

Upvotes: 0

Related Questions