Search code examples
javascriptangularjsdata-binding2-way-object-databinding

Understanding angularjs $apply and $scope while editing object


In almost every application you encounter you have objects that needs to be edited.

In my case i have a list of objects where you are able to edit them and then on the fly edit them. (The magic of angular). However this has a side effect take for instance the following controller:

    app.controller('editCategoryInstant', ['$http', '$scope', '$state', '$modalInstance', 'api', '$filter', 'category', 'LRM', function ($http, $scope, $state, $modalInstance, api, $filter, category, LRM) {

    $scope.LRM = LRM;
    $scope.category = category;
    $scope.ok = function () {
        $scope.category.color = $('#color').val();
        category = $scope.category;
        if ($scope.category .icon != null) {
            $http({
                url: api.getUrl('category', category.id),
                method: "PUT",
                data: {category: $scope.category}
            }).success(function (data, status, headers, config) {

            }).error(function (data, status, headers, config) {
                var i = 0;
            });
            $modalInstance.dismiss('cancel');
        }
        else {
            alert('Vælg ikon');
        }

    };

    $scope.cancel = function () {
        $scope.category = $scope.master;
        $modalInstance.dismiss('cancel');
    };

    $scope.clickUpload = function () {
        setTimeout(function () {
            angular.element('#fileUpload').trigger('click');
        }, 0);
    };

}]);

Okay so il cut it out for you.

For this purpose i wish to edit an existing object that looks like this:

    var category = {
    name: 'My Category',
    icon: 'fa fa-bomb',
    color: '#FFF',
    description: 'This describes the category'
};

Now i pass the category variable to the controller

The controller is bound to a modal view that looks something like this:

    <div class="modal-header">
    <h3 class="modal-title" translate="STRUCTURE.CATEGORY.CREATE"></h3>
</div>
<form role="form" class="form-validation" ng-submit="ok()">
    <div class="modal-body">
        <div class="row">
            <div class="col-xs-12">
                <label class="control-label">{{'TERMS.NAME' | translate}}</label>
                <input type="text" placeholder="{{'TERMS.NAME' | translate}}" class="form-control"
                       ng-model="category.name" required>
            </div>
            <div class="col-xs-6" style="margin-top: 10px;">
                <label>{{'LIBRARY.CATEGORY.SELECTICON' | translate}}</label>
                <lb-icon-picker targetvariable="category"></lb-icon-picker>
            </div>
            <div class="col-xs-6" style="margin-top: 10px;">
                <label class="block">{{'LIBRARY.CATEGORY.SELECTCOLOR' | translate}}</label>
                <lb-color-picker targetvariable="category"></lb-color-picker>
            </div>
            <div class="col-xs-12" style="margin-top: 5px;">
                <textarea class="form-control" style="height: 150px" placeholder="{{'TERMS.DESCRIPTION' | translate}}" ng-model="category.description"></textarea>
            </div>

        </div>
    </div>
    <div class="modal-footer">
        <a class="btn btn-grey" ng-click="cancel()" tooltip="{{ 'TOOLTIP.CANCEL' | translate }}"><i
                class="fa fa-ban"></i></a>
        <button type="submit" class="btn btn-success" tooltip="{{ 'TOOLTIP.SAVE_AND_EXIT' | translate }}"><i
                class="fa fa-check-square-o"></i></button>
    </div>
</form>

Now i start to edit the object however because of the two way data binding the original object is also changed. So in case i cancel the changes the object will still look as if it has been changed.

For this purpose i tried to look up $apply and copy however i am unable to see how this would be implemented in this example.

Can anyone tell me what is best pratice in the above situation? how do you guys handle these things?


Solution

  • If you want to edit a copy you can use:

     $scope.category = angular.copy(category);
    

    Then when you have confirmed server update you can extend the original with the updated copy:

     angular.extend( category, $scope.category);