Strategies for effectively transferring information from a directive template to a controller

I'm currently working on developing a date-picker that consists of two separate date pickers - one for the start date and another for the end date. Each datepicker element generates a template with two input tags. I want to be able to pass data from the input's value attribute to the controller. I initially tried defining fields within the inner scope using 2-way data binding (dateOne and dateTwo), but it seems to have no effect as there is no real data being passed between the two fields. My alternative approach involves using ng-model, however, my experience with this feature is limited and I am not familiar with the rules associated with it. Below is my code:

angular.module('directives', [])
    .directive('datepicker', ['$timeout',function ($timeout) {
        // Runs during compile
        return {
            scope: {
                id: '@',
                "class": '@',
                dateOne: '=',
                dateTwo: '='
            },
                restrict: 'E', // E = Element, A = Attribute, C = Class, M = Comment
                template: '<div id="{{id}}" class="{{class}}">'+
                        '<div class="date-wrapper">'+
                            '<label for="datepicker-start">From:</label>'+
                            '<div class="fieldWrapper">'+
                                '<input id="datepicker-start" type="date" placeholder="Select date" value={{dateOne}} />'+
                                '<a class="calendar"></a>'+
                            '</div>'+
                        '</div>'+
                        '<div class="date-wrapper">' +
                            '<label for="datepicker-end">To:</label>' +
                            '<div class="fieldWrapper">' +
                                '<input id="datepicker-end" type="date" placeholder="Select date" value={{dateTwo}}/>' +
                                '<a class="calendar"></a>' +
                            '</div>' +
                        '</div>'+
                        '</div>'
                        ,
                replace: true,
            link: function($scope, iElm, iAttrs, controller) {
                console.log('directive link function');
                console.log('directive iAttrs', iAttrs);
                $(".date-wrapper").each(function (index) {
                    console.log('directive index', index);
                    $input = $(this).find('input');
                    $btn = $(this).find('.calendar');

                    console.log('input', $input[0]);
                    console.log('btn', $btn[0]);

                    $input.attr('type', 'text');
                    var pickerStart = new Pikaday({
                        field: $input[0],
                        trigger: $btn[0],
                        container: $(this)[0],
                        format: 'DD/MM/YYYY',
                        firstDay: 1
                    });
                    $btn.show();
                });

            }
        };
}]);

------------------------Updated Code -----------------------------------

