2013-03-08 8 views
15

Nach der Anwendung orderBy in meiner Liste der Eingabefelder, wenn ich ein Feld bearbeiten, beginnt es sofort zu sortieren und ich verliere Fokus. Ich habe versucht, im eckigen Code zu graben und fand heraus, dass sie etwas wie $ watch auf die Attribute von orderBy angewendet haben. Wenn also der Wert sich ändert, wird die Liste sortiert. Gibt es eine Möglichkeit, orderBy während der Bearbeitung zu deaktivieren? Ich möchte die Order nicht sortieren lassen, während ich Texte bearbeite. Jede Hilfe wirdDeaktivieren von orderBy in AngularJS beim Bearbeiten der Liste

Here is my plunker

Hinweis verstanden werden: Ich möchte verwenden orderBy & brauchen keine Alternative wie die Sortierung der Liste von jedem Controller selbst. Ich möchte nur die orderBy die Liste einmal beim Laden der Seite sortieren und dann ruhig bleiben.

+1

Wenn der Server meine Daten sendet, habe ich eine zusätzliche Spalte 'sortBy', die die Felder enthält, die ich sortieren möchte, dann verwendet ng-repeat das für orderBy. Dieses Feld befindet sich nicht in der Bearbeitung, ändert sich also erst, wenn ich die Zeile (n) erneut vom Server abrufe. I.e. Wenn ich einen Datensatz "speichere", werden die zurückkehrenden Daten aus der Datenbank abgerufen, einschließlich der sortBy, die die gleiche Zeile in $ scope ersetzt und die Sortierung auf der Seite erst dann aktualisiert, wenn die Bearbeitung abgeschlossen ist. –

Antwort

8

Sie könnten die Anweisung außer Kraft setzen, um den Zeitpunkt der Aktualisierung auf den Zeitpunkt zu ändern, an dem Sie die Neuordnung vornehmen möchten. Sie könnten auch einfach nicht ng-model verwenden und sich auf eine custom directive verlassen.

Diskutieren Sie das Überschreiben der Eingangsanweisung, um das Modellupdate so zu ändern, dass es durch das Ereignis blur ausgelöst wird. Werfen Sie einen Blick auf die fiddle.

Obwohl Sie die Richtlinie außer Kraft setzen könnten, sollten Sie dies nicht tun, und die beste Lösung, wie erläutert und am Beispiel von @Liviu T. im comments below wäre eine benutzerdefinierte Richtlinie zu erstellen, die das Ereignis entfernt keyup Bindung und fügt eine Unschärfe ein . Hier ist die Richtlinie Code, und hier ist Liviu's plunker:

app.directive('modelChangeBlur', function() { 
    return { 
     restrict: 'A', 
     require: 'ngModel', 
      link: function(scope, elm, attr, ngModelCtrl) { 
      if (attr.type === 'radio' || attr.type === 'checkbox') return; 

      elm.unbind('input').unbind('keydown').unbind('change'); 
      elm.bind('blur', function() { 
       scope.$apply(function() { 
        ngModelCtrl.$setViewValue(elm.val()); 
       });   
      }); 
     } 
    }; 
}); 
<input type="text" ng-model="variable" model-change-blur/> 

Leider, wie Angular Ereignisse sind nicht Namespaces, müssen Sie alle zuvor hinzugefügt Ereignis entfernen.

+2

Ich würde vorschlagen, dass Sie nicht überschreiben, sondern fügen Sie eine neue Direktive, die dieses Verhalten nur dort, wo Sie es brauchen http://plnkr.co/edit/yoUmEUMMzecNlbKmtgDq?p=preview –

+0

gute Lösung, aber wie in Kommentaren vorgeschlagen, sollten wir nicht überschreiben Sie bereits bestehende. sowie ich nicht wollen, um eine Bindung mit Blick auch für ein Keydown-Ereignis zu brechen –

+0

Ich stimme zu, und aktualisiert die Frage @LiviuT zu beherbergen. Lösung. Leider ist die einzige Möglichkeit, das "Keydown" -Update zu vermeiden, das Entfernen von Ereignissen, die bereits an das Element gebunden sind. Der gute Punkt von Livius Lösung ist, dass Sie nicht in anderen Momenten verlieren werden, in denen die Standard-Direktive verwendet wird. –

