What purpose do controller.$viewValue and controller.$modelValue serve in the Angular framework?

I'm confused about the connection between scope.ngModel and controller.$viewValue/controller.$modelValue/controller.$setViewValue(). I'm specifically unsure of the purpose of the latter three. Take a look at this jsfiddle:

<input type="text" ng-model="foo" my-directive>

and:

myApp.directive('myDirective', function($timeout) {
    return {
        require: 'ngModel', 
        restrict: 'A',
        scope: { ngModel: '=' },
        link: function (scope, element, attrs, controller) {
            function log() {
              console.log(scope.ngModel);
              console.log(controller.$viewValue);
              console.log(controller.$modelValue);
            }
            log();
            controller.$setViewValue("boorb");
            log();
            scope.$watch('ngModel', function (val) {
               console.log("val is now", val); 
            });

            $timeout(function () {
                log();
            }, 2000);

        }
    }
});

Using the following controller:

function MyCtrl($scope, $timeout) {
    $scope.foo = 'ahha';
    $timeout(function () { 
        $scope.foo = "good";
    }, 1000);
}

The output appears as follows:

(index):45 ahha
(index):46 NaN
(index):47 NaN
(index):45 ahha
(index):46 boorb
(index):47 boorb
(index):53 val is now ahha
(index):53 val is now good
(index):45 good
(index):46 boorb
(index):47 boorb

Initially, controller.$viewValue did not match the value of foo. Also, the use of

controller.$setViewValue("boorb")
didn't affect scope.ngModel, and the update wasn't reflected in the HTML. It seems like there's no direct relationship between scope.ngModel and controller.$viewValue. For any intended action, it appears that using scope.ngModel along with watching those values would suffice. What is the benefit of utilizing controller.$viewValue and controller.$modelValue, or ensuring they are in sync with scope.ngModel?

Answer №1

When you use scope: { ngModel: '=' }, it creates a separate scope for the directive, meaning any changes made to 'foo' within the directive will not affect the parent scope of MyCtrl.

Furthermore, alterations made by using $setViewValue() will not be visible in the DOM until controller.$render() is executed, prompting Angular to update the DOM during the next digest cycle.

However, the NgModelController and its functions are primarily needed when developing specialized custom data-binding directives. For standard data input and validation tasks, there should be no requirement to utilize it. According to the documentation:

[NgModelController] offers services for managing data-binding, validation, CSS adjustments, as well as value formatting and parsing. It intentionally does not include any functionality related to DOM rendering or event handling. Other directives utilizing NgModelController for binding data to control elements should provide this DOM-related functionality. Angular takes care of this DOM logic for most input elements.

Answer №2

There seems to be some confusion here when it comes to adding a directive onto an existing one, specifically ngInput.

Instead, consider creating a new directive:

<my-custom-directive ng-model="value">Hello</my-custom-directive>

Define the value in the controller as:

$rootScope.value = 40;

The directive definition is:

.directive('myCustomDirective', function () {
  return {
    require: "ngModel", 

    // This is element-only directive
    restrict: "E", 

    // The template converts the directive into an input tag
    // 'inner' belongs to the scope of the *directive*
    template: "<input type='text' ng-model='inner'/>",       

    // The directive will have its own isolated scope
    scope: { }, 

    link: function (scope, element, attrs, ngModelCtrl) {
      // Formatter converts from modelValue (i.e. $rootScope.value) to 
      // view value (in this case, a string that is twice the model
      // value + '-'
      ngModelCtrl.$formatters.push(function (modelValue) {
        return ('' + (modelValue * 2)) + '-';
      });

      // Render sets up what is needed to display the view value
      // In this case, it sets the scope.inner so the inner
      // <input> can render it
      ngModelCtrl.$render = function () {
        scope.inner = ngModelCtrl.$viewValue;
      };

      // Changes in the inner should lead to changes in the view value
      scope.$watch('inner', function (newValue) {
        ngModelCtrl.$setViewValue(newValue);
      });

      // When the view value changes, it is parsed back into a model
      // value via parsers, which then sets the $modelValue, updating
      // the underlying model ($rootScope.value)
      ngModelCtrl.$parsers.push(function (viewValue) {
        var sub = viewValue.substr(0, viewValue.length-1);
        return parseInt(sub)/2;
      });
    }
  };
})

Test it out on Plunker.

Note that even though the directive's view value may be of a different type, typeof ugh remains "number".

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

Simulated external prerequisite

//user.js const database = require('database'); exports.createUser = function(req, res){ let user = req.body; if( validateUser(user) ) { database.insertUser(user); //redirect } else { //render new page with the user data ...

Verify the content of each file in a bulk upload before transferring them to the server

I am facing an issue with a form that has 3 separate file input fields. I need to validate their MIME types individually before uploading them to the server. The first two should only allow MP3 files, while the last one should only allow JPEG files. Is th ...

