简体   繁体   中英

Why does one of these AngularJS components successfully show the value of ng-model, while the other doesn't?

I'm trying to create a series of dynamic components that can be imported into one main component. Nesting components inside components is a lot easier when I can pass all objects into one binding versus creating multiple bindings for each object being passed. So I've created a plunkr to demonstrate what I'd like to achieve. Ideally, I'd like to be able to pass an object from a parent component into a child component's ng-model without having to create a separate binding.

Is this possible, can someone offer me any suggestions or explanations as to why the nested component only updates the model locally and not in the entire view?

Basically, if you look at the plunkr below, I'd like to get the cNestedComponent to function the same way that the cDirectComponent works, where the databinding updates from both within the component's template and outside of its template.

http://plnkr.co/edit/vusx9rm1DnkbBlNBGyZG?p=preview

MARKUP:

<h1> Data Comment => {{ data.comment }} </h1>
<c-direct-input plplaceholder="direct component" plmodel="data.comment" pltype="text"></c-direct-input>
<c-nested-input input-bindings="{type: 'text', model: 'data.comment', placeholder: 'nested component'}"></c-nested-input>

COMPONENTS:

app.component('cNestedInput', {
  template: '\
    <h2> Nested Component </h2>\
    <p style="display: block;"> {{ $ctrl.inputBindings.model }} </p>\
    <input type="{{ $ctrl.inputBindings.type }}" placeholder="{{$ctrl.inputBindings.placeholder}}" ng-model="$ctrl.inputBindings.model" />\
  ',
  bindings: {
    inputBindings: '='
  },
  controller: function($scope) {}
});

app.component('cDirectInput', {
  template: '\
    <h2> Direct Component </h2>\
    <p style="display: block;"> {{ $ctrl.plmodel }} </p>\
    <input type="{{ $ctrl.pltype }}" placeholder="{{ $ctrl.plplaceholder }}" ng-model="$ctrl.plmodel" />\
  ',
  bindings: {
    plplaceholder: '@',
    plmodel: '=',
    pltype: '@'
  },
  controller: function($scope) {}
});

========================================================

UPDATE

Based off of user Julien Tassin's feedback, I've created an updated plunker that is both cleaner, and I think demonstrates better what I'm going for:

https://plnkr.co/edit/cvYAdB?p=preview

The Direct Component examples are a clear way to achieving my goal, but I'd rather not have to list out every single binding as components get nested in each other. For example:

<c-nested-input input-bindings="$ctrl.input.inputBindings"/>

is a lot easier to type out then typing out this

<c-direct-input input-placeholder="{{$ctrl.inputPlaceholder}}" input-type="{{$ctrl.inputType}}" input-model="$ctrl.inputModel"/>\

every time I want to nest the input component inside a parent component.

Hopefully this update adds some further clarification as to what I'm looking for.

There is several issues that makes your example don't work :

First issue : the non-assignable property

You have a "non-assignable" issue. When you declare <c-nested-input input-bindings="{type: 'text', model: 'data.comment', placeholder: 'nested component'}"></c-nested-input> , you create a non-assignable property {type: 'text', model: 'data.comment', placeholder: 'nested component'} (by the way, you have a mistake on the 'data.comment' that should be data.comment ). When you are trying to assign a value to it in ngModel, it will fail because you can't affect a non-assignable expression, even a non-assignable expression property

So the solution is to set an assignable object inputBindings in your main controller and pass it to your component.

Second issue the data.comment reference

That's not all. If you try :

  $scope.data = {}
  $scope.inputBindings = {
    type: 'text',
    model: $scope.data.comment,
    placeholder: 'nested component'
  }

And pass it to your nested component :

<c-nested-input input-bindings="inputBindings"></c-nested-input>

It won't work as you want. Because when your nested will modify inputBindings.model, it won't be the same reference than data.comment. The = binding stand for inputBindings not its properties.

There is a no way to avoid this.

So you have to drop your data.comment and work as follow :

MARKUP :

<body ng-controller="MainCtrl">
  <h1> Data Comment => {{ inputBindings.model }} </h1>
  <hr/>
  <c-direct-input plplaceholder="direct component" plmodel="inputBindings.model" pltype="text"></c-direct-input>
  <c-nested-input input-bindings="inputBindings"></c-nested-input>
</body>

JS :

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

app.controller('MainCtrl', function($scope) {
  $scope.inputBindings = {
    type: 'text',
    placeholder: 'nested component'
  }
});

app.component('cNestedInput', {
  template: '\
    <h2> Nested Component </h2>\
    <p style="display: block;"> {{ $ctrl.data.comment }} </p>\
    <input type="{{ $ctrl.inputBindings.type }}" placeholder="{{ $ctrl.inputBindings.placeholder}}" ng-model="$ctrl.inputBindings.model" />\
  ',
  bindings: {
    inputBindings: '='
  },
  controller: function() {}
});

app.component('cDirectInput', {
  template: '\
    <h2> Direct Component </h2>\
    <p style="display: block;"> {{ $ctrl.plmodel }} </p>\
    <input type="{{ $ctrl.pltype }}" placeholder="{{ $ctrl.plplaceholder }}" ng-model="$ctrl.plmodel" />\
  ',
  bindings: {
    plplaceholder: '@',
    plmodel: '=',
    pltype: '@'
  },
  controller: function() {

  }
});

The example in the plunker .

My advice

I think that the cleaner way to create your component is an hybrid approach :

Something like :

JS :

app.component('cHybridInput', {
  template: '\
    <h2> Nested Component </h2>\
    <p style="display: block;"> {{ $ctrl.data.comment }} </p>\
    <input type="{{ $ctrl.options.type }}" placeholder="{{ $ctrl.options.placeholder}}" ng-model="$ctrl.ngModel" />\
  ',
  bindings: {
    ngModel: '=',
    options: '<'
  },
  controller: function() {}
});

HTML :

<c-hybrid-input ng-model="inputBindings.model" options="inputBindings"></c-hybrid-input>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM