0

(please try not to get hung up on any typo's or semantics that may have occurred while transferring it to this post. I know that the code successfully executes so my issue isn't typo's. Also while I am interested in other ways of performing this task - using a single controller for example - I am more interested in why THIS scenario is not working. It may be something that comes up again and it would be nice to already understand the issue)

I am trying to do something that i thought would be fairly simple and after following several tutorials and Stack Overflow posts (as well as other places) I cannot see what I am missing. I am hoping that a pair of fresh eyes can help!

The HTML contains a form where new products can be added (for brevity I have reduced the number of elements) and a list of P tags where products are displayed ... The goal is to add the new products to the list as soon as they are created, without, obviously, refreshing the page.

<div style="width: 300px; float: left; display: inline-block;" data-ng-controller="ProductFormCtrl" data-ng-model="svc">
    <p>
        Name:
        <input type="url" id="txProductName" data-ng-model="svc.form.productName"/>
    </p>

    <button id="btnSubmit" data-ng-click="svc.addNewProduct()">submit</button>
    {{ svc.Title }}
</div>

<div id="dvProductList" style="position:relative;float:left;" data-ng-controller="ProductListCtrl">
    <h2>{{ svc.Title }} </h2>
    <P data-ng-repeat="product in svc.products">{{ product.ProductName }}</P>
</div>

The form and the list are managed by separate controllers ... each of which references a ProductService with their $scope variable ...

var app = angular.module('productApp', []);

app.controller('ProductFormCtrl', ['$scope', '$http', 'ProductService', function ($scope, $http, $productSvc) {

    $scope.svc = $productSvc;
    $scope.svc.Title = "Tea";
}]);
app.controller('ProductListCtrl', ['$scope', '$http', 'ProductService', function ($scope, $http, $productSvc) {

    $scope.svc = $productSvc;

}]);

In order to referesh the list, the thought was to bind the P element in the ProdcutListCtrl to the products property of the ProductService, and when a new product was added simply update the contents of the products property and voila! ... or not ...

The service defines the property "products" whose value is initialized by a call to web service. It's an array of lightweight product objects.

app.service('ProductService', ['$http', function ($http) {

    var self = this;
    self.Title = "Coffee";
    //
    // initialize the property to be referenced by both controllers
    (function () {
        $http.get("/API/ProductAPI/GetUserProducts/36d43a01-22e5-494d-9e46-2d4ea5df7001")
            .success(function (data) {
                self.products = data;
            });
    })(); 
    //
    // object to hold the form values
    self.form = { productName: '' };
    //
    // calls web service to add a new product
    self.addNewProduct = function () {

        var formData = new FormData();
        formData.append('ProductName', self.form.productName);

        $.ajax({
            type: "POST",
            url: "/API/ProductAPI/AddUserProduct/36d43a01-22e5-494d-9e46-2d4ea5df7001",
            data: formData,
            processData: false,
            contentType: false
        })
            .done(function (data) {

                if (data != null) {
                //
                // These changes are not expressed in the User Interface
                    self.products.push(data);  // Update the products property with the new product
                    self.Title = "Completed";  // Update the title property 
                }
            });
    };
}]);

So my expectation is that by "pushing" the new object into the existing array, the product list which references the "products" property would automagically update. I even update the Title property when the request is completed and that change is also not being displayed in the form.

  • I KNOW that the new product is being created in the databse and even that it's being returned ... in fact I have confirmed that the "self.products" property is even being updated.

  • I also considered that since a SERVICE is actually an instantiation that perhaps a different instance of the ProductService is being passed to each controller. I have eliminated this as a possibility since I can change the property, svc.Title, in one controller and see that it is propogated to the other controller ("Coffee" changes to "Tea").

Anyway ... it appears as though I am somehow losing the reference to the "self." properties when the AJAX request to add a new product is completed and I am not sure why that is. I would welcome any thoughts or guidance.

Thanks, -G

!! SOLUTION !!

Thanks to shaunhusain for the solution ... pretty simple resolution actually. I added a dependency on $rootScope to the service and in the success handler of the jQuery AJAX request I added a call to $apply to the $rootScope. That's it. :)

app.service('ProductService', ['$http', '$rootScope', function ($http, $rootScope) {
// ...     
// all this stuff stayed the same 
// ...

    //
    // calls web service to add a new product
    self.addNewProduct = function () {

        var formData = new FormData();
        formData.append('ProductName', self.form.productName);

        $.ajax({
            type: "POST",
            url: "/API/ProductAPI/AddUserProduct/36d43a01-22e5-494d-9e46-2d4ea5df7001",
            data: formData,
            processData: false,
            contentType: false
        })
            .done(function (data) {

                if (data != null) {
                    self.products.push(data);
                    self.Title = "Completed";
                    $rootScope.$apply();   // <-- Here's the fix ... 
                }
            });
    };
}]);
John Slegers
  • 45,213
  • 22
  • 199
  • 169
Gary O. Stenstrom
  • 2,284
  • 9
  • 38
  • 59
  • Try calling $apply after you add the new product to the collection. Check this for example. http://stackoverflow.com/questions/16039076/angular-js-view-doesnt-update-when-nested-scope-array-is-updated – Krishna Veeramachaneni Oct 14 '14 at 17:59

1 Answers1

1

Since you're using jQuery and not $http you need to trigger a $digest after you make the changes for the watches to be triggered to update, in your .done function call $scope.$apply();

Anytime you do something that updates the data but is being done async and/or is outside the context of an angular call you need to call $apply to trigger the watches to update (anything not in a function called by ng-click or something using $http or $timeout because these all call $apply for you).

shaunhusain
  • 19,630
  • 4
  • 38
  • 51