0

Es gibt keinen einfachen oder direkten Weg, um das zu erreichen, was Sie wollen, aber es gibt einige Optionen. Sie müßten ohne ng-Modell arbeiten und stattdessen das Unschärfe-Ereignis verwenden:

JS:

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

app.controller('MainCtrl', function($scope) { 
    $scope.items = [ 
    { name: 'foo', id: 1, eligible: true }, 
    { name: 'bar', id: 2, eligible: false }, 
    { name: 'test', id: 3, eligible: true } 
    ]; 

    $scope.onBlur = function($event, item){ 
    // Here you find the target node in your 'items' array and update it with new value 
    _($scope.items).findWhere({id: item.id}).name = $event.currentTarget.value; 
    }; 
}); 

app.directive('ngBlur', function($parse) { 
    return function (scope, element, attr) { 
    var fn = $parse(attr.ngBlur); 
    element.bind('blur', function (event, arg) { 
     scope.$apply(function(){ 
     fn(scope, { 
      $event : event, 
      arg: arg 
     }); 
     }); 
    }); 
    }; 
}); 

HTML:

<div ng-controller="MainCtrl"> 
    <div ng-repeat="item in items | orderBy:'name'" > 
    <input value="{{item.name}}" ng-blur="onBlur($event, item)"/> 
    </div> 
</div> 

Plunker.

Beachten Sie jedoch, dass dadurch die bidirektionale Bindung zwischen Modell und Ansicht unterbrochen wird.

+0

ich habe Bindung mit anderen Elementen in meiner Seite und ich denke, dass diese Änderungen nicht widergespiegelt werden, bis ich das Eingabefeld verwischen. Recht? Wäre es nicht netter, wenn wir die orderBy so einfach annullieren/bearbeiten könnten, dass sie keine Änderungen sehen würde, bis ich die Seite neu lade oder die Sortierung über eine beliebige Schaltfläche anwende. Deine Lösung ist ohne Zweifel gut, aber ich will nicht mit ng-Modell hacken. hoffe, du verstehst –

+0

'orderBy' Filter ist tatsächlich nicht auf Änderungen zu beobachten. Es ist die ng-repeat-Direktive, die Ihr 'items'-Array überwacht und wiederholt, sobald Ihr ng-Modell geändert wird. Sie können also entweder so vorgehen, wie ich es vorgeschlagen habe, oder Sie können Ihre benutzerdefinierte ng-repeat-Direktive erstellen, die nicht nach Änderungen Ausschau hält, sondern auf eine andere (explizite) Art und Weise ausgelöst wird. – Stewie

2

Nach @CaioToOn funktioniert, aber es bricht in Angular 1.2.4 (möglicherweise der gesamte 1.2.X-Zweig). Um es zum Laufen zu bringen, müssen Sie auch die Priorität der benutzerdefinierten Anweisung auf 1 festlegen (die Eingabeanweisung ist 0). Die Richtlinie wird dann:

app.directive('modelChangeBlur', function() { 
return { 
    restrict: 'A', 
    priority: 1, 
    require: 'ngModel', 
     link: function(scope, elm, attr, ngModelCtrl) { 
     if (attr.type === 'radio' || attr.type === 'checkbox') return; 

     elm.unbind('input').unbind('keydown').unbind('change'); 
     elm.bind('blur', function() { 
      scope.$apply(function() { 
       ngModelCtrl.$setViewValue(elm.val()); 
      });   
     }); 
    } 
}; 

});

Siehe http://plnkr.co/edit/rOIH7W5oRrijv46f4d9R?p=preview und https://stackoverflow.com/a/19961284/1196057 für verwandte Priorität zu beheben.

+0

das ist gut, aber ich habe ein Kontrollkästchen in der Tabelle und wenn ein Benutzer das Element überprüft, dann schießt die Zeile an den neuen sortierten Speicherort. Ich bin mir nicht sicher, was ich dagegen tun kann. –

