0
votes

I want to embed a nested component in a page.

(A page is actually a controller that can be reached via the $routeProvider service)

And I want to bring data from the main component to its child component and vice versa - in order to make all of the components in the page and the page itself talking with each other in a full data binding.

I success to send data from parent to child with specific bindings attributes, however, I am not getting a way to bring data from child to parent.

// lobby.js - the main page.
// we can reach this page via browser by the $routeProvider service

app.config(($routeProvider) => {
  $routeProvider
  .when("/", {
    templateUrl : "screens/lobby/lobby.html"
  })
});

app.controller("lobby", ($scope, datepickerService) => {

  $scope.title = "Welcome to Lobby screen!";

  $scope.order = {};

  $scope.send = function() {
    console.log($scope.order);
  };

});

Lobby.html

<!-- This is lobby.html file -->
<!-- Which is the html template of the main page (lobby.js) -->

<link rel="stylesheet" href="screens/lobby/lobby.css">

<div class="lobby" ng-controller="lobby">

  <date-picker type="default" model="startDate"></date-picker>
  <date-picker type="default" model="endDate"></date-picker>

  <button type="button" name="button" ng-click="send()">Send</button>

</div>

Now as you can see, in the lobby.html file I have a nested component which is <date-picker></date-picker>. From parent I pass to this child component two attributes: type and model.

Now lets see this component functionality:

// datepicker.js component (actually defined as a directive)
// Initializing a datepicker plugin from jQuery UI Lib.

app.directive("datePicker", (datepickerService) => {
  return {
    templateUrl: "/shared/datepicker/datepicker.html",
    scope: {
      model: "@",
      type: "@",
    },
    link: function(scope, elements, attrs) {

      $(function() {
        setTimeout(function () {
          $("." + scope.model).datepicker({
            onSelect: function(value) {
              value = datepickerService.correct(value);
              $("." + scope.model).val(value);
              console.log(value);
            }
          });
        }, 200);
      });

    }
  }
});

datepicker.html

<!-- datepicker.html the datepicker html template -->
<!-- Successfuly getting the datepicker to be loaded and work -->

<box ng-show="type=='default'">
  <input type="text" class="{{model}}" readonly>
</box>

Now the problem: notice the:

// lobby.js
$scope.send = function() {
  console.log($scope.order);
};

in the lobby.js file.

I need this to send the actual startDate and endDate to a remote server. However I cannot access this data! $scope.order remains blank.

I have tried using components instead of directives I have tried ng-include I have tried more lot of things that I wont bother you with, since I have spent on it more than 3 days.

How can I work with nested components so all of the data will be shared through each of them, including the main page in AngularJS in order to create a scaleable modern app?

Thanks.

2
Mixing jQuery with AngularJS like this is asking for problems. Consider using an AngularJS plugin like uib-datepicker-popup, a Bootstrap component written in pure AngularJS by the AngularUI Team. - georgeawg

2 Answers

1
votes

For sending data from parent to child angular provides the $broadcast() method and for sending data from child to parent it provides the $emit() method.

More info: http://www.binaryintellect.net/articles/5d8be0b6-e294-457e-82b0-ba7cc10cae0e.aspx

0
votes

I think you have to reference your startDate and endDate within your order object. Right now it seems you save those directly on your $scope. Try this to verify:

console.log($scope.order, $scope.startDate, $scope.endDate);

add "order." in front your objects within the model attribute.

<!-- This is lobby.html file -->
<!-- Which is the html template of the main page (lobby.js) -->

<link rel="stylesheet" href="screens/lobby/lobby.css">

<div class="lobby" ng-controller="lobby">

   <date-picker type="default" model="order.startDate"></date-picker>
   <date-picker type="default" model="order.endDate"></date-picker>

   <button type="button" name="button" ng-click="send()">Send</button>

</div>

Also, you might also need to change the attribute definition of your component to use bidirectional binding. Use "=" instead of "@". @ only represents a copy of the value when getting passed to your component and not saved back to the original object.

...
scope: {
  model: "=",
  type: "@",
},
...

Update:

Please find my working Plunker here https://embed.plnkr.co/2TVbcplXIJ01BMJFQbgv/