Karma angularjs testing: test code in timeout never executes - angularjs

The following code in the $timeout is never called. I can put any wrong test I like in there and the test always passes (there is a similar question (Karma e2e testing: how to know when the DOM is ready?) but it does not provide a solution):
it('should display a filter row when attribute sg-live-filtering is present', function()
{
angular.mock.inject(function($compile, $rootScope, $timeout) {
var elem = $compile('<div sg-grid sg-data="api/grid/accounts" sg-live-filtering></div>')(scope); // the angular-kendo grid
$rootScope.$apply();
var table = elem.find('table[role="grid"]'); // find the kendo grid
expect(table.length).toBe(1);
var header = table.find('thead'); // find the grid's table header
expect(header.length).toBe(1);
$timeout(function () {
// get the second row in the header and check it has a specific class
expect(header.find('tr').eq(1).hasClass('sg-grid-filter-row')).toBeTruthy();
// e.g. I could do this and it would still pass!!!
expect(header.find('tr').eq(500));
});
}
}
PhantomJS 1.9.2 (Windows 7): Executed 1 of 871 (skipped 383) SUCCESS (0 secs / 0
This is what it looks like in the browser:
The kendo grid is created using a standard angularjs directive:
angular.module('sgComponents').directive('sgGrid', [
templateUrl: 'sg-grid.html',
// lots of kendo code follows to build the grid
]);
The external sg-grid.html template:
<div sg-grid sg-data="api/grid/accounts"
sg-live-filtering> <!-- the attribute I want to test -->
</div>
When the directive code runs, there is a check to see if the sg-live-filtering attr is present. If it is, a utility function is called to append the row you see highlighted in the image to the grid's table header:
if (attrs.sgLiveFiltering) {
/*
timeout is needed to ensure DOM is ready. COULD THIS BE THE PROBLEM?
*/
$timeout(function () {
/*
this function adds the filter row to the grid.
THE NEW ROW HAS CLASS 'sg-grid-filter-row' THAT I USE FOR TESTING
*/
buildGridFilterRow(elm, scope, attrs);
});
}

Can you display your test code ???
Because you have to wait in order to execute timeout or just use $timeout.flush();
Here is an example:
Unit testing an asynchronous service in angularjs

Finally (2 weeks later!) got this to work. #idursun and igorzg were correct when they suggested using $timeout.flush(100) but it also needed, as #idursun further mentioned in his comments, that the $timeout block be removed too.
When I initially tried this, it didn't work but now it does. Don't ask me why, all I care is that it is working! Here's my full test case for reference:
it('should display a filter row when attribute sg-live-filtering is present', function () {
angular.mock.inject(function($compile, $rootScope, $timeout) {
var scope = $rootScope.$new();
scope.accountColumnsForAdvSearch = [
{field: 'accountId', title: 'AccountId', dataType: 'string'},
{field: 'name', title: 'Account Name', dataType: 'string'},
{field: 'shortName', title: 'Short Name', dataType: 'string'},
{field: 'status', title: 'Status', dataType: 'string'}
];
$httpBackend.when('GET', 'api/grid/accounts').respond(accountData);
var elem = $compile('<div sg-grid sg-data="api/grid/accounts" sg-columns="accountColumnsForAdvSearch" sg-live-filtering="true"></div>')(scope);
$rootScope.$apply();
$httpBackend.flush();
$timeout.flush(100); // wait for DOM to load
var table = elem.find('table[role="grid"]'); // the kendo grid
expect(table.length).toBe(1);
var filterRow = table.find('.sg-grid-filter-row'); // the filter row
expect(filterRow.length).toBe(1);
});

Related

ng-click doesn't work with external JavaScript

I am creating an ionic project and I am trying to integrate with Algolia autocomplete.js. I managed to make the search system work, however I added a ng-click on my search results and this function is not working as presented in this codepen that I did as example below:
http://codepen.io/marcos_arata/pen/VKVOky
Inside my algolia's result template:
<a ng-click="add_name({{{ name }}})">
Function that should be run when clicked:
$scope.add_name = function(name) {
alert('User added!');
console.log(name);
}
I tried to inject the results inside the scope but didn't work as well:
autocomplete('#search_name', { hint: false, debug: true, openOnFocus: true },[{
source: index.ttAdapter({ hitsPerPage: 15 }),
templates: {
header: '',
suggestion: function(hit) {
$scope.hit = hit;
return template.render(hit);
}
}
}]);
http://codepen.io/marcos_arata/pen/VKVOky
---- SOLVED ----
Instead of creating a ng-click function inside your templates, you can handle the event click of your search inside your "autocomplete:selected" function and use the dataset and suggestion results.
.on('autocomplete:selected', function(event, suggestion, dataset) {
$scope.name = suggestion.name;
console.log($scope.name);
## create any functions with the suggestion and dataset results inside
});
EDITING THE ANSWER:
Here is the codepen:
Apparently the suggestion keep the name clicked, so you dont need an extra function:
.on('autocomplete:selected', function(event, suggestion, dataset) {
$scope.name = suggestion.name;
console.log($scope.name);
});

Extjs 6 Modern - How to execute function in controller from view?

In controller
listen: {
global: {
'ontest': 'ontestfunction'
}
},
ontestfunction: function(){
alert('oks 31');
}
In view
listeners: {
element: 'element',
click: function(){
Ext.GlobalEvents.fireEvent('ontest');
}
}
It is the only way I've found to work, you know some other way?
You can get any controller using Ext.app.Controller.getController.
Since application is derived from controller, all you have to know is the name of your app and the desired controller; and calling the function is a piece of cake:
var configController = MyAppName.app.getController("MyAppName.controller.Configuration");
configController.ontestfunction();
Since you're using EXT 6, I assume you're using viewcontrollers and not controllers.
Ext is going to resolve the scope as the view's controller automatically, no need to write any extra code.
First make sure you define the viewcontroller as the controller of the view:
controller: 'aliasofviewcontroller'
after that you can basically assign any of the controllers functions to the handlers of the components on the view.
Let's say you have the following function in your controller:
onClickCustomHandler:function(e){
...
}
Using the following syntax, the function is going to get called every time you're button is clicked:
Ext.create('Ext.Button', {
text: 'My button',
handler: 'onClickCustomHandler'
});
Or using xtype:
{
xtype:'button',
text:'My button',
handler: 'onClickCustomHandler'
}
For further reading: http://docs.sencha.com/extjs/6.0.2/guides/application_architecture/view_controllers.html

How to recompile a widget's templateUrl in Malhar AngularJS dashboard framework

I'm using the Malhar AngularJS Dashboard Framework located here on github: https://github.com/DataTorrent/malhar-angular-dashboard , and a simple demo here: http://datatorrent.github.io/malhar-angular-dashboard/#/ .
Their system uses JQuery Sortable Widgets, which allows me to load up some charts and grids into a dashboard.
The way I load my charts and grids is by using their templateUrl property on the widget definition, as follows :
{
name: myWidgetEnum.TREELIST,
title: 'Tree List',
templateUrl: 'app/shared/gadget-templates/tree-list.html'
}
Here's a more complete sample of the Malhar Widget Definitions, which is where I define my UI "gadgets" to be rendered within the sortable widgets :
(function () {
'use strict';
angular.module('rage')
.factory('widgetDefinitions', ['ColumnChartDataModel', 'TreeListDataModel', 'TreeMapDataModel', 'GridDataModel', widgetDefinitions])
var myWidgetEnum = Object.freeze({
TREELIST: 'chart_treelist',
TREEMAP: 'chart_treemap',
HEATMAP: 'chart_heatmap',
GRID: 'chart_grid'
});
/* Define the widget definitions - base properties are defined in WidgetModel factory.
*/
function widgetDefinitions(ColumnChartDataModel, TreeListDataModel, TreeMapDataModel, GridDataModel) {
var size = { width: '20%', height: 90 };
var fixedSize = { width: '50%', height: 320 }; // keep height as a number, and add 'px' in directive
var initImagePath = 'images4/256x256/';
return [
{
name: myWidgetEnum.TREELIST,
title: 'Tree List',
templateUrl: 'app/shared/gadget-templates/tree-list.html',
dataModelType: TreeListDataModel,
dataModelOptions: {}, // this property will be persisted in DashboardState factory
gadgetType: 'table',
chartType: 'treelist',
settingsModalOptions: { }
},
{
name: myWidgetEnum.HEATMAP,
title: 'Heat Map',
templateUrl: 'app/shared/gadget-templates/grid.html',
dataModelType: GridDataModel,
dataModelOptions: {}, // this property will be persisted in DashboardState factory
gadgetType: 'table',
chartType: 'heatmap',
settingsModalOptions: {} // see 'MainCtrl' controller
}
];
}
})();
And the dashboard options in my main controller :
$scope.dashboardOptions = {
widgetButtons: false, // 'false' makes it a dropdown of options
widgetDefinitions: widgetDefinitions, // see dashboard-widget-factory.js
defaultWidgets: defaultWidgets,
storage: $window.localStorage,
storageId: $scope.defaultDashboardId, // 'rage.ui' - no longer hard code the storage Id - 06/11/2015 BM:
hideWidgetSettings: false,
settingsModalOptions: {
templateUrl: 'app/shared/include/gadget-config-new/gadget-config-container.html',
controller: 'GadgetSettingsCtrl_NEW as settings',
backdrop: true
},
onSettingsClose: function (result, widget) {
jQuery.extend(true, widget, result);
widget.dataModelOptions = result.dataModelOptions;
},
onSettingsDismiss: function (reason) {
console.log('widget settings were dismissed. Reason: ', reason);
}
};
When Malhar first loads up the widgets, it compiles the templateUrl within the JQuery sortable widget.
However, if I launch the widget settings modal and choose a different "widget" from the Widget Definitions list, I'm having an issue when I try to recompile the new templateUrl.
In other words if the first widget loaded up is the TREELIST, then in it's modal settings I choose a new widget called HEATMAP, I need to somehow force a recompile on the new html template that was loaded by Malhar.
Here on github, you can find the main directive, malhar-angular-dashboard.js https://github.com/DataTorrent/malhar-angular-dashboard/blob/master/dist/malhar-angular-dashboard.js , and if you search down for the 'DashboardWidgetCtrl' controller you'll find the $scope.makeTemplateString function as well as the $scope.compileTemplate function further below that.
I'm experimenting with the resolve section of the $modal instance as follows, but this seems to be more of a hack than an elegant solution:
See the angular.element below inside the try...catch section :
// Set resolve and reject callbacks for the result promise
modalInstance.result.then(
function (result) {
// Call the close callback
onClose(result, widget, scope);
// CHECK IF FIRST TIME CONFIGURING THE GADGET - 04/22/2015 BM:
if (!widget.gadgetConfigured) {
widget.gadgetConfigured = true;
widget.setHeight(widget.fixedSize.height);
widget.setWidth(widget.fixedSize.width);
}
scope.$broadcast('widgetSettingsClosed', widget, result);
//AW Persist title change from options editor
scope.$emit('widgetChanged', widget);
// hack to recompile the widget if user has swapped gadget types in gadget config modal - 10/01/2015 BM:
if (widget.chartType == "heatmap" || widget.chartType == "treelist") {
try {
angular.element(document.getElementById('dash')).scope().$$childHead.compileTemplate();
} catch (e) {
console.log("In modalInstance.result.then, cannot compile template " + e);
}
}
},
function (reason) {
// Call the dismiss callback
onDismiss(reason, scope);
}
);
If there's anyone with experience in Mahlar Dashboard, or in compiling directives, I would appreciate any guidance in this area.
thank you,
Bob

WebSQL data into AngularJs DropDown

I have very simple question about getting data from WebSql
I have DropDown i.e
<select id="selectCatagoryFood" data-role="listview" data-native-menu="true"
ng-init="foodCatagory = foodCatagories.cast[0]"
ng-options="foodCatagory as foodCatagory.text for foodCatagory in foodCatagories.cast"
ng-model="foodCatagory"
ng-change="changeFoodCatagory()">
</select>
now i want to add data init from webSQL. I already get Data from webSql but i am confuse that how to add that data into DropDown
An example or hints maybe very helpful for me.
Update 1 :: Add Controller Code
myApp.controller('foodSelection',function($scope,foodCatagories){
$scope.foodCatagories = foodCatagories;
$scope.changeFoodCatagory = function(){
alert($scope.foodCatagory.value);
}
});
Update 2 webSQL and JayData
_context.onReady({
success: showData,
error: function (error){
console.log(error);
}
});
function showData(){
var option = '';
_context.FoodGroup.forEach(function(FG)
{
option += '<option value="'+FG.FoodGroupID+'">'+FG.Description+'</option>';
}).then(function(){
console.log(option);
});
}
Update 3
var myApp = angular.module('myApp',[]);
myApp.factory('foodCatagories',function(){
var foodCatagories = {};
foodCatagories.cast = [
{
value: "000",
text: "Select Any"
}
];
return foodCatagories;
});
Update 4
One thing that i didn't mention is that I am using JayData for getting data from webSQL to my App
I will try to explain how it works:
EDIT: Live demo
html
Here is your stripped down select.
<select ng-options="item as item.text for item in foodCategories"
ng-model="foodCategory"
ng-required="true"
ng-change="changeFoodCategory()">
</select>
The directive ng-options will fill automatically the option elements in your select. It will take the foodCategories variable from the $scope of your controller and foreach item in the collection, it will use the text property as the label shown (<option>{{item.text}}</option>') and it will select the whole objectitemas the value of the selectedoption. You could also refer to a property as the value like ({{item.text}}). Then yourng-modelwould be set to theid` value of the selected option.
The directive ng-model corresponds to the variable in the $scope of your controller that will hold the value of the selected option.
The directive ng-required allows you to check if a value has been selected. If you are using a form, you can check if the field is valid formName.ngModelName.$valid. See the docs for more details on form validation.
The directive ng-change allows you to execute a function whenever the selected option changes. You may want to pass the ng-model variable to this function as a parameter or call the variable through the $scope inside the controller.
If no default value is set, angular will add an empty option which will be removed when an option is selected.
You did use the ng-init directive to select the first option, but know that you could set the ng-model variable in your controller to the default value you would like or none.
js
Here I tried to simulate your database service by returning a promise in the case that you are doing an async request. I used the $q service to create a promise and $timeout to fake a call to the database.
myApp.factory('DbFoodCategories', function($q, $timeout) {
var foodCategories = [
{ id: 1, text: "Veggies", value: 100 },
{ id: 2, text: "Fruits", value: 50 },
{ id: 3, text: "Pasta", value: 200 },
{ id: 4, text: "Cereals", value: 250 },
{ id: 5, text: "Milk", value: 150 }
];
return {
get: function() {
var deferred = $q.defer();
// Your call to the database in place of the $timeout
$timeout(function() {
var chance = Math.random() > 0.25;
if (chance) {
// if the call is successfull, return data to controller
deferred.resolve(foodCategories);
}
else {
// if the call failed, return an error message
deferred.reject("Error");
}
}, 500);
/* // your code
_context.onReady({
success: function() {
deferred.resolve(_contect.FoodGroup);
},
error: function (error){
deferred.reject("Error");
}
});
*/
// return a promise that we will send a result soon back to the controller, but not now
return deferred.promise;
},
insert: function(item) {
/* ... */
},
update: function(item) {
/* ... */
},
remove: function(item) {
/* ... */
}
};
});
In your controller you set the variables that will be used in your view. So you can call your DbFoodCategories service to load the data into $scope.foodCategories, and set a default value in $scope.foodCategory that will be used to set the selected option.
myApp.controller('FoodSelection',function($scope, DbFoodCategories){
DbFoodCategories.get().then(
// the callback if the request was successfull
function (response) {
$scope.foodCategories = response; //response is the data we sent from the service
},
// the callback if an error occured
function (response) {
// response is the error message we set in the service
// do something like display the message
}
);
// $scope.foodCategory = defaultValue;
$scope.changeFoodCategory = function() {
alert($scope.foodCatagory.value);
}
});
I hope that this helped you understand more in detail what is happening!
See this example and how use $apply to update the data in scope.
in the new version we released a new module to support AngularJS. We've started to document how to use it, you can find the first blogpost here
With this you should be able to create your dropdown easily, no need to create the options manually. Something like this should do the trick:
myApp.controller('foodSelection',function($scope, $data) {
$scope.foodCatagories = [];
...
_context.onReady()
.then(function() {
$scope.foodCatagories = _context.FoodGroup.toLiveArray();
});
});
provided that FoodGroup has the right fields, of course

ExtJS Create popup using another controller/view?

I have a grid, the rows of which can be clicked on. The click fires an event that is then captured by the controller. Is there a way for that controller to open a popup and call a controller to populate that popup with its associated view? This is what I have in my grid's controller now:
init: function() {
...
this.control({
'shipmentsgrid': {
itemrowclick: this.itemRowClick
}
})
},
itemRowClick: function() {
var win = new Ext.Window({var win = new Ext.Window({
items: [{
xtype: 'shipmentmonthly' // This is not recognized
}]
}).show();
}
I am not quite sure what you trying to archive. But you can easily fetch a instance of another controller using getController('YourControllerName') called from any controller scope. That will present you with a instance of this controller (and even load necessary classes). Now you are free to call any method on this controller with any arguments. For example you may also either provide the instance of this controller as argument and us that one as scope with or use this (But that depends on your implementation)
For your example:
itemRowClick: function() {
var ctrl = this.getController('Controller2');
var win = ctrl.openWin();
win.show();
}
// resident in controller 2
openWin: function() {
var win = Ext.create('Ext.window.Window',{
items: [{
xtype: 'shipmentmonthly' // This is not recognized
}]
});
return win;
}

Resources