Jasmine test complaining about 'undefined' is not an object - javascript

I have checked other questions similar to my problem. but this problem can apparently be different in every case.
Angular Jasmine Test complains
TypeError: 'undefined' is not an object (evaluating 'fields.forEach')at discoverDependentFields
Here is my discoverDependentFields function
discoverDependentFields($scope.response.Fields);
function discoverDependentFields(fields) {
fields.forEach(function (field) {
field.DependencyFieldEvaluated = '';
if (field.DependencyField) {
var foundFields = fields.filter(function (fieldToFind) { return fieldToFind.Name === field.DependencyField; });
if (foundFields.length === 1) {
field.DependencyFieldEvaluated = foundFields[0];
}
}
});
}
and in the test I have this bit
this.controller('MyController', {
'$scope': this.scope,
}
});
this.scope.response.Fields = [
{
Name: "UserIdentity",
Value: {
"FirstName": "John"
},
PropertyName: "User.Identity"
}
];
I use the value of field.DependencyFieldEvaluated in a function in a directive like this
function dependencyMet(field) {
var dependentField = field.DependencyFieldEvaluated;
var met = compareToDependencyValue(field, dependentField.Value);
return met;
}
I have no idea why it is complaining

If
discoverDependentFields($scope.response.Fields);
is a line in your controller, then you need to setup the $scope.response.Fields data before instantiating the controller. In other words, swap the order of operations in your test to be
this.scope = {};
// or maybe this.scope = $rootScope.$new()
this.scope.response = {
Fields: [{
Name: "UserIdentity",
Value: {
FirstName: "John"
},
PropertyName: "User.Identity"
}]
};
this.controller('MyController', {
$scope: this.scope,
});

Related

TypeError: Cannot read property 'push' of undefined [VueJs]

