How can I dynamically assign ngModel in AngularJS?

I've created a directive with an isolate scope that maps a list of objects to inputs for modifying a specific property. However, I now aim to make this component more universal by allowing it to bind to deeply nested properties within each object.

For instance, consider a list of people with names spoken in various languages:


    var people = [
      {
        age: 31,
        multilang_attributes: {
          en: {name: 'John'},
          ru: {name: 'Иван'}
        }
      },
      {
        age: 29,
        multilang_attributes: {
          en: {name: 'Peter'},
          ru: {name: 'Пётр'}
        }
      },
    ];
  

I'd like to use my universal component on this list of people as shown below:

<input-list
    items="people",
    item-model="multilang_attributes[locale].name"
  ></input-list>

I attempted creating a scope property using `&` to execute the expression in the parent scope and then pass it to `ngModel`, but encountered issues. You can check out my attempt on plunkr here.

What would be the best approach to solve this task in Angular?

Answer №1

To handle this situation, one approach is to $parse the accessor-string and implement a getter/setter for the model. Here's how:


  1. Update the directive-html as follows:

    item-accessor="'multilang_attributes.' + app.locale + '.name'"

    This will create a reference like multilang_attributes.en.name.


  1. Modify the directive-code by doing the following:

    app.directive('inputList', function () {
      return {
        restrict: 'E',
        scope: {
          items: '=',
          itemAccessor: '='
        },
        template: '<ul><li ng-repeat="item in items"><input ng-model="getModel(item)" ng-model-options="{ getterSetter: true }" /></li></ul>',
        
        controller: function ($scope, $parse) {
          $scope.getModel = function(item) {
            return function (newValue) {
              var getter = $parse($scope.itemAccessor);
              
              if (arguments.length > 0) {
                getter.assign(item, newValue);
              }
              
              return getter(item);
            };
          };
        }
      };
    });
    

Try out the live demo here: http://plnkr.co/edit/VzFrBxNcsA5BarIVr6oG?p=preview

var app = angular.module('TestApp', [])

app.controller('AppCtrl', function AppController() {
    
  this.locales = ['en', 'fr']
  this.locale = 'en';
  
  this.people = [
    {
      age: 31,
      multilang_attributes: {
        en: {name: 'John (en)'},
        fr: {name: 'John (fr)'}
      }
    },
    {
      age: 29,
      multilang_attributes: {
        en: {name: 'Fred (en)'},
        fr: {name: 'Fred (fr)'}
      }
    },
  ];
  
});
  
app.directive('inputList', function () {
  return {
    restrict: 'E',
    scope: {
      items: '=',
      itemAccessor: '='
    },
    template: '<ul><li ng-repeat="item in items"><input ng-model="getModel(item)" ng-model-options="{ getterSetter: true }" /></li></ul>',
    
    controller: function ($scope, $parse) {
      
      $scope.getModel = function(item) {
        return function (newValue) {
          var getter = $parse($scope.itemAccessor);
          
          if (arguments.length > 0) {
            getter.assign(item, newValue);
          }
          
          return getter(item);
        };
      };
    }
  };
});
<!DOCTYPE html>
<html>
  <head>
    <script data-require="<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="1978777e6c75786b37736a5928372d372e">[email protected]</a>" data-semver="1.4.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.js"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" integrity="sha512-dTfge/zgoMYpP7QbHy4gWMEGsbsdZeCXz7irItjcC3sPUFtf0kuFbDz/ixG7ArTxmDjLXDmezHubeNikyKGVyQ==" crossorigin="anonymous">
  </head>

  <body ng-app="TestApp" ng-controller="AppCtrl as app">
    <div class="container">
      <select ng-model="app.locale" ng-options="v as v for v in app.locales"></select>
      <hr>
      
      <input-list
        items="app.people"
        item-accessor="'multilang_attributes.' + app.locale + '.name'"
      ></input-list>
      
      <hr>
      <pre>{{ app.people|json }}</pre>
    </div>
  </body>
</html>


Please keep in mind that using proper injection-syntax and controllerAs/bindToController would be advisable.

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

WebDriverError: The preference value for network.http.phishy-userpass-length in Firefox is not valid: exceeds maximum allowed length

Attempting to initiate a new test case using gecko driver (v0.15) with a specific Firefox profile in Protractor 5.1.1. I created the profile based on this guidance: Set firefox profile protractor Upon starting the execution through the protractor configur ...

Troubleshooting: Resolving the "npm ERR! missing script: start" Issue

I'm encountering the error below: npm ERR! missing script: start npm ERR! A complete log of this run can be found in: npm ERR! C:\Users\..\AppData\Roaming\npm-cache\_logs\2019-04-27T18_02_39_6 60Z-debug.log Th ...

