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

Access the Vue instance from a different element instance

I've developed a leaflet map using vue.js. There's a method I've created called 'showSubmit' that needs to be triggered on the leaflet marker moveend event. Here's what I'm currently doing: this.map.markers.user.on("move ...

Value of an object passed as a parameter in a function

I am trying to use jQuery to change the color of a link, but I keep getting an error when trying to reference the object. Here is my HTML : <a onmouseover="loclink(this);return false;" href="locations.html" title="Locations" class="nav-link align_nav" ...

Using htaccess, we can redirect to the dist folder and enable AngularJS html5mode

Working on an AngularJS application, I am faced with the task of enabling html5mode to remove the hash /#/ from the URL. The minified version of the app can be found in the dist folder. QUERY: My objective is to redirect traffic to the dist folder while a ...

Troubleshooting the NullInjectorError in Angular - Service Provider Missing?

I'm facing an issue in my code where I have buttons that should trigger pop-ups displaying details as a list when clicked. However, every time I click the buttons, I encounter the error mentioned below. It seems like I am unable to access the desired ...

Action type is not recognized: modification

After browsing through multiple forums, I haven't found a solution that works for my particular issue with my VueJS app. The problem arises when I try to input something in my first component. Below is the code snippet: main.js import Vue from &apos ...

How can I dynamically retrieve the width of an image in React as the screen size changes?

I have successfully implemented an image hover effect on my website. When I hover over a certain text, an image appears. The image is responsive, but I am facing an issue where I want the width of the text to be equal to the width of the image. When I resi ...

What is the best way to move the numerical range value into a span element?

How can I implement the "update" function to retrieve the current slider value and transfer it to a Meter element with the message "Value: [current value]". The indicator color should be #ffff00 if the current value is at least 85, otherwise, it should b ...

Assistance with offsetting a jQuery drop down menu

This is the third jQuery script that I've been working on. While parts of it have been inspired by other scripts, I'm now focusing on implementing a specific feature. I've dedicated 4 hours to solving the issue of displaying the submenu on ...

I'm having trouble understanding why my Javascript validation suddenly stopped functioning. Can anyone assist me in troubleshooting this issue?

I have been working on this webpage for a school project for a few days, and it was running smoothly until about 10 minutes ago. The only change I made was adding an extra JavaScript validation. Now, when I try to register by clicking the "register" butt ...

Is there a way to detect when the mobile keyboard is open in a React application?

I am currently working with a Textfield that includes the Autofocus attribute. I am wondering if there is a method to detect when the keyboard opens in mobile view and then store this information in a boolean variable. https://i.stack.imgur.com/z0EtB.png ...

What is the browser location for storing JavaScript constants?

How can I retrieve the value of a constant using a string as its name, where the constant is an arrow function? const foo = (bar) => { return bar } I tried accessing it through the window object but got undefined: let fn = window['foo'] // ...

What is the method to designate the initial value of a <select> tag within a React component?

I am working with a basic <select> element that is looping through a set of options: <select onChange={(event) => this.setState({selectedOption: event.target.value }) }> { this.state.options.map(option => <option key={option.label} ...

Adding angular-scenario.js to my Rails Jasmine tests is causing them to malfunction and not run properly

I'm currently working on a simple website with a Rails back end and an AngularJS front end. I have Jasmine tests set up, but when I try to include angular-scenario.js for end-to-end testing, my tests don't even run. They simply refuse to start. ...

Exploring the possibilities with Rollbar and TypeScript

Struggling with Rollbar and TypeScript, their documentation is about as clear as AWS's. I'm in the process of creating a reusable package based on Rollbar, utilizing the latest TS version (currently 4.2.4). Let's delve into some code snipp ...

propagate the previous state using a variable

Currently, I am in the process of refactoring a codebase but have hit a roadblock. My main aim is to update the state when the onChange event of a select box occurs. Specifically, the parameter searchCriteria in my handleFilterChange function is set to in ...

How can you securely transfer the ARRAY OBJECT from JavaScript to PHP using Ajax?

I am attempting to send a Javascript Array Object via Ajax to PHP and then use that object as a true Array on the PHP side. My current approach has not been successful in terms of utilizing the incoming object as an Array within PHP; I can only parse it as ...

Is there a way to trigger $q notify without initiating a $digest cycle?

Within my application, the $digest cycle typically takes around 5ms to complete. I heavily utilize $q.defer with deferred.notify throughout my codebase, but I've encountered an issue. Each time deferred.notify is triggered, it schedules a new digest c ...

Analyzing the contents of a JSON file and matching them with POST details in order to retrieve

When comparing an HTTP Post body in node.js to a JSON file, I am looking for a match and want the details from the JSON file. I've experimented with different functions but I'm unsure if my JSON file is not formatted correctly for my needs or if ...

Error: Unhandled promise rejection: Trying to access a property of null (specifically 'branch') causing a TypeError

While developing a dashboard for an Angular application, I encountered an error when trying to access the dashboard: ERROR Error: Uncaught (in promise): TypeError: Cannot read properties of null (reading 'branch') TypeError: Cannot read propert ...

Utilize Protractor Selenium to extract content from a popup window

Having trouble capturing the text from a popup using Protractor with getText? The HTML structure can be found here. This popup only appears for a few seconds before disappearing. Can anyone assist me in retrieving the text from this popup? To retrieve the ...