AngularJS filtering by code - javascript

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 :)

Related

How to remove class inside of directive using AngularJS?

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>

AngularJS custom sort directive

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:

AngularJS Masonry for Dynamically changing heights

I have divs that expand and contract when clicked on. The Masonry library has worked great for initializing the page. The problem I am experiencing is that with the absolute positioning in place from Masonry and the directive below, when divs expand they overlap with the divs below. I need to have the divs below the expanding div move down to deal with the expansion.
My sources are:
http://masonry.desandro.com/
and
https://github.com/passy/angular-masonry/blob/master/src/angular-masonry.js
/*!
* angular-masonry <%= pkg.version %>
* Pascal Hartig, weluse GmbH, http://weluse.de/
* License: MIT
*/
(function () {
'use strict';
angular.module('wu.masonry', [])
.controller('MasonryCtrl', function controller($scope, $element, $timeout) {
var bricks = {};
var schedule = [];
var destroyed = false;
var self = this;
var timeout = null;
this.preserveOrder = false;
this.loadImages = true;
this.scheduleMasonryOnce = function scheduleMasonryOnce() {
var args = arguments;
var found = schedule.filter(function filterFn(item) {
return item[0] === args[0];
}).length > 0;
if (!found) {
this.scheduleMasonry.apply(null, arguments);
}
};
// Make sure it's only executed once within a reasonable time-frame in
// case multiple elements are removed or added at once.
this.scheduleMasonry = function scheduleMasonry() {
if (timeout) {
$timeout.cancel(timeout);
}
schedule.push([].slice.call(arguments));
timeout = $timeout(function runMasonry() {
if (destroyed) {
return;
}
schedule.forEach(function scheduleForEach(args) {
$element.masonry.apply($element, args);
});
schedule = [];
}, 30);
};
function defaultLoaded($element) {
$element.addClass('loaded');
}
this.appendBrick = function appendBrick(element, id) {
if (destroyed) {
return;
}
function _append() {
if (Object.keys(bricks).length === 0) {
$element.masonry('resize');
}
if (bricks[id] === undefined) {
// Keep track of added elements.
bricks[id] = true;
defaultLoaded(element);
$element.masonry('appended', element, true);
}
}
function _layout() {
// I wanted to make this dynamic but ran into huuuge memory leaks
// that I couldn't fix. If you know how to dynamically add a
// callback so one could say <masonry loaded="callback($element)">
// please submit a pull request!
self.scheduleMasonryOnce('layout');
}
if (!self.loadImages){
_append();
_layout();
} else if (self.preserveOrder) {
_append();
element.imagesLoaded(_layout);
} else {
element.imagesLoaded(function imagesLoaded() {
_append();
_layout();
});
}
};
this.removeBrick = function removeBrick(id, element) {
if (destroyed) {
return;
}
delete bricks[id];
$element.masonry('remove', element);
this.scheduleMasonryOnce('layout');
};
this.destroy = function destroy() {
destroyed = true;
if ($element.data('masonry')) {
// Gently uninitialize if still present
$element.masonry('destroy');
}
$scope.$emit('masonry.destroyed');
bricks = [];
};
this.reload = function reload() {
$element.masonry();
$scope.$emit('masonry.reloaded');
};
}).directive('masonry', function masonryDirective() {
return {
restrict: 'AE',
controller: 'MasonryCtrl',
link: {
pre: function preLink(scope, element, attrs, ctrl) {
var attrOptions = scope.$eval(attrs.masonry || attrs.masonryOptions);
var options = angular.extend({
itemSelector: attrs.itemSelector || '.masonry-brick',
columnWidth: parseInt(attrs.columnWidth, 10) || attrs.columnWidth
}, attrOptions || {});
element.masonry(options);
var loadImages = scope.$eval(attrs.loadImages);
ctrl.loadImages = loadImages !== false;
var preserveOrder = scope.$eval(attrs.preserveOrder);
ctrl.preserveOrder = (preserveOrder !== false && attrs.preserveOrder !== undefined);
scope.$emit('masonry.created', element);
scope.$on('$destroy', ctrl.destroy);
}
}
};
}).directive('masonryBrick', function masonryBrickDirective() {
return {
restrict: 'AC',
require: '^masonry',
scope: true,
link: {
pre: function preLink(scope, element, attrs, ctrl) {
var id = scope.$id, index;
ctrl.appendBrick(element, id);
element.on('$destroy', function () {
ctrl.removeBrick(id, element);
});
scope.$on('masonry.reload', function () {
ctrl.scheduleMasonryOnce('reloadItems');
ctrl.scheduleMasonryOnce('layout');
});
scope.$watch('$index', function () {
if (index !== undefined && index !== scope.$index) {
ctrl.scheduleMasonryOnce('reloadItems');
ctrl.scheduleMasonryOnce('layout');
}
index = scope.$index;
});
}
}
};
});
}());
Like with many non-Angular libraries, it appears the answer lies in wrapping the library in an Angular directive.
I haven't tried it out but it appears that is what this person did
You can use angular's $emit, $broadcast, and $on functionality.
Inside your masonry directive link function:
scope.$on('$resizeMasonry', ctrl.scheduleMasonryOnce('layout'));
Inside your masonryBrick directive link function or any other child element:
scope.$emit('$resizeMasonry');
Use $emit to send an event up the scope tree and $broadcast to send an event down the scope tree.

Set Caret position in Input with AngularJS

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/

When does the directive replace the element with the template?

I'm trying to write an AngularJS directive in which I replace the element with my specified HTML tags, then attach a keypress handler that calls a function on the directive's controller. Unfortunately that is not working for me. First off, here's the code I'm working with:
.directive('amsNewEntry', function() {
return {
restrict: 'E',
replace: true,
scope: {
url: '#navigateTo'
},
compile: function (tElement, tAttrs) {
var text = tAttrs.text;
var html = '<div>'
+ ' ' + text + ''
+ ' <input data-ng-show="isInputFieldVisible" type="text" data-ng-model="inputValue" />'
+ '</div>';
tElement.replaceWith(html);
return {
post: function (scope, element, attrs) {
console.log(element);
console.log(scope);
var input = $(element).find('input');
if (input.length > 0) {
console.log('attaching handler');
input.keypress(function (e) {
alert('keypress');
var code = e.which || e.keyCode;
if (code === 13) {
alert('clicked enter');
scope.navigate();
}
});
}
}
}
},
controller: function ($scope, $location) {
$scope.isInputFieldVisible = false;
$scope.url = null;
$scope.showInput = function () {
$scope.isInputFieldVisible = true;
}
$scope.inputValue = '';
$scope.navigate = function () {
var target = $scope.url + '/' + $scope.inputValue;
console.log(target);
$location.path(target);
}
}
The thing is, in the post-linking function returned from the compile function, when I console out the element variable, it is still not being compiled to the HTML I specified. Shouldn't the tElement.replaceWith(html) line do that?
And one last thing, the input field renders visible on the screen although I'm using the ng-show directive and binding it to the isInputFieldVisible property which is initialized to false. Any ideas why this is not working?

Categories

Resources