0

I have my main html file structured as follows:

<html ng-app='information'>
<body ng-controller="FirstController as first>
<div ng-controller="SecondController as second>

Following is the custom directive.

<!-- Custom Directive -->

<div id="information">

    <myOwnDirective ng-controller="ThirdController as thirdCtrl"></myOwnDirective>

  </div>

Directive is created as follows:

(function() {

    var app = angular.module('information', ['ui.bootstrap']);

    app.directive('myOwnDirective', function(){
    return {
        restrict: 'E',
        templateUrl: 'my-own-directive.html',

        };  
    });

This is my custom directive template:

<uib-accordion tag ng-repeat="info in first">

<form ng-submit="thirdCtrl.updateInformation()">

<div class="form-group">

<label for="someprop">Property</label> <input type="text" name="someprop"
            class="form-control" ng-model="info.propValue"
            ng-readonly="info.propValue">
 </div>

 <button type="submit" class="btn btn-success btn-lg btn-block">Click</button>
</form>

And here is my controller where there is the function that is to be invoked on ng-click in custom directive template.

(function() {
    angular.module('deviceInfo2').controller('FormController', [ '$scope','$http', function($scope) {               


        this.updateStatus =function () {

            console.log("Inside function");
        };
    }]);        
})();

I want to get data in my custom directive template on ng-click and pass it to the form controller but I cant seem to find way to get the data in there. I have tried exploring creating a service but it is not working. I think I am missing the scope. Can anyone point me in the right direction please. I have been stuck on this for a while and need to make some progress. Thank You.

user3044240
  • 621
  • 19
  • 33

2 Answers2

2

If you find yourself creating a directive that produces an entire web form, you should question your approach. Think smaller. A directive that does too much is not very reusable and you will often find yourself in this situation.

Instead of creating a directive that produces that entire web form, why not just create the web form as your view template? Why even have a custom directive?

If your answer is: "I want this web form to appear in multiple places within my app", consider using ng-include or ui-router to create a reusable state. A directive is a custom HTML element (or attribute), not a rubber stamp for huge chunks of code.

If you still need your custom directive to supply data to the parent directive, one good way to do it is to use the & data binding.

Working example: JSFiddle

JavaScript

angular.module('myApp', [])
  .controller('MyController', MyController)
  .controller('MySelectController', MySelectController)
  .directive('mySelect', mySelectDirective)
;

function MyController() {
  var vm = this;
  vm.setValue = function(value) {
    vm.value = value;
  };
}

function MySelectController() {
  var vm = this;
  vm.items = [
    {label: 'One', value: 1},
    {label: 'Two', value: 2},
    {label: 'Three', value: 3}
  ];
}

function mySelectDirective() {
  return {
    bindToController: {
      callback: '&'
    },
    controller: 'MySelectController',
    controllerAs: 'vm',
    link: postLink,
    scope: true,
    template: '<select ng-model="vm.selected" ng-options="item.value as item.label for item in vm.items"></select>'
  };

  function postLink(scope, iElement, iAttrs, vm) {
    scope.$watch(function() {
      return vm.selected;
    }, function(newValue) {
      if (angular.isDefined(newValue) && typeof vm.callback === 'function') {
        vm.callback({$value: newValue});
      }
    });
  }
}

HTML

<div ng-controller="MyController as vm">
  <my-select callback="vm.setValue($value)"></my-select>
  <p>Selected Value: {{vm.value}}</p>
</div>
Shaun Scovil
  • 3,905
  • 5
  • 39
  • 58
  • In my code templateUrl: 'my-own-directive.html', the html is referring to the web form. I am populating the form fields by fetching data from web service, which is working. That data is using the FirstController as first controller in my code sample above. It is just that, on submission of that form, the function associated with ng-submit, does not have access to the form fields. – user3044240 Feb 15 '16 at 15:35
  • Right, because your directive has an isolate scope. You can access the parent scope directly using $parent, as mentioned in another answer, but that is a bad idea because the directive makes an assumption about the parent. A directive should be able to be used in any app and just work, without having any implicit dependencies. The view using the directive should be supplying data to it, not the other way around. – Shaun Scovil Feb 15 '16 at 15:46
  • To put it another way, look at my example using & data binding and do that with your form submit function. Or better yet, don't use a custom directive that creates an entire web form because that's not what directives are for. – Shaun Scovil Feb 15 '16 at 15:59
  • I understand. Also, cant I use a service to get data from the directive's scope and use it the way I want. Will that be a bad approach. I am thinking that anytime I want to use the data that my directive will have, I can get it returned from a service and use it throughout the application. Any thoughts on that? – user3044240 Feb 15 '16 at 16:24
  • 1
    You're asking if I think using a service to cache data from a web form created by a directive is a good approach, but I'm saying I don't think a directive that creates a web form is a good approach. The problem you are having is a direct result of a bad design decision, in my opinion. Take the directive out of the equation and the problem no longer exists. – Shaun Scovil Feb 15 '16 at 16:49
  • So what prompted me to create a custom directive was that there was this big form having 20 fields, in my index.html. I thought after watching different videos is that the way to make code structured and easy to maintain is to use custom directive. Hence my decision to use a custom directive with its template as my web form. I ave been using Angular for just 2 weeks, so I apologise for my design. From your answer "Instead of creating a directive that produces that entire web form, why not just create the web form as your view template?", some guidance on that? I can give that a try. – user3044240 Feb 15 '16 at 17:03
  • 1
    I would suggest looking into ngInclude and ui-router, both of which I included link for in my answer. The first enables you to include partial HTML templates, while the second enables you to create a stateful web app. There are other options as well, but ngInclude is a native Angular directive and ui-router is widely used and well documented. Regarding custom directives: Keep them simple. I try to think of them as custom HTML elements, or special attributes that can be added to an element. They shouldn't generate a bunch of HTML, in my opinion. – Shaun Scovil Feb 15 '16 at 17:19
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/103521/discussion-between-user3044240-and-shaun-scovil). – user3044240 Feb 15 '16 at 18:23
  • Thank you for your advice. I removed the directive and used ng-include with a view template as source file. There were some `$scope` issues as `ng-include` creates its own scope, but I followed this question on SO (http://stackoverflow.com/questions/23686831/why-form-undefined-inside-ng-include-when-checking-pristine-or-setdirty) and was able to make it work. Thank for your help again. – user3044240 Feb 16 '16 at 19:19
0

If you have a scope variable in your controller scope, say

$scope.var1 = '';

Make sure the controller's partial is where you use your custom directive. Then in your directive's controller, you can use this below to push a value to the controller's scope:

$scope.$parent.var1 = 'value to pass';

The assumption here is your directive has an isolated scope. Your controller's scope is the parent of your directive's scope, so you can use the $parent keyword to pass data.

TonyW
  • 18,375
  • 42
  • 110
  • 183
  • 2
    Whilst this might work, it is not recommended to rely on `$parent` if at all possible. The main reason is that it breaks as soon as you introduce an intervening scope. There are other, better ways of communicating with parent scopes that don't require you to make a flawed assumption about the scope hierarchy. This should be an "if all else fails" plan. – GregL Feb 14 '16 at 23:23