10

Ich verwendete orderBy auf einer Liste, die unter Verwendung angular-sortable neu geordnet werden konnte, und stieß auf ein ähnliches Problem.

Meine Lösung war, die Bestellung manuell durchzuführen, indem der Filter orderBy bei der Initialisierung der Seite im Controller aufgerufen und anschließend bei Bedarf aufgerufen wurde, z. B. in einer Callback-Funktion, nachdem die Liste neu geordnet wurde.

+2

Könnten Sie ein Beispiel dafür geben ... –

+3

Es gibt ein Beispiel in der offiziellen Dokumentation: https://docs.angularjs.org/api/ng/filter/orderBy. Suche nach 'var orderBy = $ filter ('orderBy');'. Vergessen Sie nicht, '$ filter' zu injizieren. – Maxence

0

Sie können benutzerdefinierte Filter erstellen und nur bei Bedarf aufrufen. Beispiel, wenn Sie auf ‚Grid-Header‘ klicken für die Sortierung oder nach dem dynamischen Hinzufügen/Entfernen von Werten Array, oder klicken Sie einfach auf eine Schaltfläche (Refresh Grid)

Sie benötigen Inject Angular Filter und Art Filter

der Abhängigkeits
angular 
    .module('MyModule') 
    .controller('MyController', ['filterFilter', '$filter', MyContFunc]) 

    function ExpenseSubmitter(funcAngularFilter, funcAngularFilterOrderBy) { 
     oCont = this; 
     oCont.ArrayOfData = [{ 
     name: 'RackBar', 
     age: 24 
     }, { 
     name: 'BamaO', 
     age: 48 
     }]; 
     oCont.sortOnColumn = 'age'; 
     oCont.orderBy = false; 
     var SearchObj = { 
     name: 'Bama' 
     }; 

     oCont.RefreshGrid = function() { 
     oCont.ArrayOfData = funcAngularFilter(oCont.ArrayOfData, SearchObj); 
     oCont.ArrayOfData = funcAngularFilterOrderBy('orderBy')(oCont.ArrayOfData, oCont.sortOnColumn, oCont.orderBy); 
    } 
} 

und rufen Sie in HTML so etwas wie:

<table> 
    <thead> 
    <tr> 
     <th ng-click="oCont.sortOnColumn = 'age'; oCont.RefreshGrid()">Age</th> 
     <th ng-click="oCont.sortOnColumn = 'name'; oCont.RefreshGrid()">Name</th> 
    </tr> 
    </thead> 
    <tbody> 
    <tr ng-repeat="val in oCont.ArrayOfData"> 
     <td>{{val.age}}</td> 
     <td>{{val.name}}</td> 
    </tr> 
    </tbody> 
</table> 
2

Ein anderer Ansatz sein kann mit nicht lose Fokus zu beginnen. Wenn Ihr Hauptproblem ist, dass Ihr Fokus zu verlieren, dann statt orderBy zu deaktivieren, fügen Sie diese Anweisung, um Ihre Eingabe:

app.directive("keepFocus", ['$timeout', function ($timeout) { 
    /* 
    Intended use: 
     <input keep-focus ng-model='someModel.value'></input> 
    */ 
    return { 
     restrict: 'A', 
     require: 'ngModel', 
     link: function ($scope, $element, attrs, ngModel) { 

      ngModel.$parsers.unshift(function (value) { 
       $timeout(function() { 
        $element[0].focus(); 
       }); 
       return value; 
      }); 

     } 
    }; 
}]) 

Dann einfach:

<input keep-focus ng-model="item.name"/> 

ich weiß, ist dies nicht direkt beantworten Sie die Frage, aber es könnte helfen, das zugrunde liegende Problem zu lösen.

+0

Das war hilfreich und verbessert meine Situation, danke. Wenn es jedoch ein Kontrollkästchen in einer Sortierspalte gibt und Sie es überprüfen, dann schießt das Objekt in die richtige Reihenfolge. Irgendwelche Ideen, wie man das verbessert? –

0

Sie können die aktuelle Bestellung während der Bearbeitung einfrieren. Sagen Sie Ihre HTML wie folgt aussieht:

