Bidirectional data flow in AngularJS Directives

In an effort to create a dynamic form with different "widgets" based on field types and parameters stored in a database, I have been exploring directives for handling layout changes in response to various scenarios.

After experimenting with some examples, I've managed to come up with code that is somewhat functional:

HTML

<input type="text" ng-model="myModel" style="width: 90%"/>  
<div class="zippy" zippy-title="myModel"></div>

Directive

myApp.directive('zippy', function(){
    return {
      restrict: 'C',
      transclude: true,
      scope: { title:'=zippyTitle' },
      template: '<input type="text" value="{{title}}"style="width: 90%"/>',
      link: function(scope, element, attrs) {
            element.bind('blur keyup change', function() {
                scope.$apply(read);
            });

            var input = element.children();

            function read() {
                scope.title = input.val();
            }
        }
    }
});

While this solution seems to be functioning, albeit with some performance issues compared to standard AngularJS variable binding. I believe there is room for improvement. Can anyone provide insights on how to enhance this approach?

Answer №1

There's no need to manually trigger the $apply method in this case.

I made some adjustments to the Angular example you referenced and added the input field. I tested it out and it seems to be functioning correctly: http://jsfiddle.net/6HcGS/2/

HTML

<div ng-app="zippyModule">
  <div ng-controller="Ctrl3">
    Title: <input ng-model="title">
    <hr>
    <div class="zippy" zippy-title="title"></div>
  </div>
</div>​

JS

function Ctrl3($scope) {
  $scope.title = 'Lorem Ipsum';
}

angular.module('zippyModule', [])
  .directive('zippy', function(){
    return {
      restrict: 'C',
      replace: true,
      transclude: true,
      scope: { title:'=zippyTitle' },
      template: '<input type="text" value="{{title}}" style="width: 90%"/>',
      link: function(scope, element, attrs) {
        // Your controller
      }
    }
  });

UPDATE maxisam pointed out that using ng-model is more appropriate than directly binding the variable to the value:

<input type="text" ng-model="title" style="width: 90%"/>

You can check out the working version here: http://jsfiddle.net/6HcGS/3/

Answer №2

Do you mean something along the lines of this example?

I am using a similar approach to @Flek's example.
The only variation is the usage of ng-model='title'

The key to achieving two-way binding is through ng-model, as described in the documentation:

ngModel is a directive that enables Angular to facilitate two-way data binding. It collaborates with input, select, textarea elements. You can also create your own directives to leverage ngModel.

<input type="text" ng-model="title" style="width: 90%"/>

Answer №3

Here is a method for passing a callback parameter within a directive. This is how the controller template looks:

    <component-paging-select-directive
            page-item-limit="{{pageItemLimit}}"
            page-change-handler="pageChangeHandler(paramPulledOutOffThinAir)"
            ></component-paging-select-directive>

Now, let's take a look at the directive itself:

