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 ... 
                }
            });
    };
}]);
1

1 Answer 1

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).

Sign up to request clarification or add additional context in comments.

1 Comment

That did it! I have added the details of the fix the OP. Thank you sir!!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.