<tbody ng-repeat="item in items | orderBy:orderBy:reverse"> 
    <tr ng-click="startEdit()"> 
     <td>{{item.name}}</td> 
    </tr> 
</tbody> 

In Ihrem Controller schreiben Sie:

var savedOrderBy, savedReverse; 
$scope.startEdit() = function() { 
    $scope.items = $filter('orderBy')($scope.items, $scope.orderby, $scope.reverse); 

    for (var i = 0; i < $scope.items.length; ++i) { 
     if (i < 9999) { 
      $scope.items[i]['pos'] = ("000" + i).slice(-4); 
     } 
    } 

    savedOrderBy = $scope.orderBy; 
    savedReverse = $scope.reverse; 
    $scope.orderBy = 'pos'; 
    $scope.reverse = false; 
}; 

Bevor der Anwender die Bearbeitung beginnt, müssen Sie zunächst in genau der gleichen Reihenfolge die aktuellen Artikel sortieren, dass sie derzeit in der erscheinen Seite. Dazu rufen Sie den Befehl orderBy $ filter() mit den aktuellen Sortierparametern auf.

Dann gehen Sie über Ihre - jetzt sortierten - Elemente und fügen eine beliebige Eigenschaft (hier "pos") hinzu und setzen sie an die aktuelle Position. Ich zero-pad es so, dass die Position 0002 vor dem Jahr 0011 zusammenkommt. Vielleicht ist das nicht notwendig, keine Ahnung.

Normalerweise möchten Sie sich an die aktuelle Reihenfolge erinnern, hier in den Scope-Variablen "savedOrder" und "savedReverse".

Und schließlich sagen Sie eckig nach der neuen Eigenschaft "pos" zu sortieren und voilà ist die Tabellenreihenfolge eingefroren, weil sich diese Eigenschaft während der Bearbeitung einfach nicht ändert.

Wenn Sie mit der Bearbeitung fertig sind, müssen Sie das Gegenteil tun. Sie restaurieren die alte Ordnung von dem Umfang Variablen „savedOrder“ und „savedReverse“:

$scope.endEdit = function() { 
    $scope.orderBy = savedOrderBy; 
    $scope.reverse = reverse; 
}; 

Wenn die Reihenfolge der Array-Angelegenheiten scope.items $ für Sie, würden Sie auch sortieren sie müssen wieder in seine ursprüngliche Bestellung .

0

Verwenden Sie eine Bereichsvariable, um die Reihenfolge zu ändern. In diesem Fall rufen Sie beim Bestellen eine Funktion in Ihrem Controller auf, die den variablen Bestellwert in das Feld ändert, in dem Sie bestellen möchten, und wenn Sie bearbeiten, setzen Sie es zurück.

Beispiel:

<li ng-repeat="item in filtered = (items | filter:search) | orderBy:order:rever" > 

Also hier haben wir die Variable „Ordnung“ haben die ng-repeat zu sagen, von welchem ​​Feld die Liste bestellt werden müssen. Eine Schaltfläche ruft eine Funktion im Controller auf, die den Wert "Auftrag" ändert.

<button type="button" id="orderName" click="changeOrder('item.name')" >Name order </button> 

<button type="button" id="orderDate" click="changeOrder('item.date')" >Date order </button>` 

Und dann, in der Funktion change

$scope.order = param; 

Wo 'param' ist das Feld, das Sie von bestellen möchten. Wenn Sie nichts anderes tun, werden Sie das gleiche Problem haben Sie hatten, so dann, nachdem Sie den richtigen Wert der Ordnung Variablen zugewiesen haben gehen Sie

$scope.order = ""; 

, die die Bestellung zurücksetzt. Das Ergebnis ist, dass die Bestellung nur wirksam wird, wenn die Taste gedrückt wird, und dann wird sie nicht wieder neu bestellt, es sei denn, Sie drücken die Taste erneut. Dies kann geändert werden, so dass es erneut bestellt, wenn Sie zum Beispiel einen Artikel fertig bearbeitet haben, wie Sie es wollten.