angular.module('component')
    .directive('componentPagingSelectDirective', [
        function( ) {
            return {
                restrict: 'E',
                scope: { 
                    // using the '=' for two way doesn't work
                    pageItemLimit:  '@', // the '@' is one way from controller
                    pageChangeHandler: '&'
                },
                controller: function($scope) {   
                    // passing value from this scope to controller method. 
                    // The controller method will then update the variable in the controller scope
                    $scope.pageChangeHandler({
                        paramPulledOutOffThinAir: $scope.pageItemLimit
                    })

                }, ...

In the controller:

angular.module('...').controller(...
        $scope.pageItemLimit = 0; // initial value for controller scoped variable

        // completing the data update by setting the variable in the controller scope 
        // equal to the one from the directive
        $scope.pageChangeHandler = function(paramPulledOutOffThinAir) {
            $scope.pageItemLimit = paramPulledOutOffThinAir;
        }

It's important to note the difference in function parameters for the directive (an object with parameter as keys), template ('unwrapped' keys from the parameter object in directive), and controller definition.

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

Executing JavaScript code within an AJAX request

Having a problem with an AJAX function that I need help solving: PAGE A represents the main page. PAGE X represents the content loaded via AJAX. RES A represents the results from PAGE A. RES B represents the new results loaded via AJAX. PAGE A initially ...

The resizing function on the droppable element is malfunctioning on Mozilla browsers

I've been working on making one div both droppable and resizable. Surprisingly, everything is functioning perfectly in Chrome but not in Firefox. If you'd like to see the issue for yourself, here is my jsFiddle demo that you can open in Firefox: ...

The standard TextField functionality was disrupted by the update to MUI v5

After typing a comment in the TextField and trying to click Done, nothing happens because the TextField still has focus. The first click removes the focus, while a second click is needed to complete the action. <TextField id={'generalCom ...

Ways to turn off JavaScript when reaching a certain breakpoint, similar to a media query

I am facing an issue with my <header> and <nav> blocks which are impacted by JavaScript. Is there a way to create a JavaScript solution similar to a media query that would deactivate this JavaScript code if the window width is less than or equa ...

What are the steps to successfully deploy a static website created with Next.js on Vercel?

Using the Next.js static site generator, I created a simple static site that I now want to deploy on Vercel. However, I keep encountering an error during the build process. While I have successfully deployed this site on other static hosting platforms befo ...

Adjusting and arranging multiple thumbnail images within a container of specific width and height

I need a user-friendly method to achieve the following. The thumbnails will not be cropped, but their container will maintain a consistent height and width. The goal is for larger images to scale down responsively within the container, while smaller image ...

Is it possible in HTML to detect *any* changes made to an input, not just those made by the keyboard?

Let's consider a scenario where we have an input element like this: <input id="myInput" type="text" /> The question now arises, how can we detect when the value of this input is changed programmatically (such as through $("#myInput").val("new ...

initiate a POST request using fetch(), where the data sent becomes the key of

Encountered an issue with sending a POST fetch request where the JSON String turns into the Object Key on the receiving end, specifically when using the { "Content-Type": "application/x-www-form-urlencoded" } header. I attempted to use CircularJSON to res ...

utilizing the target attribute within a form to open the link in the current page

I have been working on a web application and implemented a drop-down menu using a form to display information about the selected category. However, I noticed that when an option is selected, the link opens in a new window instead of the same window. Below ...

Bump the version number and request a message for the commit

Recently diving into the world of Grunt, I've been experimenting with merging grunt-bump and grunt-prompt. My intention is to have users prompted for a commit message that will then be added to the commit. I looked to this article for guidance in str ...

Sharing variables between components within the same file in Angular 6

I have an Angular 6 application that relies on APIs to function. Within my app, I need to display certain data from the APIs in an Angular Material Dialog. The dialog Component is located alongside the main component used to showcase the API data. However ...

Issue with Date generation in TypeScript class causing incorrect date output

I have a simple code snippet where I am creating a new Date object: var currentDate = new Date(); After running this code, the output value is: Sat May 11 2019 13:52:10 GMT-0400 (Eastern Daylight Time) {} ...

The issue with property unicode malfunctioning in bootstrap was encountered

I have tried to find an answer for this question and looked into this source but unfortunately, when I copy the code into my project, it doesn't display Unicode characters. section { padding: 60px 0; } section .section-title { color: #0d2d3e ...

Understanding how to utilize the scoped slot prop within a parent computed property

Currently, I have a closely connected pair of parent-child components utilizing a scoped slot. The child component sends data to the parent through a scoped slot prop (which we'll refer to as myScopedProp). Everything is functioning smoothly in this s ...

The height of a DIV element can vary based on

Looking at this div structure: DIV3 has a fixed height. The nested DIV5 will be receiving content from Ajax, causing its height to change. Additionally, there are some DHTML elements inside it that also affect the height. DIV5 has a fixed min-height set. ...

React Three Fiber - THREE.BufferGeometry.computeBoundingSphere(): The calculated radius is invalid. It is possible that the "position" attribute contains NaN values causing this issue

Currently, I am working on a website using Reactjs and React Three Fiber for 3D components. Out of the total 3 3D components, 2 are functioning properly. However, one of them suddenly stopped working more than 6 hours ago, and I have been unable to find a ...

NextJS's conversion of HTML to image is malfunctioning, resulting in the download of the identical reference

Having encountered difficulties with the html-to-image library while working on a Next.js project, I used the following code to convert images: import Image from "next/image"; import {toPng} from 'html-to-image' const divReferenceA = u ...

Displaying data in table rows using ng-repeat

I'm trying to organize the table data in a specific way, similar to the example shown below. I've managed to display the years at the top and the list of companies down the first column. However, I'm struggling with how to include the yield ...

Altering the properties of every item within a v-for loop's array

I'm currently exploring Vue's approach to writing JavaScript. Let's consider this situation: In the .vue template <button v-on:click="biggerFont()" class="btn btn-s btn-default" type="button" name="button">A</button> < ...

the reason for the blurring of shadows in Three.js

Typically, the shadow appears as shown below: https://i.sstatic.net/5L9N2.png However, upon adjusting the parameters of directionalLight.shadow.camera directionalLight.shadow.camera.left = -50 directionalLight.shadow.camera.right = 50 directionalLight.s ...