Passing arguments to EJS view with Express

As a newcomer to Node.js/Express/EJS, I've made an interesting observation. I've realized that if I pass arguments from an Express request handler to an EJS view without specifying the argument name, it automatically assigns a name based on the v ...

What is the best way to assign a series of radio buttons to an array within an Angular controller's model?

Let's say I have a controller that contains an array property named 'houses'. I want to use ng-repeat to display this array on a table row with a set of radio buttons (true/false, etc.). How can I ensure that selecting any of these radio but ...

Vue JS i18next: Handling Single Translation String Fallbacks

Recently, I've been utilizing i18next, and I decided to set a fallback value for some translated strings in case they are not available in the selected language. Here's an example: en: base.json "yes": "yes" "no": "no" fr: base.json ...

Creating immersive experiences with fabric.js in fullscreen mode on canvas

Attempting to implement a fullscreen mode (using HTML5 fullscreen) for a canvas object created with Fabric.js. var canvas = fabricCanvas.getSelectionElement(); if(canvas.requestFullScreen) { canvas.requestFullScreen(); } else if(canvas.webkitRequestFu ...

Ways to verify if TypeScript declaration files successfully compile with local JavaScript library

I have recently updated the typescript definitions in HunterLarco/twitter-v2, which now follows this structure: package.json src/ twitter.js twitter.d.ts Credentials.js Credentials.d.ts My goal is to verify that the .js files correspond correctly ...

What is the best way to calculate the time elapsed between two consecutive double clicks using jQuery?

I am trying to calculate the time difference between two clicks on a single button. Here is my code snippet: <a href="#">click here</a> My current JavaScript code to capture the time of each click is as follows: var clickedTime = '&apos ...

Discord.js counter feature

Recently, I attempted to create my own counting system for my server inspired by bots like countr. Here is the code snippet I came up with: if (message.channel.id === "794733520458612736") { const numdb = db.get("numdb"); if (me ...

Exploring the integration of Ant Design Vue within HTML code

How can ant design vue be implemented in HTML? I attempted to install this library using npm and obtained the following files: antd.js, antd-with-locales.js.map, antd.js.map, antd-with-locales.min.js, antd.min.js, antd-with-locales.min.js.LICENSE.txt, ant ...

How can I use Angular forEach to retrieve both the name of a JSON key and its corresponding

Is there a way to retrieve both the key name and value of a JSON object in Angular using the forEach method? var institute = { "courses": [], "user_type": 3, "institute_name": "sd", "tagline": "sd", "overview": "sd", ...

Having trouble with adding data from an array of objects to an HTML element using jQuery's each method

I am currently facing an issue with looping through an array of objects using $.each in jQuery and trying to append the values to an <li>. Here is the relevant jQuery code: $(".sidebar").empty().append("<div>" + "<h5>Student Info</ ...

Real-time monitoring within a callback function in Angular 5

I need to ensure that a specific callback is executed only after receiving a response, starting from the line this.groupDefaultExpanded = -1; onwards. loadLoginDetails() { this.derivativeSpecService.getDerivativeDetails().subscribe( res => ...

The Child/Parent arguments in Typescript methods cannot be assigned

Why is this not working in TypeScript? class Parent { id: string = '' } class Child extends Parent{ name: string = '' } const fails: (created: Parent) => void = (created: Child) => { return }; const failsToo: ({ create ...

Obtain $stateParams excluding UI-navigation

Is there a way to access $stateParams without using a <ui-view> tag in the HTML? I'm trying to make this code function properly: .config([ '$locationProvider', '$stateProvider', function($locationProvider, $stateProvide ...

Failure to achieve success with jQuery Ajax

success in my AJAX call is not triggering at all, leaving me puzzled as to why. None of the alerts specified in the AJAX file are appearing. The form: <form onsubmit="check_reg();return false;" method="post" name="reg_form" id="reg"> <label ...

Employing the Map function in React leads to an error that is undefined

Upon simplifying my code, it seems that I am encountering an unresolved issue with the map function being used in different locations. I have noticed that code from examples running on platforms like Codepen does not work in my locally created react app p ...

Modify the JSON file stored on the local server

I am currently working on a project that is being hosted on a local server. My main objective is to be able to edit and save a JSON file that will contain the configurations for this project. I have succeeded in reading the file and accessing it using axio ...

Retrieving Filter Values with AngularJS - ngTable

Is there a way to retrieve the filter values of an ngTable in my controller, given that I have one in my view? ...

eliminating items from an array nested inside another array

****************UPDATED********************************************************* I am stuck trying to manipulate an array within another array and remove elements based on conditions. The main goal is to make changes without altering the original array of ...