angular.module('directives', [])
    .directive('datepicker', ['$timeout',function ($timeout) {
        // Runs during compile
        return {
            scope: {
                id: '@',
                "class": '@',
                dateOne: '=',
                dateTwo: '='
            },
                restrict: 'E', // E = Element, A = Attribute, C = Class, M = Comment
                template: '<div id="{{id}}" class="{{class}}">'+
                        '<div class="date-wrapper">'+
                            '<label for="datepicker-start">From:</label>'+
                            '<div class="fieldWrapper">'+
                                '<input id="datepicker-start" type="date"  placeholder="Select date" ng-model=dateOne />' +
                                '<a class="calendar"></a>'+
                            '</div>'+
                        '</div>'+
                        '<div class="date-wrapper">' +
                            '<label for="datepicker-end">To:</label>' +
                            '<div class="fieldWrapper">' +
                                '<input id="datepicker-end" type="date" placeholder="Select date" ng-model=dateTwo />' +
                                '<a class="calendar"></a>' +
                            '</div>' +
                        '</div>'+
                        '</div>'
                        ,
                replace: true,
            link: function($scope, iElm, iAttrs, controller) {
                console.log('directive iAttrs', iAttrs);
                $(".date-wrapper").each(function (index) {
                    console.log('directive index', index);
                    $input = $(this).find('input');
                    $btn = $(this).find('.calendar');

                    console.log('input', $input[0]);
                    console.log('btn', $btn[0]);

                    $input.attr('type', 'text');
                    var pickerStart = new Pikaday({
                        field: $input[0],
                        trigger: $btn[0],
                        container: $(this)[0],
                        format: 'DD/MM/YYYY',
                        firstDay: 1
                    });
                    $btn.show();
                });

                $scope.$watch(iAttrs.dateOne, function (newValue, oldValue) {
                    console.log('newValue', newValue);
                    console.log('oldValue', oldValue);
                }, true);

            }
        };

Answer №1

Almost there! I've tackled a similar issue before, and here's how I approached solving it (I utilized the UI-Bootstrap Date picker in my scenario).

To send data from your directive to your controller, consider using callbacks instead of simple watches. Using = would require setting up watches in both the controller and directive, leading to unnecessary code and not ideal practice.

Here's what you need to do:

  1. In your directive definition object, bind a callback method/function using the & sign like this:

    scope: {
        onSelect: "&" // onSelect is our callback function in the controller
    }
    
  2. Next, assign the callback attribute a function tied to the controller's $scope, passing it a function reference (not a function call as seen in ng-change), for instance:

    <my-directive on-selected="onSelected"></my-directive>

  3. Define what the onSelected function should do; let's say we want to print the selected date:

    // inside the controller
    $scope.onSelected = function(time) {
        console.log("Time selected: ", time);
    }
    

Remember to pass the time argument from the directive to the controller as demonstrated in scope.onSelect(), which functions like a curried function. This means it will return a function once called. To test it, use angular.isFunction and call the curried function while providing the argument, such as scope.onSelect()(time).

scope.selectDate = function(time) {
    if (angular.isFunction(scope.onSelect())) {
        // Use isFunction to verify that the callback function points to a valid function object
        scope.onSelect()(time); // Pass the new selected date and time
      }
  }

You can check out a working example in this Plunk demo link.

Answer №2

  1. Update the ng-model to dateOne and dateTwo in the template.

  2. Consider creating a separate controller for this directive instead of handling logic within the link function.

app.directive('someDirective', function () { return { restrict: 'A', controller: 'SomeController', controllerAs: 'ctrl', template: '{{ctrl.foo}}' }; });

For further information, visit

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

"Encountered a 400 Error while trying to access the OpenAI

var openairequest = new XMLHttpRequest(); const payload = ({ "model": "text-davinci-003", "prompt": "say this is a test" }); console.log(payload) openairequest.open("POST",`https://api.openai.com/v ...

Removing data with sweetalert in a Ruby on Rails application

Hey there, I'm currently exploring the use of sweet alert js to enhance the appearance of my alert boxes. In my table, I have a specific data deletion feature that is triggered by a standard JavaScript alert confirmation. However, when attempting to i ...

How to dynamically insert a new row below a specific row with a sliding animation in jQuery

My task is to create a tree-like table structure where clicking on a specific row will reveal and add new rows below it. Specifically, I am designing the Category and SubCategory structure. Currently, I can append rows after a specified row but am struggl ...

Node text: three.js and ngraph.tree working together

I am currently working on developing a 3D network (network of people) for a browser using three.js and ngraph. The graph has been successfully created, but the nodes are currently displayed as squares. My goal is to replace these squares with the text "nod ...

Once you have reviewed the initial reply, proceed to send a follow-up message

Once I have received a response from the server for one string, my goal is to send the next JSON object. I need to verify the first object and then proceed to send the second one based on that verification. How should I handle the server's response? ...

Continuously providing service within the application

In the process of developing my app, I have implemented a feature that sends the user's geolocation data to the server (given proper permissions). Currently, this geolocation data is only pushed to the server when the user navigates to the account pag ...

updating the model value in AngularJS2 when the input value is modified

Looking to update a model's value based on user input without relying on the blur event. Unsure of the best approach for this task. Below is the code snippet I have attempted: <div *ngFor='let l of list'> <input #temp [value]= ...

When attempting to call a recursive method in Vue with a changing `this` object, an error is thrown: "RangeError: Maximum call stack size exceeded"

Update integrate codePen into the project. https://codepen.io/jiaxi0331/pen/xxVZBMz Description encountered an issue while trying to call the parent method recursively Code export default { methods: { dispatch(componentName, event, value) { ...

The Arrow notations don't seem to be functioning properly in Internet Explorer

Check out my code snippet in this JSFiddle link. It's working smoothly on Chrome and Mozilla, but encountering issues on IE due to arrow notations. The problem lies within the arrow notations that are not supported on IE platform. Here is the specifi ...

The backbone module is experiencing formatting issues

I'm new to learning backbone.js. I've created the example below, but unfortunately, it's not functioning properly. Can someone please help me understand why? The goal is to simply display the name within my div element. (function($) { ...

Comparing the benefits of using ajax to switch pages versus the traditional method of loading pages

Is it advisable to intercept all internal links and load the target page using ajax? The new history API in HTML5 allows for changing the URL in the address bar as well. Are there any drawbacks to this approach compared to the old traditional way of lett ...

Converting an array of date strings to a single string in JavaScript

Here is the JSON format I received with dynamic data: {"range":["2018-07-23T16:03:26.861Z","2018-07-23T16:03:26.861Z"]} Now, I need to convert this into the following format: range(20180723,20180723) Below is my code snippet : var data:Date[] = {"rang ...

I am attempting to incorporate a List View within a Scroll View, but they are simply not cooperating. My goal is to display a collection of items with additional text placed at the bottom

This is how it should appear: item item item item additional text here I am trying to create a layout where the list is in List View for benefits like virtual scrolling, but the entire layout needs to be within a Scroll View. I want to be able to con ...

The issue here is that "onreadystatechange" in JS Ajax is not defined, even

For the past day, I've been struggling with this issue and going in circles. Any help would be much appreciated :-) Synopsis I'm facing a challenge with asynchronous AJAX calls to CGI using resolver and FQDN variables to obtain DNS resolution ...

Guide to utilizing jQuery for setting values in all subsequent rows of a table

I have a table with 15 rows of text boxes labeled as text1, text2, and so on up to text15. If I enter text into, let's say, the 5th textbox, I want that same text to be automatically copied into all subsequent textboxes from the 6th through the 15th. ...

How to retrieve the ID of a parent sibling using jQuery DataTables

I have encountered a peculiar issue while trying to retrieve the ID of a table's parent sibling. Prior to initializing jQuery DataTables, obtaining the table's ID poses no problem. However, once it is initialized and the table is built, retrievin ...

Is there a way to invoke a high-order function multiple times within the code?

const x = { a:1 }; (function q({a}){ console.log(a); return a; })(x); // unable to execute console.log(q(x)) I'm encountering an issue trying to run line 6. Why is this happening? The console works in line 3 when I remove line 6: Error: q is n ...

IE11 displays a white screen when in fullscreen mode

I am currently working on a three.js webGL application and attempting to make it go fullscreen using the following code: launchFullscreen(document.documentElement); function launchFullscreen(element) { if(element.requestFullscreen) { ele ...

Issue with form validation, code malfunctioning

I am struggling to figure out why this validation isn't working. Here is the HTML code I'm using: <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type='text/javascript' src="scripts. ...

Adjusting the transparency level of the map outside a circular zone in the Google Maps JavaScript API v

Currently, I am adding a circle to my map: var optionsCircle = { center: latlang, map: map, radius: 1000, fillOpacity: 0.1, strokeWeight: 0 }; this.circle = new google.maps.Circle(optionsCircle); Instea ...