-1

End goal: Associate multiple email addresses, each with a frequency setting (daily,weekly,monthly), to a notification.

I am attempting to define a directive which acts on a button element, such that, when the button is clicked, it takes the email address from the input element and the frequency from the drop-down next to the button and inserts them into a list below the input+button and binds the dynamic list to a property on the controller so it can be sent to the server when the user submits the form.

The form is all wired up - thats not the question.

I just want some help in building the directive.

Here's what i have so far:

HTML:

<section ng-controller="notificationmanagement as vm">
    <input name="email" type="email"/>
    <select>
        <option>Daily</option>
        <option>Monthly</option>
        <option>Weekly</option>
    </select>
    <button data-cm-click type="button" class="btn btn-default"></button>
    <div ng-model="vm.Subscribers"></div>
</section>

Directive:

commonModule.directive('cmClick', function () {
    return function (scope, element) {            
        element.bind("click", function () {

            // Is this the best way to get the value of the EmailAddress & Frequency?
            var emailAddress = element[0].parentElement.parentElement.children[0].value;
            var frequency = element[0].parentElement.parentElement.children[1].value;
            var spanEmailTag = angular.element('<span>' + emailAddress + '</span>');
            var spanFreqTag = angular.element('<span>' + frequency + '</span>');
            angular.element(spanEmailTag).appendTo(element[0].parentElement.parentElement);
            angular.element(spanFreqTag).appendTo(element[0].parentElement.parentElement);

            // How to make sure each added email+frequency is available 
            // in the array bound to 'vm.Subscribers'
        });
    }
});

The structure of 'vm.Subscribers' should be something like this, in order for it be consumed by the server:

vm.Subscribers = [ {'EmailAddress':'joe@email.com', 'Frequency':'Daily'}, {'EmailAddress':'bob@email.com', 'Frequency':'Monthly'} ];

NOTE: I would ideally like to achieve this without relying on jQuery within the directive.

Any and all pointers/help/advice would be most appreciated!

JTech
  • 3,420
  • 7
  • 44
  • 51
  • I don't think the use of a directive is the right move here, at least as you have attempted it. It _could_ be done with a directive, but then it should contain all the data it requires, like email and frequency. Also, what is the meaning of a `
    ` with `ng-model`?
    – New Dev Jan 16 '15 at 02:21
  • The reason I chose a directive is because I have read from numerous sources that DOM manipulation should be done in directives e.g. [http://ng-learn.org/](http://ng-learn.org/2014/01/Dom-Manipulations/). The meaning of the `
    ` with ng-model` was my ignorant attempt at marking up an element into which the Email+Frequency instance elements can be inserted whilst at the same time binding their values to the `vm.Subscribers` array. @NewDev If directive is not the right move...any other suggestions?
    – JTech Jan 16 '15 at 03:33
  • Yes, DOM manipulations should only be done in a directive, but DOM manipulations should be just that - manipulation of the View. What you are trying to do is to get data from the DOM, which should belong with the ViewModel. You should really read this first: http://stackoverflow.com/questions/14994391/thinking-in-angularjs-if-i-have-a-jquery-background – New Dev Jan 16 '15 at 03:42

1 Answers1

2

If you want to encapsulate and reuse some functionality, then, by all means, use a directive.

But first, understand the MVVM (Model-View-ViewModel) paradigm and how Angular implements this.

For starters, assume that there is no View (and so, no DOM, directives, etc...) and that user inputs magically occur when something is exposed on the $scope (or, if you are using ControllerAs - as a property of the controller). Now, build you app's functionality starting from the controller. Define the data structure and the behavior with functions.

app.controller("notificationmanagement", function(){
   // list of subscribers. You could also populate it from the backend. 
   this.subscribers = [];

   // this is where the details of a new subscriber will be stored
   // until subscriber is added
   this.newSubscriber = {EmailAddress: null, Frequency: null}; 

   // action to add a susbscriber
   this.addSubscriber = function(){
      this.subscribers.push(this.newSubscriber);

      // clear the object (it's not necessary to declare all the properties)
      this.newSubscriber = {};
   }
});

That is, in a nutshell, all your app is doing. The controller doesn't (and shouldn't) care how this is displayed in the View. This is why DOM manipulation is frown upon, because it breaks separation of concerns.

Now, to the View. The View is mostly declarative:

<section ng-controller="notificationmanagement as vm">
   <input ng-model="vm.newSubscriber.EmailAddress" type="email>
   <select ng-model="vm.newSubscriber.Frequency">
      <option value="Daily">Daily</option>
      <option value="Monthly">Monthly</option>
      <option value="Weekly">Weekly</option>
   </select>
   <button ng-click="vm.addSubscriber()"> Add </button>

   <hr/>

   <h3>All subscribers:</h3>
   <div ng-repeat="s in vm.subscribers">
      <span>{{s.EmailAddress}}</span>
      <span>{{s.Frequency}}</span>
   </div>
</section>

Notice how ng-model directives bind input data to controller's data. Notice ng-click that invokes an action on the controller. Notice ng-repeat that iterates and creates DOM elements based on that data. The View is purely driven by data, which is referred to by ViewModel.

Understand this first, then move onto directives.

New Dev
  • 48,427
  • 12
  • 87
  • 129
  • Thanks for taking the time to explain the correct way to achieve the desired outcome! Very helpful...hope this helps someone else too. – JTech Jan 16 '15 at 05:38