I'm trying to clean up some of my HTML code by creating an AngularJS directive for sorting columns in my tables. When I click on the heading, no errors are logged, but nothing happens, the column isn't reordered.
directive
Glenn.directive('sort', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
scope.predicate = 'title';
scope.reverseSort = false;
element.on('click', function() {
scope.predicate = attrs.sort;
scope.reverseSort != scope.reverseSort;
});
}
}
});
html
<th>
<a sort="title" href="">Title
<span ng-show="predicate == 'title'">
<span ng-show="!reverseSort"><i class="fa fa-caret-up"></i></span>
<span ng-show="reverseSort"><i class="fa fa-caret-down"></i></span>
</span>
</a>
</th>
Where am I going wrong here? Do I need to use something like $apply to make the sorting changes appear in the dom?
Got the directive to work, just needed to add scope.$apply(); to the end.
Glenn.directive('sort', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
scope.predicate = 'title';
scope.reverseSort = false;
element.on('click', function() {
scope.predicate = attrs.sort;
scope.reverseSort != scope.reverseSort;
scope.$apply();
});
}
}
});
i can suggest add sorting by filter and not by directive
add the follwing code to your filters.js:
.filter('orderObjectBy', [function() {
return function(items, field, reverse) {
var filtered = [];
angular.forEach(items, function(item) {
filtered.push(item);
});
filtered.sort(function (field_a, field_b) {
var result = (parseFloat(field_a) - parseFloat(field_b));
if (isNaN(result)) {
if (field_a > field_b)
{
return 1;
}
else if (field_a < field_b)
{
return -1;
}
else
{
return 0;
}
}
return result;
});
if(reverse) filtered.reverse();
return filtered;
};
}]);
on html:
<div id="table-title-publisher" data-ng-click="orderByField='name'; reverseSort = !reverseSort;"></div>
on your repeater (in case it's a table use tr otherwiser li for list) please add:
Related
When I use the code below in a directive using AngularJS don't work.
angular.element(document.querySelector(id)).removeClass("ng-hide")
Don't work, but when I use it on controller, works well, the question is: why ?
Here a example https://codepen.io/krekto/pen/JjoebNp
app.directive('filterList', function($timeout) {
return {
link: function(scope, element, attrs) {
// var li = Array.prototype.slice.call(element[0].children);
var el = element[0];
function filterBy(value) {
// li.forEach(function(el) {
el.className = el.textContent.toLowerCase().indexOf(value.toLowerCase()) !== -1 ? '' : 'ng-hide';
// });
//verify if element is grou
if(el.id.substring(0,5) == 'group'){
//if group is not hide, need show all layers from this group
if(el.className != 'ng-hide'){
// console.log(el.className)
var lyrs = Array.prototype.slice.call(el.children);
lyrs.forEach(function (lyr) {
// console.log(lyr)
angular.element(document.querySelector("#"+lyr.id)).removeClass("ng-hide");
// angular.element(document.querySelector("#"+lyr.id)).addClass('py-1 ng-show');
// console.log(document.querySelector("#layer2"));
});
}
}
}
scope.$watch(attrs.filterList, function(newVal, oldVal) {
if (newVal !== oldVal) {
filterBy(newVal);
}
});
}
};
});
<ul filter-list="search" id="group1">Fruits1
<li filter-list="search" id="layer1">Banana</li>
<li filter-list="search" id="layer2">Apple</li>
<li filter-list="search" id="layer3">Orange</li>
</ul>
Here's a plunkr with my problem: http://plnkr.co/edit/Sx830ekQyP7YBqmRB4Nd?p=preview
Click "Open", then click on "5". Notice how it changes to "test"? Now, type something into Body. It'll either say "Say a little more..." or "Now for the title". Either way, click the button again, and notice how it doesn't change to "test"? Why not? If I remove the directive, the button changes to "test" with or without text in the body.
I know this has to do with the scope in the directive, but I don't understand what exactly is wrong. Can you explain? Thanks.
angular.module('plunker', ['ngDialog']).controller('MainCtrl', function($scope, ngDialog) {
//$scope.submitPostValue = "OK";
$scope.submitPost = function() {
$scope.submitPostValue = 'test';
};
$scope.open = function () {
console.log('open');
$scope.submitPostValue = '5';
ngDialog.openConfirm({
template: 'postModal',
showClose: true,
trapFocus: false,
scope: $scope,
}).then(function (success) {
}, function (error) {
});
};
}).directive('bodyValidator', function () {
return {
require: 'ngModel',
link: function (scope, element, attr, ctrl) {
function customValidator(ngModelValue) {
if(ngModelValue.length > 0){
if(ngModelValue.length < 10) {
scope.submitPostValue = "Say a little more...";
scope.bodyValid = false;
}
else {
scope.bodyValid = true;
if(scope.titleValid)
scope.submitPostValue = "Submit";
else
scope.submitPostValue = "Now for the title..."
}
}
else {
scope.submitPostValue = "Enter a body...";
scope.bodyValid = false;
}
return ngModelValue;
}
ctrl.$parsers.push(customValidator);
}
};
});
Try to wrap all your variables into an object.
Define $scope.obj = {}; first and change all your scope.submitPostValue to $scope.obj.submitPostValue. In your HTML, change ng-value='submitPostValue' to ng-value=obj.submitPostValue.
I'm working with AngularJS. How can I search by Code? My current implementation searches what ever you type: id , name, city, code... How can I search only by code?
app.js:
var app = angular.module('stack', []);
app.directive('filterList', function ($timeout) {
return {
link: function (scope, element, attrs) {
var td = Array.prototype.slice.call(element[0].children);
function filterBy(value) {
td.forEach(function (el) {
el.className = el.textContent.toLowerCase().indexOf(value.toLowerCase()) !== -1 ? '' : 'ng-hide';
});
}
scope.$watch(attrs.filterList, function (newVal, oldVal) {
if (newVal !== oldVal) {
filterBy(newVal);
}
});
}
};
});
example on jSFiddle
var app = angular.module('stack', []);
app.directive('filterList', function ($timeout) {
return {
link: function (scope, element, attrs) {
var td = Array.prototype.slice.call(element[0].children);
function filterBy(value) {
td.forEach(function (el) {
el.className = el.cells[3].textContent.toLowerCase().indexOf(value.toLowerCase()) !== -1 ? '' : 'ng-hide';
});
}
scope.$watch(attrs.filterList, function (newVal, oldVal) {
if (newVal !== oldVal) {
filterBy(newVal);
}
});
}
};
});
If you have any text box to write the code and filter based upon on that, then you can use | inbuilt filter of angular like this,
<div ng-repeat="product in products | filter:{ colour: by_colour }">
Hope this will help to solve your problem :)
I need to increase the corresponding value, on the click of + button,
but at the same time, on click of any reset button, i need to reset all the values to 100.
Currently, i am able to increase the corresponding value on the click of + button, but on click of reset button, only the values that have not been incremented gets reset to 100
Basically i need to access a single value of f(for incrementation) and also all the values of f together(for resetting)
How do i implement this.
A demo of this problem is available here in plunkr
HTML snippet
<body ng-controller="MainCtrl">
<counter-widget startnumber=1 resetter="reset"></counter-widget>
</body>
JS snippet
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, $timeout) {
$scope.triggerReset = function () {
$scope.reset = true;
console.log('reset')
$timeout(function() {
$scope.reset = false;
},100)
}
});
app.directive("counterWidget",function(){
return{
restrict:"E",
scope:{
startnumber: '=',
resetter: '='
},
link:function(scope,elem,attr){
scope.f = 1;
scope.add = function(f){
this.f ++
}
scope.reset = function(){
scope.$parent.triggerReset()
}
scope.$watch(function(attr) {
return scope.resetter
},
function(newVal) {
if (newVal === true) {
scope.f = 100;
}
})
},
template:'<li ng-repeat="item in [1,2,3]">'+
"<button ng-click='add(f)'>+</button>"+
"{{f}}   "+
"<button ng-click='reset()'>reset</button><br><br>"+
'</li>'
}
})
ngRepeat creates a new scope for each item, so you get an new f for each item. To get access to all values I propose to store the values in an array and bind to this array.
http://plnkr.co/edit/amGSHZqCWsFgjl2bEM98
app.controller('MainCtrl', function($scope, $timeout) {
$scope.reset = function() {
$scope.values = []
for(var i = 0; i < 100; ++i) {
$scope.values.push({f: i});
}
}
$scope.reset();
});
app.directive("counterWidget",function(){
return{
scope:{
values: '=',
reset: '='
},
link:function(scope, elem, attr){
scope.add = function(value){
value.f++;
}
},
template:'<li ng-repeat="item in values">'+
"<button ng-click='add(item)'>+</button>"+
"{{item.f}}   "+
"<button ng-click='reset()'>reset all</button><br><br>"+
'</li>'
}
})
I need to change the caret position of an input, where a given number of digits is added (Example).
app.controller('MainCtrl', function($scope, $element, $timeout, $filter) {
//$scope.val = '12';
$scope.$watch('val', function(newValue, oldValue) {
if (!isNaN(newValue)) {
if (newValue.length > 3) {
//Set Caret Position
}
}
});
});
Is it possible to do something like this example?
I need for example :
Input: 1234.
so the caret position will be 2.
New digit: 9
final: 12934
Thanks in advance.
I think that such kind of things look better in directives. For example:
app.directive('caret', function() {
function setCaretPosition(elem, caretPos) {
if (elem !== null) {
if (elem.createTextRange) {
var range = elem.createTextRange();
range.move('character', caretPos);
range.select();
} else {
if (elem.setSelectionRange) {
elem.focus();
elem.setSelectionRange(caretPos, caretPos);
} else
elem.focus();
}
}
}
return {
scope: {value: '=ngModel'},
link: function(scope, element, attrs) {
var caret = Number(attrs.caret);
scope.$watch('value', function(newValue, oldValue) {
if (newValue && newValue != oldValue && !isNaN(newValue) && newValue.length > (caret + 1)) {
setCaretPosition(element[0], caret);
}
});
}
};
});
Usage:
<input ng-model='val' caret="2" />
I used setCaretPosition function for cross browser cursor positioning from this answer.
Demo: http://plnkr.co/edit/5RSgzvyd8YOTaXPsYr8A?p=preview
I think that the best approach for this is to make a reusable directive as we are dealing with DOM manipulation.
Link to the demo: http://plnkr.co/edit/qlGi64VO1AOrNpxoKA68?p=preview
var app = angular.module('angularjs-starter', []);
app.controller('MainCtrl', function($scope, $element, $timeout, $filter) {
$scope.$watch('val', function(newValue, oldValue) {
if (!isNaN(newValue)) {
if (newValue.length > 3) {
// $element.find('input')[0].selectionEnd = 2;
}
}
});
});
app.directive('setCaret', function() {
return {
restrict: 'A',
link: function(scope,element,attrs) {
var changed = false;
element.bind('keypress', function() {
if(element[0].selectionStart > 3 && !changed) {
changed = true;
element[0].selectionEnd = parseInt(attrs.position, 10);
}
})
},
}
})
You can see in the commented out part in the controller we can have access to this by using $element, but as this is DOM and controllers are not for DOM manipulation we need to make this into a directive.
I also had the same problem.
I thought to solve it creating an appropriate directive. You can find it here. Enjoy it!
Usage
Include directive, declare it by caret-aware attribute
<script src="https://cdn.rawgit.com/leodido/ng-caret-aware/master/caretaware.min.js"></script>
<script type="text/javascript">
var app = angular.module('myModule', ['leodido.caretAware']);
</script>
...
<div data-ng-app="app">
<input type="text" name="myname" caret-aware="cursor"/>
</div>
Then on the scope you'll have a variable cursor containing the position of the caret in the input named myname.
Nevertheless, this directive's controller exposes an API
getPosition
setPosition
For other usage examples see example directory of the above linked github repository.
I believe you could do it by using .setSelectionRange() on your input. I updated your example - see if this is what you wanted: http://plnkr.co/edit/bIJAPPAzkzqLIDUxVlIy?p=preview
Note: setSelectionRange is not supported by IE8 (see https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement.setSelectionRange), so if you need to support IE < 9, you'll need to look for shims.
I jsfiddled a working solution.
So basically, you have to create a directive :
app.directive('keypressdetector', function($compile){
return {
restrict:'AEC',
link: function(scope, element, attrs){
element.bind("keypress", function (event) {
if(event.which === 13) {
var selectionStart = element[0].selectionStart;
var value = element.val();
var valueLength = value.length;
var newValue= '';
if (selectionStart == valueLength){
newValue = value;
} else {
newValue = value.substring(selectionStart, valueLength);
}
var newElement = angular.element('<input type="text" value="' + newValue +'"/>')
angular.element(document.body).append(newElement);
}
});
}
};
});
Your controller would be useless in that situation.
You can invoke the directive like this (see : keypressdetector) :
<div ng-app="myapp">
<div ng-controller="LoginController">
<div>Hello {{ user.firstName }}</div>
<input ng-model="user.firstName" keypressdetector />
<input type="submit" ng-click="login()" value="Login"/>
<div ng-repeat="login in logins">{{ login }}</div>
</div>
</div>
See demo : https://jsfiddle.net/Lt7aP/3468/