1

So, we're in the process of bringing our Angular 1.3 scope soup application up to 1.5 standards. But we've noticed some strange behavior. When we pass a $scope variable into a component binding, it doesn't seem to correctly reflect any changes made within the component to the $scope variable.

Our $scope based controller:

app.controller('ParentCtrl', function ($scope) {

      $scope.dates = [...array of dates...]

      $scope.focusDate = new Date()

})

Our component tag:

   <section-dates dates="dates" focus-date="focusDate"></section-dates>

The component itself:

app.component("sectionDates", {
bindings: {
    dates: "=",
    focusDate: "="
},
controller: function () {
    this.onClickADate = function (date)
    {
        this.focusDate=date
    }
...
}

When a new date is clicked, the focusDate changes in the component, but not on the parent controller's $scope. Why is this?

1
  • have you tried using 'this' in the parent controller? Commented Nov 2, 2016 at 13:30

1 Answer 1

1

We're going through something similar on our end.

One important concept that took us some time to wrap our heads around was that all components have isolate $scope. So in theory this prevents the '$scope soup' problem you mentioned, but in practice this leads to the need to explicitly pass data into and out of components which can take some getting used to.

In general, you'll want to keep your data immutable, that is, prevent child components from changing data directly (except in some specific cases where two-way data binding improves UI).

The idea here is to pass data into components using one-way data binding like this:

...    
bindings: { 
    oneWayBindingInput: '<'
}, 
...

and then pass events back out of child components to then be handled by the parent:

...    
bindings: { 
    oneWayBindingInput: '<'
    onEventOutput: '&'
}, 
...

So in your case, you might try something like this:

Component tag (in parent template):

<section-dates dates="dates" on-update="handleUpdateEvent($event)"></section-dates>

Component:

app.component("sectionDates", {
bindings: {
    dates: "<",
    onUpdate: "&"
},
controller: function () {
    this.onClickADate = function (date)
    {
        this.onUpdate({$event: {date: date});
    }
...
}

Parent Controller:

app.controller('ParentCtrl', function ($scope) {
    $scope.dates = [...array of dates...];
    $scope.handleUpdateEvent = function(event) {
        $scope.date = event.date;
    };
})

Just a quick note. If the data you one-way data bind into the component is an object, it's properties will still be mutable. One-way data binding is not broken here, this is typical javascript behavior. Todd Motto discusses a great technique to overcome this by cloning the data to break the binding:

$onChanges() = function(changes) { 
    if(changes.user) { 
        this.user = angular.copy(this.user);
    }
};

For more examples see these references with useful component patterns: https://toddmotto.com/exploring-the-angular-1-5-component-method/ http://dfsq.info/site/read/angular-components-communication

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.