consider plunker has behaviour i'm after.
the problem 1) think i'm doing wrong, , 2) uses deep watch, expensive , should unnecessary.
the example bit contrived. have in application featured typeahead/autocomplete control binds objects , renders them according expressions. plunker stripped down show parts should relevant question; how implement sort of thing correctly?
the main difficulty ensuring entering "something" in input , clicking button should propagate directive , cause span.outlet
update accordingly. ie, clicking alpha, , attempting change description should lead "selected: - something" showing on page.
if remove deep watch, won't happen unless replace $scope.selected new object reference rather changing property on existing object reference (see comment in mainctrl of plunker).
so, first requirement custom directive deals objects, , not strings.
the second requirement directive must able update span.outlet
whenever $scope.selected
object changes outside of custom directive.
thirdly directive should performant possible. , why i'm raising question. ng-model allready has shallow $watch internally, , i'm adding deep $watch on top of it, bad perf. there way without such deep $watch?
finally nice if didn't have have scope.ngmodel binding. feels dirty.
relevant markup:
<input ng-model="newdescription"> <button ng-click="setnewdescription(newdescription)">set description</button> <hr/> <my-list ng-model="selected" expression="{{::expression}}" options="options"></my-list>
relevant main controller code:
$scope.options = [ {key:'a', desc: 'alpha'}, {key:'b', desc: 'beta'}, {key:'g', desc: 'gamma'}, {key:'d', desc: 'delta'} ]; $scope.selected = $scope.options[1]; $scope.expression = '{{key}} - {{desc}}';
mylist directive:
app.directive('mylist', ['$interpolate', function($interpolate) { return { restrict: 'e', require: 'ngmodel', replace: true, template: '<div>selected: <span class="outlet"></span><ul><li ng-repeat="item in vm.items"><a href="" ng-click="vm.select(item)">{{vm.render(item)}}</a></li></ul></div>', scope: { ngmodel: '=', items: '=options' }, link: link, controller: ctrl, controlleras: 'vm', bindtocontroller: true }; function link(scope, element, attrs, ngmodelctrl) { var outlet = element.children()[0]; scope.vm.render = $interpolate(attrs.expression); ngmodelctrl.$formatters.push(function(modelvalue) { if (ngmodelctrl.$isempty(modelvalue)) return ''; else return scope.vm.render(modelvalue); }); ngmodelctrl.$render = function() { console.log('rendering', ngmodelctrl.$viewvalue); outlet.textcontent = scope.vm.render(ngmodelctrl.$modelvalue); }; ngmodelctrl.$parsers.push(function(value) { // gets called due $setviewvalue call in deep $watch. // don't have way of going string object, $modelvalue contains right thing. console.log('parsing', value); return ngmodelctrl.$modelvalue; }); // prefer if solve without deep watch on ngmodel! scope.$watch('vm.ngmodel', function(n, o) { if (angular.equals(n, o)) return; console.log('ngmodel $watch\r\n', o, '->\r\n', n); ngmodelctrl.$setviewvalue(scope.vm.render(n)); ngmodelctrl.$render(); }, true); } function ctrl($scope) { this.select = function(item) { console.log('selecting', item); this.ngmodel = item; }.bind(this); } }]);
any appreciated i've been trying wrap head around problem while now. thanks!
i not sure understand needs, prepared sample
app.directive('mylist', ['$interpolate', function($interpolate) { return { restrict: 'e', replace: true, template: '<div>selected: <span class="outlet"></span><ul><li ng-repeat="item in vm.items"><a href="" ng-click="vm.select(item)">{{vm.render(item)}}</a></li></ul></div>', scope: { selected: '=', items: '=options' }, link: link, controller: ctrl, controlleras: 'vm', bindtocontroller: true }; function link(scope, element, attrs, ctrl) { var outlet = element.children()[0]; scope.vm.render = $interpolate(attrs.expression); scope.$watch(function () { return ctrl.selected; }, function (value) { console.log('rendering', value); outlet.textcontent = scope.vm.render(value); }); } function ctrl($scope) { this.select = function(item) { console.log('selecting', item); this.selected = item; }.bind(this); } }]);
html:
<input ng-model="newdescription"> <button ng-click="setnewdescription(newdescription)">set description</button> <hr/> <my-list selected="selected" expression="{{::expression}}" options="options"></my-list>
update 2
sample ng-model
. don't need call $render function in $watch. it's called after model changing automatically.
also example how create directive ngmodel here.
Comments
Post a Comment