Angular 1.x and AG-Grid Components - angularjs

I'm trying to upgrade our app from a really old version to the latest (v16). Since they have deprecated the old way of cell and header renders I'm trying to wrap my head around using scope and Angular compilation.
The ag-grid documentation states: You will then need to manage creating and destroying child scopes yourself inside the init() and destroy() methods.
angularCompileRows, angularCompileFilters and angularCompileHeaders are not supported within Components.
I tried to create a cell render like this:
function MyCellRenderer() {
}
MyCellRenderer.prototype.init = function (params) {
$scope.myMessage = 'Hi Scott';
var compiled = $compile('<p ng-bind="myMessage"></p>')($scope);
this.eGui = document.createElement('span');
this.eGui.innerHTML = compiled.html();
};
MyCellRenderer.prototype.getGui = function () {
return this.eGui;
};
However this doesn't work. Any thoughts?
I was able to get this to work, but I don't like that I need to wrap around a $timeout. I had to do this because a digest was already in progress:
function MyCellRendererSimple() {
}
MyCellRendererSimple.prototype.init = function (params) {
console.log('init Called');
this.eGui = document.createElement('span');
};
MyCellRendererSimple.prototype.getGui = function () {
var self = this;
$timeout(function() {
var compiled = $compile('<nice-checkbox checkbox-id="checkbox-2" ng-model="checkboxModal1"></nice-checkbox>')($scope);
$scope.$digest();
self.eGui.appendChild(compiled[0]);
});
return self.eGui;
};

Related

calling a function when AngularUI Bootstrap modal has been dismissed and animation has finished executing

I'm using the Angular UI bootstrap modal and I ran into a bit of a problem.
I want to call a function when the bootstrap modal dismiss animation is finished. The code block below will call the cancel() function as soon as the modal starts to be dismissed - and NOT when the modal dismiss animation has finished.
Angular UI does not use events, so there is no 'hidden.bs.modal' event being fired (at least, not to my knowledge).
var instance = $modal.open({...});
instance.result.then(function(data) {
return success(data);
}, function() {
return cancel();
})
The cancel() block immediately runs when the modal starts to close. I need code to execute when the closing animation for the Bootstrap modal finishes.
How can I achieve this with angular UI?
Component for reference:
https://angular-ui.github.io/bootstrap/#/modal
Thanks!
A little late but hope it still helps! You can hijack the uib-modal-window directive and check when its scope gets destroyed (it is an isolated scope directive). The scope is destroyed when the modal is finally removed from the document. I would also use a service to encapsulate the functionality:
Service
app.service('Modals', function ($uibModal, $q) {
var service = this,
// Unique class prefix
WINDOW_CLASS_PREFIX = 'modal-window-interceptor-',
// Map to save created modal instances (key is unique class)
openedWindows = {};
this.open = function (options) {
// create unique class
var windowClass = _.uniqueId(WINDOW_CLASS_PREFIX);
// check if we already have a defined class
if (options.windowClass) {
options.windowClass += ' ' + windowClass;
} else {
options.windowClass = windowClass;
}
// create new modal instance
var instance = $uibModal.open(options);
// attach a new promise which will be resolved when the modal is removed
var removedDeferred = $q.defer();
instance.removed = removedDeferred.promise;
// remember instance in internal map
openedWindows[windowClass] = {
instance: instance,
removedDeferred: removedDeferred
};
return instance;
};
this.afterRemove = function (modalElement) {
// get the unique window class assigned to the modal
var windowClass = _.find(_.keys(openedWindows), function (windowClass) {
return modalElement.hasClass(windowClass);
});
// check if we have found a valid class
if (!windowClass || !openedWindows[windowClass]) {
return;
}
// get the deferred object, resolve and clean up
var removedDeferred = openedWindows[windowClass].removedDeferred;
removedDeferred.resolve();
delete openedWindows[windowClass];
};
return this;
});
Directive
app.directive('uibModalWindow', function (Modals) {
return {
link: function (scope, element) {
scope.$on('$destroy', function () {
Modals.afterRemove(element);
});
}
}
});
And use it in your controller as follows:
app.controller('MainCtrl', function ($scope, Modals) {
$scope.openModal = function () {
var instance = Modals.open({
template: '<div class="modal-body">Close Me</div>' +
'<div class="modal-footer"><a class="btn btn-default" ng-click="$close()">Close</a></div>'
});
instance.result.finally(function () {
alert('result');
});
instance.removed.then(function () {
alert('closed');
});
};
});
I also wrote a blog post about it here.

