Question

I need to add new items to a collection, that gets rendered with ngrepeat and using xeditable make it automatically editable.

BTW, I'm using the "manual trigger" method for xeditable.

Here it is the HTML

<h4>Angular-xeditable demo</h4>
<div ng-app="app" ng-controller="Ctrl" style="margin: 50px">
<div class="btn btn-default" ng-click="addNew()">+</div>
<ul>
  <li ng-repeat="item in array | orderBy:'-value'">
    <a href="#" e-form="itemForm" editable-text="item.field">{{ item.field }}</a>
    <i ng-show="!itemForm.$visible" ng-click="itemForm.$show()">edit</i>
  </li>
</ul>
</div>

and here the controller:

var app = angular.module("app", ["xeditable"]);

app.run(function(editableOptions) {
  editableOptions.theme = 'bs3';
});

app.controller('Ctrl', function($scope, $filter) {

  $scope.array = [
    {value: 1, field: 'status1'},
    {value: 2, field: 'status2'},
    {value: 3, field: 'status3'},
    {value: 4, field: 'status4'}
  ]; 

  $scope.addNew = function(){
    $scope.array.push({value:$scope.array.length+1, field: 'enter text here'});
    //MAKE IT EDITABLE????????
  }
});

Take a look to the issue in this fiddle: http://jsfiddle.net/dpamio/hD5Kh/1/

Was it helpful?

Solution

Here is a updated fiddle that works. Because of how the directive was written, and how ng-repeat works, it required an extremely hacky solution...

app.controller('Ctrl', function($scope, $filter, $timeout) {

  $scope.itemForms = {};

  $scope.addNew = function(){
    $scope.array.push({value:$scope.array.length+1, field: 'enter text here'});

     // Use timeout to force evaluation after the element has rendered
     // ensuring that our assignment expression has run
     $timeout(function() {
         $scope.itemForms[0].$show(); // last index since we sort descending, so the 0 index is always the newest
     })
  }

Background on how ng-repeat works: ng-repeat will create a new child scope for each element that is repeated. The directive assigns a variable on that scope using the string passed into e-form for its name (in this case itemForm). If it was smarter, it'd allow for expression evaluation for assignment. (Then we could assign it to the parent scope, and access it in the controller, but that's a different matter).

Since we don't have any way to access this child scope outside of the directive, we do something very bad. We use the mustache expression in a span of display none to assign the itemForm variable to the parent scope so that we can use it later. Then inside our controller we use the look up value to call the itemForm.$show() method that we expect.

Abstracting that bit of nastyness into an angular directive, we could write the following:

.directive('assignFromChild', function($parse) {
    return {
        restrict: 'A',
        link: function(scope, el, attrs) {
            scope.$watch(function() { return $parse(attrs.assignFromChild)(scope); }, function(val) {
                $parse('$parent.' + attrs.toParent).assign(scope, val);
            })
        }
    }; 
});

Allowing our HTML to go back down to:

<ul>   
  <li ng-repeat="item in array | orderBy:'-value'" assign-from-child="itemForm" to-parent="itemForms[{{$index}}]">
    <a href="#" e-form="itemForm" editable-text="item.field">{{ item.field }}</a>
    <i ng-show="!itemForm.$visible" ng-click="itemForm.$show()">edit</i>
  </li>
</ul>

Here is a fiddle with my final solution

OTHER TIPS

I found a very simple solution using ng-init="itemForm.$show()", that will activate the xeditable form when the new item is inserted.

Here's the updated jsFiddle answering the question: http://jsfiddle.net/hD5Kh/15/

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top