Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid "sausage" type binding when having nested model

Tags:

angularjs

I have nested model and I am trying to avoid vm.someObject.someChild.ChildOfChild.name type of situations. Is there a way to set the model for outer <div> so that I can instead do ChildOfChild.name or even name. In Silverlight this was called DataContext. I put "vm" on the $scope, but in html I would like to avoid having to type the full path to attribute.

For example:

 <div>
    {{someObject.Id}}
    <div>
        {{someObject.name.first}}
        {{someObject.name.last}}
    </div>
     <div>
            {{someObject.someChild.name.first}}
     </div>
 </div>

I would like to do something like this

  <div datacontext = someObject>
    {{Id}}
    <div datacontext = name>
        {{first}}
        {{last}}
    </div>
     <div datacontext = someChild.name>
            {{first}}
     </div>
 </div>
like image 727
epitka Avatar asked Jul 23 '26 17:07

epitka


2 Answers

You can do this with a custom directive.

HTML:

<div ng-app="myApp" ng-controller="myCtrl as ctrl">
  <div>
      Access from deepObj: {{ctrl.deepObj.one.two.three.four}}
  </div>
  <div scope-context="ctrl.deepObj.one.two.three">
      Access from third level: {{four}}
  </div>
</div>

JS:

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

var myCtrl = function() {
    this.deepObj = {one: {two: {three: {four: "value"}}}};
};

myApp.directive('scopeContext', function () {
    return {
        restrict:'A',
        scope: true,
        link:function (scope, element, attrs) {
            var scopeContext = scope.$eval(attrs.scopeContext);
            angular.extend(scope, scopeContext);
        }
    };
});

See the documentation on $compile for information on what scope: true does.

Make sure you don't call the directive something like data-context as an attribute starting with data- has a special meaning in HTML5.

Here is the plunker: http://plnkr.co/edit/rMUQlaNsH8RTWiRrmohx?p=preview

Note that this can break two-way bindings for primitive values on the scope context. See this plunker for an example: http://plnkr.co/edit/lCuNMxVaLY4l4k5tzHAn?p=preview

like image 78
rob Avatar answered Jul 25 '26 10:07

rob


You could try/abuse ng-init

Try ng-init, you'll have one more ., but it's better than the other answer I've seen proposed:

<div ng-init="x = foo.bar.baz">
   {{x.id}}
   {{x.name}}
</div>

BUT Be warned, doing this actually creates a value on your scope, so doing this with something like ng-model if you're reusing the same name, or in a repeater, will produce unexpected results.

Why a custom directive for this probably isn't a good idea

What @rob suggests above is clever, and I've seen it suggested before. But there are issues, which he touches on, in part at least:

  1. Scope complexity: Adding n-scopes that need to be created (with prototypical inheritence) whenever views are compiled.
  2. View processing complexity: Adding an additional directive (again for no real functional benefit) that needs to be checked on each node when the view is compiled.*
  3. Readability? The next Angular developer will likely less readable because it's different.
  4. Forms Validation: If you're doing anything with forms in Angular, this might break things like validation.
  5. ng-model woahs: Setting things with ng-model this way will not be at all intuitive. You'll have to use $parent.whatever or $parent.$parent.whatever depending on how may contexts deep you are.

* For reference, views are $compiled more than you think: For every item in a repeater, whenever it's changed for example.

A common idea that just doesn't jive with Angular

I feel like this question comes up frequently in StackOverflow, but I'm unable to find other similar questions ATM. ... regardless, if you look at the approaches above, and the warnings given about what the side effects will be, you should be able to discern it's probably not a good idea to do what you're trying to do just for the sake of readability.

like image 20
Ben Lesh Avatar answered Jul 25 '26 09:07

Ben Lesh



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!