Stop testing after specific failures

I have unpredictable behavior page that depends on changes that create developers. And sometimes my tests failed, because page wasn't load. My test scenario structure looks like:
describe('0. first actions', function () {
var lib = require("../../common.js");
var config = browser.params;
var url = config.listOfReferencesUrl, toolbar;
load(url, "list-of-references");
beforeAll(function () {
// some actions on the page
});
it('test0', function () {
since('test0 failed').
expect(toolbar.isPresent()).toBe(true);
});
describe('1.actions1', function () {
beforeAll(function () {
// some actions on the page
});
it('test1', function () {
since('test1 failed').
expect(table.getRow(clientNameNum).getRowInput().isEnabled()).toBe(true);
});
// ... another invested describes
});
Where load function is:
global.load = function (url, pageType) {
browser.get(url);
if (pageType == 'list-of-references'){
browser.executeScript("icms.go('WEB_INQ_PROC', 'InquiryList', null, 0)");
}
browser.waitForAngular();
};
I wonder if I can create structure to stop my tests if page isn't load. But I don't want to use 'jasmine-bail-fast', because I want to see another failures if page will load.
I tried to write something like:
if (this.results_.failedCount > 0) {
// Hack: Quit by filtering upcoming tests
this.env.specFilter = function(spec) {
return false;
};
}
But it isn't working. I use jasmine2.
Maybe somebody know how I can organize it?
You can define a wrapper
var filter = function (fn) {
if (!condition)
throw new Error('skipped');
return fn;
}
and use it on all relevant describe/it blocks:
describe('...', filter(function () {
...
}));

Using angular functions ($q) in protractor

I want to use the $q service of angular in my e2e tests. (I want to get the texts of a bunch of elements via getText() which returns a promise. After all promises are resolved, I want to test the list. So I want to use $q.all() etc.)
angular.injector(['myApp']).get('$q'); results in "ReferenceError: angular is not defined"
Installing angular via node and then var angular = require("angularjs"); results in "Error: Cannot find module 'angular'"
Also, inserting a browser.waitForAngular() does not help there.
Using the inject(function($q) {}) syntax has the same problem.
How can I use such angular functions in protractor?
edit:
Here's the very naive version of what I want to achieve
var collectEntries = function(containers) {
var entries = {};
containers.each(function (container) {
var title = container.element(by.tagName('h2'));
title.getText().then(function (text) {
var key = getSomeKey();
var entry = processEntry(text);
entries[key] = entry;
});
});
return entries;
};
That works in principle, at some point in time entries contains all data. However, I need to wait for that moment. What I would do is create and return a promise that gets resolved as soon as all getText promises are resolved.
e.g.
var deferred = $q.defer();
$q.all(getTextPromises).then(function () {
deferred.resolve(entries);
});
return deferred.promise;
From the looks of your code containers is a list of elementFinders? (i.e. var containers = [element(by.x), element(by.y), element(by.z)]):
Using q: (you need to add q as dependency in package.json first)
var q = require('q');
var collectEntries = function(containers) {
var entries = {};
containers.each(function (container) {
var deferred = q.defer();
var title = container.element(by.tagName('h2'));
title.getText().then(function (text) {
deferred.resolve(processEntry(text));
});
entries[getSomeKey()] = deferred.promise();
});
return q.all(entries);
};
expect(collectEntries).toBe({key1: 'title1', key2: 'title2'})
But protractor knows promise itself (and it's preferably that you don't mix protractor's promise with q promise unless you know what you're doing):
var collectEntries = function(containers) {
var entries = {};
containers.each(function (container) {
entries[getSomeKey()] = container.element(by.tagName('h2')).
getText().then(function (text) {
return processEntry(text);
});
});
return protractor.promise.fullyResolved(entries);
};
expect(collectEntries).toBe({key1: 'title1', key2: 'title2'})
If your containers are found using a single selector (i.e. var containers = element.all(by.xyz)), it's even easier:
var collectEntries = function(containers) {
return containers.reduce(function(entries, elem) {
return elem.getText().then(function(text) {
entries[getSomeKey()] = processEntry(text);
return entries;
});
}, {});
};
expect(collectEntries).toBe({key1: 'title1', key2: 'title2'})

angularjs binding/scope issue for select list?

OK switching my code to angularjs and the angular 'way', not sure what I am doing wrong.
A select list is not getting updated when the model changes unless I call $apply, and I find myself calling apply a lot.
index.html has this:
<div id='rightcol' data-ng-include="'partials/rightSidebar.html'"
data-ng-controller="rightSidebarController">
</div>
and rightSidebar.html has this:
<select id='srcList' size='10'
data-ng-model="data.source"
data-ng-click='srcOnclick()'
data-ng-options="s.title for s in data.srcList | filter:{title:data.srcFilter} | orderBy:'title'"></select>
rightSidebarController.js has this:
$scope.data = {};
$scope.data.srcList = dataProvider.getSourceList();
$scope.data.source = dataProvider.getSource();
dataProvider is a service that makes an asynchronous database call (IndexedDB) to populate srcList, which is what gets returned in dataProvider.getSource().
Is it the asynchronous database call that forces me to call $apply, or should the controller be ignorant of that?
Is there a 'better' way to do this?
Edited to add service code.
Another controller calls dataProvider.refreshSourceList:
myDB.refreshSourceList = function() {
myDB.getRecords("source", function(recs) {
myDB.srcList = recs;
$rootScope.$broadcast('SrcListRefresh');
});
};
myDB.srcList is the field being bound by $scope.data.srcList = dataProvider.getSourceList();
myDB.getRecords:
myDB.getRecords = function(storeName, callback) {
var db = myDB.db;
var recList = [];
var trans = db.transaction([storeName], 'readonly');
var store = trans.objectStore(storeName);
var cursorRequest = store.openCursor();
cursorRequest.onerror = myDB.onerror;
cursorRequest.onsuccess = function(e) {
var cursor = cursorRequest.result || e.result;
if (cursor === false || cursor === undefined) {
if (callback !== undefined) {
$rootScope.$apply(function() {
callback(recList);
});
}
} else if (cursor.value !== null) {
recList.push(cursor.value);
cursor.continue();
}
};
cursorRequest.onerror = myDB.onerror;
};
Anything you do async needs to be wrapped in $scope.$apply(). This is because angular works in a similar fashion to a game loop, however instead of constantly running, it knows to end the loop when an action is taken, and $scope.$digest() is called.
If you are using IndexedDB, I would recommend creating an angular wrapper for it, like so:
(forgive my IndexedDB code, I'm not experience with it)
angular.module('app',[])
.factory('appdb', function($rootScope){
var db = indexedDB.open('appdb', 3);
return {
get : function(table, query, callback) {
var req = db.transaction([table])
.objectStore(table)
.get(query);
req.onsuccess(function(){
$rootScope.$apply(function(){
callback(req.result);
});
});
}
};
});
This way you can be sure that any data retrieve and set on a controller scope inside of callback will have $scope.$digest() called afterward.

Can't get waiting for function to return (with promises?) working in angular controller

I'm trying to get the following findTimelineEntries function inside an Angular controller executing after saveInterview finishes:
$scope.saveInterview = function() {
$scope.interviewForm.$save({employeeId: $scope.employeeId}, function() {
$scope.findTimelineEntries();
});
};
The save action adds or edits data that also is part of the timeline entries and therefore I want the updated timeline entries to be shown.
First I tried changing it to this:
$scope.saveInterview = function() {
var functionReturned = $scope.interviewForm.$save({employeeId: $scope.employeeId});
if (functionReturned) {
$scope.findTimelineEntries();
}
};
Later to this:
$scope.saveInterview = function() {
$scope.interviewForm.$save({employeeId: $scope.employeeId});
};
$scope.saveInterview.done(function(result) {
$scope.findTimelineEntries();
});
And finaly I found some info about promises so I tried this:
$scope.saveInterview = function() {
$scope.interviewForm.$save({employeeId: $scope.employeeId});
};
var promise = $scope.saveInterview();
promise.done(function() {
$scope.findTimelineEntries();
});
But somehow the fact that it does work this way according to http://nurkiewicz.blogspot.nl/2013/03/promises-and-deferred-objects-in-jquery.html, doesn't mean that I can use the same method on those $scope.someFuntcion = function() functions :-S
Here is a sample using promises. First you'll need to include $q to your controller.
$scope.saveInterview = function() {
var d = $q.defer();
// do something that probably has a callback.
$scope.interviewForm.$save({employeeId: $scope.employeeId}).then(function(data) {
d.resolve(data); // assuming data is something you want to return. It could be true or anything you want.
});
return d.promise;
}

Resources