Create a PHP form that includes text and image inputs with the help of AdminLTE and integrates DropZone.js

I have been working with a template from adminLTE and you can check it out at the following link: . At this point, I am trying to upload images first and then use the image names as input text elements in my main form for submission. However, I have encou ...

Utilizing Conditional ng-style Binding in Angular for Data Presentation

<div ng-click="uploadwall($event)" ng-style="{{ wall.pic && {'background-image':'data:image/jpeg;base64,wall.pic'} || {'background-image': 'url(img/wall-bg.jpg)'} }}"> </div> The image displayed i ...

Troubleshooting problem with MongoDB queries within a for loop

I have an array of user emails obtained from the post data. My goal is to find the _id associated with each email. Here's the for loop I attempted: var studentIds = []; for (var i = studentEmails.length - 1; i >= 0; i--) { var email = studentEm ...

Obtaining Input Field Value in Angular Using Code

How can I pass input values to a function in order to trigger an alert? Check out the HTML code below: <div class="container p-5 "> <input #titleInput *ngIf="isClicked" type="text" class="col-4"><br& ...

What is the solution for fixing the '$ not defined error' in a .js file with $ajax code?

var example = document.createElement("SCRIPT"); example.src = "https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"; var nodeScript= document.createTextNode("function test() {console.log('test message');$.ajax({ type: \"POST&bs ...

Can you explain the significance of argument[0] in JavascriptExecutor?

I recently implemented the "How to handle hidden web elements using the JavaScript executor method". However, I am still unclear about how the method works. public static void selectDateByJS(WebDriver driver, WebElement element, String dateVal) { Javas ...

Learn the best way to handle special characters like <, >, ", ', and & in Javascript, and successfully transfer escaped data from one text box to another

I am seeking a way to use JavaScript to escape special characters. I came across a code snippet on this URL: http://jsperf.com/encode-html-entities. It successfully handles <>&, but I have encountered an issue with double quotes ("). The JavaScri ...

Tips on resolving the flickering issue in dark mode background color on NextJS sites

One problem I am facing is that Next.js does not have access to the client-side localStorage, resulting in HTML being rendered with or without the "dark" class by default. This leads to a scenario where upon page reload, the <html> element momentari ...

Can an inline try be implemented without including a catch block as demonstrated?

I have a scenario where I need to execute code that may result in an error without catching it. If the response is not valid JSON, then the desired outcome should be 0: Here is the code snippet I am considering: var overallProgress = try {JSON.parse(text ...

Having Trouble with Axios PUT Request in React and Redux

I'm having trouble making a PUT request to the server. I understand that for a PUT request, you need an identifier (e.g id) for the resource and the payload to update with. This is where I'm running into difficulties. Within my form, I have thes ...

Using the @ symbol in jQuery within an MVC framework can be achieved by first ensuring that

I'm attempting to incorporate the @ symbol into a JavaScript if condition, but I keep receiving an error. if (password.match(/(.*[!,%,&,@,#,$,^,*,?,_,~].*[!,%,&,@,#,$,^,*,?,_,~])/)) { alert('yes'); ...

Using a physical Android device to test and run a Meteor mobile application

I'm struggling to get my Meteor app to run on my Android device (LG G2). Despite searching online for a solution, I haven't come across any similar issues. I followed the instructions carefully, added the Android platform to my project, and ran i ...

Setting a header with the Axios module and Vue.js in Nuxt: A step-by-step guide

I've been attempting to set up an "Accept-Language" header for my SPA using vue.js (along with nuxt). Here's the approach I took, but unfortunately, it's not functioning properly. I specified that I am utilizing the axios module for nuxt. ...

"Error encountered when making a request to Google API using Ember.js, response remains

Trying to fetch place suggestions from Google API using Ember js. Below is the code snippet for the service module: fetch(){ let url=`https://maps.googleapis.com/maps/api/place/autocomplete/json?input=IL&types=geocode&key=API_KEY` return Ember.RSV ...

What are some npm web servers that support URL rewriting?

I am currently developing a single page application using AngularJS and require a local web server that can handle URL rewriting. I need all requests such as /home, /profile/1, /products?id=12 to serve the index.html file from the root directory. I have ...

Implementing Vue modal within a Laravel 5.2 foreach loop

I am facing a challenge with my Laravel blade template that uses a foreach loop to create a table with data. Each row has a link that triggers a modal when clicked. However, the issue is that clicking on any link activates the modal for every row instead o ...

Troubleshooting issue: Event-stream mapSync not functioning properly in async/await scenario

The await commands that I have marked as //******This await does not work */ do not appear to be functioning. It is unclear whether this issue is related to them being in an event stream or if it's a problem with the promise in the imported module. U ...

Guide on incorporating the Chain Pattern alongside the Self Revealing Module Pattern within JavaScript

I have come across the following code snippet: filtersManager = (function ($) { var that = this; function initialize() { // some tasks return that; }; function execute() { // some tasks return that; ...