Lazy loading on scrolling drop down options in angularjs - javascript

I am working on MEAN stack application in which I am defining a drop down using following customize directive. This drop down shows available variables from an array(around 70K options).
.directive('inputDropdown', function($parse) {
var template =
'<input class="form-control" ng-model="ngModel" ng-disabled="disabled" ng-focus="setDirectiveList()" ng-blur="removeDirectiveList()">' +
'<div class="dropdown dropdown1" input-dropdown="increaseLimit()" ng-init="limit=20;" ng-click="setDirectiveList()">' +
'<div class="form-control" ng-repeat="value in selectedList | filter:ngModel | limitTo:limit">' +
'<div ng-mousedown="select($event, value)">{{value}}</div>' +
'</div>' +
'</div>';
return {
restrict: 'EA',
require: '^form',
scope: {
ngModel: '=',
list: '=',
onSelect: '&',
disabled:'=ngDisabled'
},
template: template,
link: function(scope, element, attrs,mapController) {
element.addClass('input-dropdown');
var handler = $parse(attrs.inputDropdown);
element.scroll(function (evt) {
var scrollTop = element[0].scrollTop,
scrollHeight = element[0].scrollHeight,
offsetHeight = element[0].offsetHeight;
if (scrollTop === (scrollHeight - offsetHeight)) {
$scope.$apply(function () {
handler($scope);
});
}
});
if(scope.$parent.setDirty)
{
scope.makeFormDirty = mapController.$setDirty();
}
scope.select = function(e, value) {
scope.ngModel = value;
// scope.onSelect({$event: e, value: value});
scope.makeFormDirty = mapController.$setDirty();
};
scope.setDirectiveList = function() {
// debugger;
scope.selectedList = scope.list;//scope.$parent.variables;
}
scope.removeDirectiveList = function() {
scope.selectedList = [];
}
}
};
})
What I am trying to do is using limitTo option, increasing the the display variables count by some value when the scroll bar of drop down hits bottom. The increaseLimit function is defined in my controller as:
$scope.increaseLimit = function () {
if ($scope.limit < $scope.variables.length) {
$scope.limit += 20;
}
};
where $scope.variables contains 70K entries. I am not sure whether the I am adding increaseLimit to correct div or scrolling function is wrong.
I want to achieve this type of loading in my drop down. How to add this in my customize directive input-dropdown. I tried but no success.
Please Help.

Related

Clear data from a service

Here is what I am trying to do:
I have a directive with graphs that ng-repeat data from a service array.
When I go from one page using the directive to another the directive has information from the first page. So if compliance is at 80% for person1 when I go to see person2 with a compliance of 50% I still see person1's information.
I tried $state.reload() which started an infinite loop.
I tried clearing the scope variables on $rootScope.$on($stateChangeLeave) etc.
Here is what my html looks like ( the compliance-gauge is the directive):
<div class="col-md-4" ng-repeat="gauge in regulatorDict[license.reg_id].gauges">
<compliance-gauge license="regulatorDict"
id="license.reg_id + gauge.name"
key="gauge.name"
value="license.applied[gauge.value]"
max="gauge.max"></compliance-gauge>
Here is the directive js file:
export default function() {
'ngInject';
'use strict';
return {
restrict: 'E',
replace: false,
scope: {
key: '=',
id: '=',
value: '=',
max: '='
},
templateUrl: template,
controller: function($scope,$state) {
'ngInject';
let percentage = ($scope.value / $scope.max) * 100;
let gaugeColors = ['#72C02C', '#f8ac59', '#ED5565'];
$scope.colorDict = {};
$scope.newId = $scope.id.split(' ')[0];
if (percentage >= 66) {
$scope.colorDict[$scope.newId] = gaugeColors[0];
} else if (percentage >= 33 && percentage < 66) {
$scope.colorDict[$scope.newId] = gaugeColors[1];
} else {
$scope.colorDict[$scope.newId] = gaugeColors[2];
}
}
};
}
Any suggestions?

How to update all the copies of a variable used within ng-repeat

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}}&nbsp&nbsp&nbsp"+
"<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}}&nbsp&nbsp&nbsp"+
"<button ng-click='reset()'>reset all</button><br><br>"+
'</li>'
}
})

Loading state as a modal overlay in AngularJS

