Angularjs: How to access main controller from modal controller and directive controller

Problem

I am new to Angularjs and working on a pet project. I have a main page which has functions in HomeController. Then I have a button in the main page which on click opens a modal and the modal has a separate controller called ModalController. There is also a directive which is present in the modal which has its own controller. I have 3 other places where I need to use date picker so I created a date picker directive and using it in all 3 places.

I am not able to do the below functionalities.

  1. Click on edit I opened a dialog but I need to open it with the row data and when pressed save should update the row. (The trouble I am facing here is how to get the value of the date which is directive's controller.)
  2. Open Modal and enter the details and click save I need to create a new record and add it to the records which is present in the main controller. (How save the data from Modal controller to Home controller records.)

script.js

angular.module('myApp', ['ngAnimate', 'ui.bootstrap']);
angular.module('myApp').controller('HomeController', function($scope, $uibModal) {

  $scope.records = [{'date': new Date(), 'place': 'Bangalore'}];

    $scope.openModal = function() {
      $uibModal.open({
        templateUrl: 'modaldialog.html',
        controller: 'ModalController'
      });
    };

    // How to get the directive date-picker value and pass it and save it.
    $scope.edit = function(record) {
      $uibModal.open({
        templateUrl: 'modaldialog.html',
        controller: 'ModalController'
      });
    };

    $scope.addwithinCtrl = function() {
      var record = {'date': new Date(), 'place': 'Hyderabad'};
      $scope.records.push(record);
    };

});

angular.module('myApp').controller('ModalController', function($scope, $uibModalInstance) {
  // save the input and dismiss the dialog
  $scope.save = function() {
    // how to save the entered data before closing the dialog?
    $uibModalInstance.dismiss('cancel');
  };

  $scope.cancel = function() {
    $uibModalInstance.dismiss('cancel');
  };
});


angular.module('myApp').directive('dateDirective', function() {
  return {
    restrict: 'A',
    templateUrl: 'date.html',
    controller: function($scope) {
      $scope.format = 'dd-MMM-yy';

      $scope.open = function() {
          $scope.status.opened = true;
      };

        $scope.status = {
            opened: false
        };
    }
  }
});

Plunker: PLUNKER

Problem courtesy of: Amarnath

Solution

There are multiple steps you need to make yet in order to complete your task. I will give you a good start regarding passing data to and from modal controller and setting proper date to datepicker.

First of all, make sure you isolate scope of the datepicker, it would make your life simpler in future. Then pass necessary model into this directive and bind it to datepicker (in datepicker template ng-model="model"):

angular.module('myApp').directive('dateDirective', function() {
    return {
        scope: {
            model: '=ngModel'
        },
        // ... rest of code is unchanged 
    }
});

Then you need to pass record object into modal controller. For this you would use resolve instruction of the modal:

$scope.edit = function(record) {
    $uibModal.open({
        resolve: {
            record: record
        },
        templateUrl: 'modaldialog.html',
        controller: 'ModalController'
    });
};

where in HTML you need to pass record to the edit function:

<button class="btn btn-primary" type="button" ng-click="edit(record)">edit</button>

Finally, in order to update the original record you would need to subscribe to resolved promise returned by $uibModal.open method:

.controller('ModalController', function($scope, $uibModalInstance, record) {

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

    $uibModalInstance.result.then(function() {
        angular.extend(record, $scope.record);
    });

    // ...
});

Note, that in modal controller you need to make a copy of the original record and use it in modal template. This is necessary because you want to affect original record only if Save is pressed - then you extend original model with changed data.

Demo: http://plnkr.co/edit/Hq0wFilQpZ26Ha2nyNxN?p=info

Adding new record from outside of controller

As for your second question, this is another interesting question. Since in your original code you stored records in the scope array, this is actually not really convenient design solution for adding new records. In real world you would likely have a separate model layer responsible for fetching existing and creating new records. In Angular you use services for this. So you need to register a new service object with methods to get, update and save records. In terms of MVC it would correspond to M-layer. Then whenever you want to add new record from any controller you would use this service which is singleton object same in every controller.

Here is an example of very simple service in your case:

angular.module('myApp').service('records', function() {

    this.data = [{
        'date': new Date(),
        'place': 'Bangalore'
    }];

    this.fetch = function() {
        return this.data;
    };

    this.save = function(record) {
        this.data.push(record);
    };
});

And finally, here is complete example for your problem which can save new records both from controller and from outside using services and also allows editing.

Demo: http://plnkr.co/edit/NxP0HJurZuLA7odzzdie?p=preview

Solution courtesy of: dfsq

Discussion

There is currently no discussion for this recipe.

This recipe can be found in it's original form on Stack Over Flow.