Search code examples
javascripthtmlangularjsangularjs-scope

Why can't I set input values when ng-model is used?


I'm trying to populate some default fields in a form. The AngularJS way as far as I know is to use the controller to set these values. I'm using components - which I'm starting to regret given that most examples online don't use this layout.

component.js

angular.
  module('patientInfo').
  component('patientInfo', {
    templateUrl: 'patient-info/patient-info.template.html',
    controller: ['$http', '$routeParams',
      function PatientInfoController($http, $routeParams, $scope) {
        var self = this;
        this.patientId = $routeParams.patientId;

        $http.get("patients/" + this.patientId + ".json").then(function(response) {
          self.patient = response.data;
        });
      }
    ]
  });

template.html

<div class="container-fluid">
  <h1>{{$ctrl.patient[0].first_name}} {{$ctrl.patient[0].last_name}}</h1>
  <div class="col-md-2">
    <!--Sidebar content-->
  </div>

  <div class="col-md-10">
    <!--Body content-->
    <form action="" >
      <p>First Name: <input type="text" ng-model="first_name"></p>
      <p>Last Name: <input type="text" ng-model="last_name"></p>
      <p>Patient Date of Birth: <input type="text" ng-model="patient_dob" ></p>
    </form>
  </div>
</div>

What I'd like to accomplish is to populate all those fields (name, dob) with elements from a JSON file. My problem is likely two-fold:

  1. When I write $scope.first_name = "anything" I'm told Cannot set property 'first_name' of undefined. Why is this the case?
  2. Assuming I figure out the first item, how would I go about using fields from the JSON file (self.patient or response.data)? Could I write: $scope.first_name = self.patient[0].first_name? My intuition is no, so what would be the proper way to both reference the JSON data inside my controller?

Bonus question: What are the idiosyncrasies that one should be aware of when using a component like this as opposed to the more traditional controller definition? I'm relatively new to web development and am finding myself overwhelmed by the number of ways to accomplish the same thing.

Sample response data would just be a JSON array with a single value:

[{"first_name": "John", "last_name": "Doe", "hospital_name": "Mayo Clinic","dob": "01/01/01"}]

Solution

  • as first question was answered in the comments, let's move to the second one.

    The answer is YES, you can write it like:

    Object.assign

    $http.get("patients/" + this.patientId + ".json").then(function(response) {
            //this will copy all properties from response.data[0] into $scope
            Object.assign($scope, response.data[0]);
        });

    And then you'll be able to see data into that fields after you get the request. But sometimes the $digest cycle (angular.js thing that updated all the values in the html) is not reacting to async calls, so you could try to use this code

    angular.
      module('patientInfo').
      component('patientInfo', {
        templateUrl: 'patient-info/patient-info.template.html',
        controller: ['$http', '$routeParams', '$scope', '$timeout',
          function PatientInfoController($http, $routeParams, $scope, $timeout) {
            var self = this;
            this.patientId = $routeParams.patientId;
    
            $http.get("patients/" + this.patientId + ".json").then(function(response) {
              $timeout(function() {
                Object.assign($scope, response.data[0])
              });
            });
          }
        ]
      });

    In the last snippet I've added the $timeout directive from angular that triggers the $digest cycle after the execution