I want to load a state as a modal so that I can overlay a state without effecting any other states in my application. So for example if I have a link like:
<a ui-sref="notes.add" modal>Add Note</a>
I want to then interrupt the state change using a directive:
.directive('modal', ['$rootScope', '$state', '$http', '$compile',
function($rootScope, $state, $http, $compile){
return {
priority: 0,
restrict: 'A',
link: function(scope, el, attrs) {
$rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
event.preventDefault();
});
el.click(function(e){
$http
.get('URL HERE')
.then(function(resp){
$('<div class="modal">' + resp.data + '</div>').appendTo('[ui-view=app]');
setTimeout(function(){
$('.wrapper').addClass('showModal');
},1);
});
});
}
}
}
])
This successfully prevents the state change and loads the URL and appends it as a modal to the application. The problem is that it loads the entire application again...
How can I load just the state? e.g. the template files and the adjoining controller.
The state looks like:
.state('notes.add',
{
parent: 'notes',
url: '/add',
views: {
'content': {
templateUrl: 'partials/notes/add.html',
controller: 'NotesAddCtrl'
}
}
})
An example of how it should work using jQuery: http://dev.driz.co.uk/AngularModal
See how I can access StateA and StateB loading via AJAX that uses the History API to change the URL to reflect the current state change.
And regardless of whether I am on the index, StateA or StateB I can load StateA or StateB as a modal (even if I'm on that State already) and it doesn't change the url or the current content, it just overlays the state content.
This is what I want to be able to do in AngularJS.
Note. this example doesn't work with the browser back and forward buttons due to it being a quick example and not using the history api correctly.
I've seen your question a few days ago and it seemed interesting enough to try and set up something that would work.
I've taken the uiSref directive as a start, and modified the code to use angular-bootstrap's $modal to show the desired state.
angular.module('ui.router.modal', ['ui.router', 'ui.bootstrap'])
.directive('uiSrefModal', $StateRefModalDirective);
function parseStateRef(ref, current) {
var preparsed = ref.match(/^\s*({[^}]*})\s*$/), parsed;
if (preparsed) ref = current + '(' + preparsed[1] + ')';
parsed = ref.replace(/\n/g, " ").match(/^([^(]+?)\s*(\((.*)\))?$/);
if (!parsed || parsed.length !== 4) throw new Error("Invalid state ref '" + ref + "'");
return { state: parsed[1], paramExpr: parsed[3] || null };
}
function stateContext(el) {
var stateData = el.parent().inheritedData('$uiView');
if (stateData && stateData.state && stateData.state.name) {
return stateData.state;
}
}
$StateRefModalDirective.$inject = ['$state', '$timeout', '$modal'];
function $StateRefModalDirective($state, $timeout, $modal) {
var allowedOptions = ['location', 'inherit', 'reload'];
return {
restrict: 'A',
link: function(scope, element, attrs) {
var ref = parseStateRef(attrs.uiSrefModal, $state.current.name);
var params = null, url = null, base = stateContext(element) || $state.$current;
var newHref = null, isAnchor = element.prop("tagName") === "A";
var isForm = element[0].nodeName === "FORM";
var attr = isForm ? "action" : "href", nav = true;
var options = { relative: base, inherit: true };
var optionsOverride = scope.$eval(attrs.uiSrefModalOpts) || {};
angular.forEach(allowedOptions, function(option) {
if (option in optionsOverride) {
options[option] = optionsOverride[option];
}
});
var update = function(newVal) {
if (newVal) params = angular.copy(newVal);
if (!nav) return;
newHref = $state.href(ref.state, params, options);
if (newHref === null) {
nav = false;
return false;
}
attrs.$set(attr, newHref);
};
if (ref.paramExpr) {
scope.$watch(ref.paramExpr, function(newVal, oldVal) {
if (newVal !== params) update(newVal);
}, true);
params = angular.copy(scope.$eval(ref.paramExpr));
}
update();
if (isForm) return;
element.bind("click", function(e) {
var button = e.which || e.button;
if ( !(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || element.attr('target')) ) {
e.preventDefault();
var state = $state.get(ref.state);
var modalInstance = $modal.open({
template: '<div>\
<div class="modal-header">\
<h3 class="modal-title">' + ref.state + '</h3>\
</div>\
<div class="modal-body">\
<ng-include src="\'' + state.templateUrl + '\'"></ng-include>\
</div>\
</div>',
controller: state.controller,
resolve: options.resolve
});
modalInstance.result.then(function (selectedItem) {
$scope.selected = selectedItem;
}, function () {
console.log('Modal dismissed at: ' + new Date());
});
}
});
}
};
}
You can use it like this <a ui-sref-modal="notes.add">Add Note</a>
Directive requires the angular-bootstrap to resolve the modal dialog. You will need to require the ui.router.modal module in your app.
Since asked to provide an example for my comment,
Example Directive
myapp.directive('openModal', function ($modal) {
return function(scope, element, attrs) {
element[0].addEventListener('click', function() {
$modal.open({
templateUrl : attrs.openModal,
controller: attrs.controller,
size: attrs.openModalSize,
//scope: angular.element(element[0]).scope()
});
});
};
});
Example Html
<button
open-modal='views/poc/open-modal/small-modal.html'
open-modal-size='sm'
controller="MyCtrl">modal small</button>
The above directive approach is not very different from using a state, which has templateUrl and controller except that url does not change.
.state('state1.list', {
url: "/list",
templateUrl: "partials/state1.list.html",
controller: function($scope) {
$scope.items = ["A", "List", "Of", "Items"];
}
})
Apparently there is the issue Ui-sref not generating hash in URL (Angular 1.3.0-rc.3) refering to
https://github.com/angular-ui/ui-router/issues/1397
It is seems to be fixed as per comments.
I personally dislike html5mode because it requires extra work on your server for no apparent advantage (I don't regard having "more beautiful url" as tangible advantage to justify the extra work).
There is another performance problem when using routers, that the view DOM is re-created upon each route change. I mentioned a very simple solution
in this answer.
As a side remark, the example in http://dev.driz.co.uk/AngularModal/ does not behave quite well. It does not record history, so I can't go back. Further, if you click on links like Index or modals, and then reload, you don't get the same page.
UPDATE.
It seems from the comments that a route change is not wanted when opening the modal. In that case the easiest solution is not to put ui-sref on the opening button and let the modal directive along handle it.

Creating a kendo-grid with reusable options using AngularJS

How to create a kendo-grid with reusable options using AngularJS?
Besides the default settings, the grid must include a checkbox column dynamically with the option to select all rows . Methods to treat the selections should be part of the directive and, somehow, I should be able to access the rows selected in controller.
Another important behavior is to keep a reference to the grid :
// In the controller : $scope.grid
<div kendo-grid="grid" k-options="gridOptions"></div>
Below an initial path that I imagined, but it is not 100% working because AngularJS not compile information from checkbox column, so do not call the methods of the controller directive. At the same time I'm not sure where force $compile in this code.
myApp.directive('myApp', ['$compile', function ($compile) {
var directive = {
restrict: 'A',
replace: true,
template: '<div></div>',
scope: {
gridConfiguration: '='
},
controller: function ($scope) {
$scope.gridIds = [];
$scope.gridIdsSelected = [];
var updateSelected = function (action, id) {
if (action === 'add' && $scope.gridIdsSelected.indexOf(id) === -1) {
$scope.gridIdsSelected.push(id);
}
if (action === 'remove' && $scope.gridIdsSelected.indexOf(id) !== -1) {
$scope.gridIdsSelected.splice($scope.gridIdsSelected.indexOf(id), 1);
}
};
$scope.updateSelection = function ($event, id) {
var checkbox = $event.target;
var action = (checkbox.checked ? 'add' : 'remove');
updateSelected(action, id);
};
$scope.isSelected = function (id) {
return $scope.gridIdsSelected.indexOf(id) >= 0;
};
$scope.selectAll = function ($event) {
var checkbox = $event.target;
var action = (checkbox.checked ? 'add' : 'remove');
for (var i = 0; i < $scope.gridIds.length; i++) {
var id = $scope.gridIds[i];
updateSelected(action, id);
}
};
},
link: function ($scope, $element, $attrs) {
var baseColumns = [
{
headerTemplate: '<input type="checkbox" id="selectAll" ng-click="selectAll($event)" ng-checked="isSelectedAll()">',
template: '<input type="checkbox" name="selected" ng-checked="isSelected(#=Id#)" ng-click="updateSelection($event, #=Id#)">',
width: 28
}
];
for (var i = 0; i < $scope.gridConfiguration.columns.length; i++) {
var column = $scope.gridConfiguration.columns[i];
baseColumns.push(column);
}
var gridOptions = {...};
var grid = $element.kendoGrid(gridOptions).data("kendoGrid");;
$scope.$parent[$attrs[directive.name]] = grid;
}
};
return directive;
}]);
I've put an example directive here: http://embed.plnkr.co/fQhNUGHJ3iAYiWTGI9mn/preview
It activates on the my-grid attribute and inserts a checkbox column. The checkbox is bound to the selected property of each item (note that it's an Angular template and it uses dataItem to refer to the current item). To figure out the selected items you can do something like:
var selection = $scope.grid.dataSource.data().filter(function(item){
return item.selected;
});
The checkbox that is added in the header will toggle the selection.
HTH.
#rGiosa is right
I've tried to do that :
var options = angular.extend({}, $scope.$eval($attrs.kOptions));
options['resizable']= true;
Seems to add the attribute in options object but the grid is still not resizable why?
http://plnkr.co/edit/Lc9vGKPfD8EkDem1IP9V?p=preview
EDIT:
Apparently,Options of the Grid cannot be changed dynamically. You need to re-create the whole Grid with different options in order to disable/enable them dynamically?!
Cheers

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