I am trying to add an object to an array but it is not working with me, the program can't read the property push
I defined an array in <script>:
Data: function() {
return {
Projects: [
{
name: '',
id: 0,
subscribers: 0,
products: {name:'',color:''},
}
],
}
And in the function:
GetAllWorkspaces: function(){
var app = this;
const instance = axios.create({
timeout: 1000,
headers: {
........
}
});
instance.get("XXXXXXX")
.then( function(response) {
console.log(response);
Object.keys(response.data.result).forEach( function (product) {
var subscribersCounter = 0;
let example = {
name: response.data.result[product].name,
id: response.data.result[product].id,
subscribers: response.data.result[product].subscribers,
products: response.data.result[product].products,
};
let uploadedExample = {
name: '',
id: '',
subscribers: '',
products: {name:'',color:''},
};
uploadedExample.name = example.name;
uploadedExample.id = example.id;
if ( example.subscribers ) {
Object.keys(example.subscribers).forEach(function (key) {
subscribersCounter++;
});
}
uploadedExample.subscribers = subscribersCounter;
if ( example.products ) {
Object.keys(example.products).forEach(function (Pkeys) {
uploadedExample.products.name = Pkeys;
Object.keys(example.products[Pkeys]).forEach(function (key) {
if (key == 'color') {
uploadedExample.products.color = example.products[Pkeys][key];
}
});
});
}
//add the new workspace to the list of workspaces.
app.Projects.push(uploadedExample);
});
})
.catch(function(error) {
console.log(error);
});
My problem is with this line
app.Projects.push(uploadedExample);
where when I try to push an object into the array, the error message is shown:
TypeError: Cannot read property 'push' of undefined
As the error says, the problem is that app.Projects is undefined. This happens because 'this' refers to the function scope inside GetAllWorkspaces and not to the component scope (you can try it by console.logging 'this' - anyway- it is a good practice under all circumstances because 'this' can change from context to context). If you want to keep the component scope inside the method, you should use an arrow function like this:
GetAllWorkspaces: () => {
// do all your stuff
}

Why is my angular factory provider code throwing an error on the injected $window object

I a trying to access the browser $window object in angular but I keep getting this error Error: $window is undefined even when this same code works perfectly in a service provider code:
Here is the sessionFactory code:
angular.module('app').factory('sessionFactory', [
'$window',
'formattingFactory',
sessionFactory
]);
var myFormattingFactory = new formattingFactory();
function sessionFactory($window, formattingFactory) {
function formatText(text) {
myFormattingFactory.format(text);
}
return {
save: function(key, value) {
$window.sessionStorage.setItem(key, formatText(value));
},
get: function(key) {
return $window.sessionStorage.getItem(key);
},
clear: function() {
$window.sessionStorage.clear();
}
}
}
And this is my sessionController code:
angular.module('app').controller('sessionController', [
'sessionService',
'sessionFactory',
sessionController
]);
var mySessionFactory = new sessionFactory();
function sessionController(sessionService, sessionFactory) {
var vm = this;
vm.getFactorySession = getFactorySession;
vm.setFactorySession = setFactorySession;
vm.clearFactorySession = clearFactorySession;
vm.getServiceSession = function() {
vm.model = {
name: sessionService.get('name'),
nickname: sessionService.get('nickname'),
status: 'Retrieved by service on' + new Date()
}
}
vm.setServiceSession = function() {
sessionService.save('name', vm.model.name);
sessionService.save('nickname', vm.model.nickname);
vm.getServiceSession();
}
vm.clearServiceSession = function() {
sessionService.clear();
vm.getServiceSession();
}
function getFactorySession() {
vm.model = {
name: mySessionFactory.get('name'),
nickname: mySessionFactory.get('nickname'),
status: 'Retrieved by Factory on ' + new Date()
};
}
function setFactorySession() {
mySessionFactory.save('name', vm.model.name);
mySessionFactory.save('nickname', vm.model.nickname);
getFactorySession();
}
function clearFactorySession() {
mySessionFactory.clear();
getFactorySession();
}
}
And this is the code for the sessionService that works great and can access the browser $window object without any error:
angular.module('app').service('sessionService', [
'$window',
sessionService
]);
function sessionService($window) {
this.save = save;
this.get = get;
this.clear = clear;
function save(key, value) {
$window.sessionStorage.setItem(key, value);
}
function get(key) {
return $window.sessionStorage.getItem(key)
}
function clear() {
$window.sessionStorage.clear();
}
}
This is the formattingFactory code:
angular.module('app').factory('formattingFactory', [
formattingFactory
]);
function formattingFactory() {
function format(text) {
this.text = text;
if ((text.trim().length % 2) === 0) {
return text.toUpperCase();
} else {
return text.toLowerCase();
}
}
return {
format: format
}
}
myFormattingFactory is a dependency of sessionFactory and should reside inside its factory function:
angular.module('app').factory('sessionFactory', [
'$window',
'formattingFactory',
sessionFactory
]);
function sessionFactory($window, formattingFactory) {
// formattingFactory is an object and can be used here
...
formattingFactory service instance is passed as an argument there. It is undefined otherwise.

Unknown JavaScript syntax when defining an object

I came across some JavaScript syntax I haven't seen before. Can someone help point me in the right direction, I'm not even sure what I could google to learn more :/
$.variable = function() {
return a(b, [{
key: "setPeriod",
value: function(t) {
this.variable.period = t;
}
}, {
key: "location",
get: function() {
return "earth";
}
}])
}
$.variable.setPeriod("test");
$.variable.location;
My question is what is this structure where an object is defined via a list. Also not sure what the difference between value and get is. Any help would be appreciated.
Here's an example of a snippet:
https://pastebin.com/zymW2XZw
Here my guess about what happens to this list of objects :
var o = s([{
key: "attribute",
value: "default"
}, {
key: "getAttribute",
value: function () {
return this.attribute;
}
}, {
key: "setAttribute",
value: function (value) {
this.attribute = value;
}
}]);
console.log(o.getAttribute());
o.setAttribute("custom");
console.log(o.getAttribute());
function s (members) {
var o = {};
members.forEach(function (member) {
o[member.key] = member.value;
});
return o;
}
I guess the framework needs to preprocess the object's members for some obscure reasons related to the framework internal mecanism.

Angularjs retain local variable

I have a factory like this:
TestFactory= function () {
var objectName=null;
return {
SetName:function(name) {
objectName = name;
},
GetName:function() {
return objectName;
},
Init:function() {
return angular.copy(this);
}
}
}
A controller like:
TestController = function($scope) {
$scope.TestClick = function () {
var tstA = TestFactory.Init();
var tstB = TestFactory.Init();
tstA.SetName('test A')
tstB.SetName('test B')
console.log('A', tstA.GetName());
console.log('B', tstB.GetName());
}
}
In the console I get Test B for both objects.
How can I make a proper instance of this object?
I would like to use the objectName value in other functions of the factory.
Take into account that in Angular, Factories are singletons, so the instance is always the same.
You can do the following:
TestFactory= function () {
var objectName={};
return {
SetName:function(property,name) {
objectName[property] = name;
},
GetName:function(property) {
return objectName[property];
},
Clear:function(property) {
delete objectName[property]
}
}
}
Then in your controller:
TestController = function($scope, TestFactory) {
$scope.TestClick = function () {
TestFactory.SetName('a','test A')
TestFactory.SetName('b','test B')
console.log('A', TestFactory.GetName('a')); // test A
console.log('B', TestFactory.GetName('b')); // test B
}
}
Couple of issues. First your returning an object rather than a function from your factory.
app.factory('TestFactory', function() {
return function() {
var objectName = null;
var setName = function(name) {
objectName = name;
};
var getName = function() {
return objectName;
};
return {
SetName: setName,
GetName: getName
};
};
});
Then you can just instantiate like this:
var tstA = new TestFactory();
var tstB = new TestFactory();
Services and factories are singletons so I think you can achieve what you want with a more appropriate use of the factory by providing an Init function that returns the common code and unique name like so:
angular.module('app')
.factory('ServiceFactory', serviceFactory);
function serviceFactory() {
return {
Init: function (name) {
return {
objectName: name,
setName: function (name) {
this.objectName = name;
},
getName: function () {
return this.objectName;
}
};
}
};
}
This leaves the possibility to use it as a factory that can initialize many types.
You basically need to create a simple getter/setter.
angular.module('app', [])
.controller('TestController', testController)
.service('serviceFactory', serviceFactory);
testController.$inject = ['serviceFactory'];
function testController(serviceFactory) {
serviceFactory.set('A', {
name: 'test A'
});
serviceFactory.set('B', {
name: 'test B'
});
console.log(serviceFactory.getAll());
console.log(serviceFactory.get('A'));
console.log(serviceFactory.get('B'));
}
function serviceFactory() {
var
_model = {
name: ""
},
_data = {};
return {
set: function(key, data) {
_data[key] = angular.extend({}, _model, data);
},
get: function(key) {
return _data[key];
},
getAll: function() {
return _data;
}
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<body ng-app="app" ng-controller="testController"></body>

javascript: Alter the output of a function to the specified decimal place

I am not very good with my javascript but recently needed to work with a library to output an aggregated table. Was using fin-hypergrid.
There was a part where I need to insert a sum function (rollups.sum(11) in this example)to an object so that it can compute an aggregated value in a table like so:
aggregates = {Value: rollups.sum(11)}
I would like to change this value to return 2 decimal places and tried:
rollups.sum(11).toFixed(2)
However, it gives the error : "rollups.sum(...).toFixed is not a function"
If I try something like:
parseFloat(rollups.sum(11)).toFixed(2)
it throws the error: "can't assign to properties of (new String("NaN")): not an object"
so it has to be a function object.
May I know if there is a way to alter the function rollups.sum(11) to return a function object with 2 decimal places?
(side info: rollups.sum(11) comes from a module which gives:
sum: function(columnIndex) {
return sum.bind(this, columnIndex);
}
)
Sorry I could not post sample output here due to data confidentiality issues.
However, here is the code from the example I follow. I basically need to change rollups.whatever to give decimal places. The "11" in sum(11) here refers to a "column index".
window.onload = function() {
var Hypergrid = fin.Hypergrid;
var drillDown = Hypergrid.drillDown;
var TreeView = Hypergrid.TreeView;
var GroupView = Hypergrid.GroupView;
var AggView = Hypergrid.AggregationsView;
// List of properties to show as checkboxes in this demo's "dashboard"
var toggleProps = [{
label: 'Grouping',
ctrls: [
{ name: 'treeview', checked: false, setter: toggleTreeview },
{ name: 'aggregates', checked: false, setter: toggleAggregates },
{ name: 'grouping', checked: false, setter: toggleGrouping}
]
}
];
function derivedPeopleSchema(columns) {
// create a hierarchical schema organized by alias
var factory = new Hypergrid.ColumnSchemaFactory(columns);
factory.organize(/^(one|two|three|four|five|six|seven|eight)/i, { key: 'alias' });
var columnSchema = factory.lookup('last_name');
if (columnSchema) {
columnSchema.defaultOp = 'IN';
}
//factory.lookup('birthState').opMenu = ['>', '<'];
return factory.schema;
}
var customSchema = [
{ name: 'last_name', type: 'number', opMenu: ['=', '<', '>'], opMustBeInMenu: true },
{ name: 'total_number_of_pets_owned', type: 'number' },
{ name: 'height', type: 'number' },
'birthDate',
'birthState',
'employed',
{ name: 'income', type: 'number' },
{ name: 'travel', type: 'number' }
];
var peopleSchema = customSchema; // or try setting to derivedPeopleSchema
var gridOptions = {
data: people1,
schema: peopleSchema,
margin: { bottom: '17px' }
},
grid = window.g = new Hypergrid('div#json-example', gridOptions),
behavior = window.b = grid.behavior,
dataModel = window.m = behavior.dataModel,
idx = behavior.columnEnum;
console.log('Fields:'); console.dir(behavior.dataModel.getFields());
console.log('Headers:'); console.dir(behavior.dataModel.getHeaders());
console.log('Indexes:'); console.dir(idx);
var treeView, dataset;
function setData(data, options) {
options = options || {};
if (data === people1 || data === people2) {
options.schema = peopleSchema;
}
dataset = data;
behavior.setData(data, options);
idx = behavior.columnEnum;
}
// Preset a default dialog options object. Used by call to toggleDialog('ColumnPicker') from features/ColumnPicker.js and by toggleDialog() defined herein.
grid.setDialogOptions({
//container: document.getElementById('dialog-container'),
settings: false
});
// add a column filter subexpression containing a single condition purely for demo purposes
if (false) { // eslint-disable-line no-constant-condition
grid.getGlobalFilter().columnFilters.add({
children: [{
column: 'total_number_of_pets_owned',
operator: '=',
operand: '3'
}],
type: 'columnFilter'
});
}
window.vent = false;
//functions for showing the grouping/rollup capabilities
var rollups = window.fin.Hypergrid.analytics.util.aggregations,
aggregates = {
totalPets: rollups.sum(2),
averagePets: rollups.avg(2),
maxPets: rollups.max(2),
minPets: rollups.min(2),
firstPet: rollups.first(2),
lastPet: rollups.last(2),
stdDevPets: rollups.stddev(2)
},
groups = [idx.BIRTH_STATE, idx.LAST_NAME, idx.FIRST_NAME];
var aggView, aggViewOn = false, doAggregates = false;
function toggleAggregates() {
if (!aggView){
aggView = new AggView(grid, {});
aggView.setPipeline({ includeSorter: true, includeFilter: true });
}
if (this.checked) {
grid.setAggregateGroups(aggregates, groups);
aggViewOn = true;
} else {
grid.setAggregateGroups([], []);
aggViewOn = false;
}
}
function toggleTreeview() {
if (this.checked) {
treeView = new TreeView(grid, { treeColumn: 'State' });
treeView.setPipeline({ includeSorter: true, includeFilter: true });
treeView.setRelation(true, true);
} else {
treeView.setRelation(false);
treeView = undefined;
delete dataModel.pipeline; // restore original (shared) pipeline
behavior.setData(); // reset with original pipeline
}
}
var groupView, groupViewOn = false;
function toggleGrouping(){
if (!groupView){
groupView = new GroupView(grid, {});
groupView.setPipeline({ includeSorter: true, includeFilter: true });
}
if (this.checked){
grid.setGroups(groups);
groupViewOn = true;
} else {
grid.setGroups([]);
groupViewOn = false;
}
}
you may try:
(rollups.sum(11)).toFixed(2)
enclosing number in parentheses seems to make browser bypass the limit that identifier cannot start immediately after numeric literal
edited #2:
//all formatting and rendering per cell can be overridden in here
dataModel.getCell = function(config, rendererName) {
if(aggViewOn)
{
if(config.columnName == "total_pets")
{
if(typeof(config.value) == 'number')
{
config.value = config.value.toFixed(2);
}
else if(config.value && config.value.length == 3 && typeof(config.value[1]) == 'number')
{
config.value = config.value[1].toFixed(2);
}
}
}
return grid.cellRenderers.get(rendererName);
};

Categories