The below directive checks/loads the template with value "pass, fail, required".
The conditions are
if(parent value == required){
1. if the value is true --> $scope.msg = "fail" --> loads the template with {{msg}} value
2. if the value is false --> $scope.msg = "pass" --> loads the template with {{msg}} value
}
In detail,
loading Template: [showerror.html]
<div>{{msg}}</div>
Directive Call:
<div show-error="obj"></div>
(obj contains as obj.error and obj.required )
Directive:
angular.module("dsf").directive('showError', [
function () {
'use strict';
return {
scope: {
obj: '=showError'
},
link: function ($scope) {
$scope.showError = {};
$scope.msg = "";
function setTemplate(filename) {
$scope.showError.template = 'app/path/' + filename + '.html';
}
$scope.$watch(function () {
if ($scope.obj.required === true) {
if ($scope.obj.error === false) {
$scope.msg = "Pass";
} else if ($scope.obj.error === "required") {
$scope.msg = "Required";
} else if ($scope.obj.error === true) {
$scope.msg = "fail";
}
} else {
if ($scope.obj.error === true) {
$scope.msg = "fail";
} else if ($scope.obj.error === false) {
$scope.msg = "Pass";
}
}
setTemplate("showerror");
});
},
template: '<div ng-include="showError.template"></div>'
};
}
]);
As i am new to jasmine test, how can i write the test for this directive? any suggestions?
Ok. I have written the unit test for this directive. What is the wrong now?
describe('showError', function () {
'use strict';
var compile, $scope, element;
beforeEach(module('dsf'));
beforeEach(inject(function ($compile, $rootScope) {
element = angular.element('<div show-error="obj"></div>');
$scope = $rootScope.$new();
compile = function (obj) {
$scope.obj = obj;
$compile(element)($scope);
$scope.$digest();
};
}));
it('updates the element when obj validation changes', function () {
var obj;
obj = {};
$scope = compile(obj);
$scope.apply(function () {
obj.required = true;
obj.error = true;
});
expect($scope.obj.msg).toContain('fail');
$scope.apply(function () {
obj.required = true;
obj.error = false;
});
expect($scope.obj.msg).toContain('Pass');
$scope.apply(function () {
obj.required = true;
obj.error = "required";
});
expect($scope.obj.msg).toContain('Required');
$scope.apply(function () {
obj.required = false;
obj.error = true;
});
expect($scope.obj.msg).toContain('Pass');
$scope.apply(function () {
obj.required = false;
obj.error = false;
});
expect($scope.obj.msg).toContain('fail');
});
});
I am getting error:
undefined is not an object (evaluating $scopr.apply) error
The command you're looking for is $scope.$apply() not $scope.apply().
Related
My html code is as follows.
<div class="panel-heading">
Select areas to be visited by the operator
<multiselect ng-model="selection" options="areanames" show-search="true"></multiselect>
</div>
My application's controller code is as follows. The function getArea is being called when a user inputs some details in the form (not included here).
this.getArea = function(){
$scope.areanames = [];
$http({
method: "GET",
url: "http://xx.xx.xx.xx/abc",
params:{city:$scope.city,circle:$scope.circle}
}).then(function(success){
for (i = 0; i < success.data.length; i++)
$scope.areanames.push(success.data[i].area);
},function(error){
console.log('error ' + JSON.stringify(error));
});
}
The directive multiselect is written as follows.
multiselect.directive('multiselect', ['$filter', '$document', '$log', function ($filter, $document, $log) {
return {
restrict: 'AE',
scope: {
options: '=',
displayProp: '#',
idProp: '#',
searchLimit: '=?',
selectionLimit: '=?',
showSelectAll: '=?',
showUnselectAll: '=?',
showSearch: '=?',
searchFilter: '=?',
disabled: '=?ngDisabled'
},
replace:true,
require: 'ngModel',
templateUrl: 'multiselect.html',
link: function ($scope, $element, $attrs, $ngModelCtrl) {
$scope.selectionLimit = $scope.selectionLimit || 0;
$scope.searchLimit = $scope.searchLimit || 25;
$scope.searchFilter = '';
if (typeof $scope.options !== 'function') {
$scope.resolvedOptions = $scope.options;
}
if (typeof $attrs.disabled != 'undefined') {
$scope.disabled = true;
}
$scope.toggleDropdown = function () {
console.log('toggleDown');
$scope.open = !$scope.open;
};
var closeHandler = function (event) {
console.log('closeHandler');
if (!$element[0].contains(event.target)) {
$scope.$apply(function () {
$scope.open = false;
});
}
};
$document.on('click', closeHandler);
var updateSelectionLists = function () {
console.log('updateSelectionList');
if (!$ngModelCtrl.$viewValue) {
if ($scope.selectedOptions) {
$scope.selectedOptions = [];
}
$scope.unselectedOptions = $scope.resolvedOptions.slice(); // Take a copy
} else {
$scope.selectedOptions = $scope.resolvedOptions.filter(function (el) {
var id = $scope.getId(el);
for (var i = 0; i < $ngModelCtrl.$viewValue.length; i++) {
var selectedId = $scope.getId($ngModelCtrl.$viewValue[i]);
if (id === selectedId) {
return true;
}
}
return false;
});
$scope.unselectedOptions = $scope.resolvedOptions.filter(function (el) {
return $scope.selectedOptions.indexOf(el) < 0;
});
}
};
$ngModelCtrl.$render = function () {
console.log('render called');
updateSelectionLists();
};
$ngModelCtrl.$viewChangeListeners.push(function () {
console.log('viewChangeListener');
updateSelectionLists();
});
$ngModelCtrl.$isEmpty = function (value) {
console.log('isEmpty');
if (value) {
return (value.length === 0);
} else {
return true;
}
};
var watcher = $scope.$watch('selectedOptions', function () {
$ngModelCtrl.$setViewValue(angular.copy($scope.selectedOptions));
}, true);
$scope.$on('$destroy', function () {
console.log('destroy');
$document.off('click', closeHandler);
if (watcher) {
watcher(); // Clean watcher
}
});
$scope.getButtonText = function () {
console.log('getButtonText');
if ($scope.selectedOptions && $scope.selectedOptions.length === 1) {
return $scope.getDisplay($scope.selectedOptions[0]);
}
if ($scope.selectedOptions && $scope.selectedOptions.length > 1) {
var totalSelected;
totalSelected = angular.isDefined($scope.selectedOptions) ? $scope.selectedOptions.length : 0;
if (totalSelected === 0) {
return 'Select';
} else {
return totalSelected + ' ' + 'selected';
}
} else {
return 'Select';
}
};
$scope.selectAll = function () {
console.log('selectAll');
$scope.selectedOptions = $scope.resolvedOptions;
$scope.unselectedOptions = [];
};
$scope.unselectAll = function () {
console.log('unSelectAll');
$scope.selectedOptions = [];
$scope.unselectedOptions = $scope.resolvedOptions;
};
$scope.toggleItem = function (item) {
console.log('toggleItem');
if (typeof $scope.selectedOptions === 'undefined') {
$scope.selectedOptions = [];
}
var selectedIndex = $scope.selectedOptions.indexOf(item);
var currentlySelected = (selectedIndex !== -1);
if (currentlySelected) {
$scope.unselectedOptions.push($scope.selectedOptions[selectedIndex]);
$scope.selectedOptions.splice(selectedIndex, 1);
} else if (!currentlySelected && ($scope.selectionLimit === 0 || $scope.selectedOptions.length < $scope.selectionLimit)) {
var unselectedIndex = $scope.unselectedOptions.indexOf(item);
$scope.unselectedOptions.splice(unselectedIndex, 1);
$scope.selectedOptions.push(item);
}
};
$scope.getId = function (item) {
console.log('getID');
if (angular.isString(item)) {
return item;
} else if (angular.isObject(item)) {
if ($scope.idProp) {
return multiselect.getRecursiveProperty(item, $scope.idProp);
} else {
$log.error('Multiselect: when using objects as model, a idProp value is mandatory.');
return '';
}
} else {
return item;
}
};
$scope.getDisplay = function (item) {
console.log('getDisplay');
if (angular.isString(item)) {
return item;
} else if (angular.isObject(item)) {
if ($scope.displayProp) {
return multiselect.getRecursiveProperty(item, $scope.displayProp);
} else {
$log.error('Multiselect: when using objects as model, a displayProp value is mandatory.');
return '';
}
} else {
return item;
}
};
$scope.isSelected = function (item) {
console.log('isSelected');
if (!$scope.selectedOptions) {
return false;
}
var itemId = $scope.getId(item);
for (var i = 0; i < $scope.selectedOptions.length; i++) {
var selectedElement = $scope.selectedOptions[i];
if ($scope.getId(selectedElement) === itemId) {
return true;
}
}
return false;
};
$scope.updateOptions = function () {
console.log('updateOptions');
if (typeof $scope.options === 'function') {
$scope.options().then(function (resolvedOptions) {
$scope.resolvedOptions = resolvedOptions;
updateSelectionLists();
});
}
};
// This search function is optimized to take into account the search limit.
// Using angular limitTo filter is not efficient for big lists, because it still runs the search for
// all elements, even if the limit is reached
$scope.search = function () {
console.log('search');
var counter = 0;
return function (item) {
if (counter > $scope.searchLimit) {
return false;
}
var displayName = $scope.getDisplay(item);
if (displayName) {
var result = displayName.toLowerCase().indexOf($scope.searchFilter.toLowerCase()) > -1;
if (result) {
counter++;
}
return result;
}
}
};
}
};
}]);
When areanames is getting updated asynchronously its values are not getting displayed in multiselect. The inner scope's options value is becoming undefined though I am using '=' with same attribute name i.e., options in the html code.
I have some objects in my angularjs controller.
var app = angular.module("myApp", []);
app.controller("noteCtrl", function ($scope) {
$scope.draft = {};
$scope.notes = [];
$scope.note = {};
$scope.submit = function() {
$scope.notes.push($scope.note);
$scope.note = {};
};
$scope.save = function() {
if ($scope.button == "Save") {
$scope.draft = angular.copy($scope.note);
} else {
$scope.note = $scope.draft;
}
};
$scope.cancel = function() {
$scope.note = {};
}
});
I need to check whether the draft object is empty, and output the save successfully information.
<span data-ng-hide="draft == {}" style="color:green">Your note has been saved.</span>
I have also tried:
<span data-ng-hide="draft.length == -1" style="color:green">Your note has been saved.</span>
or
<span data-ng-hide="draft == ''" style="color:green">Your note has been saved.</span>
But all of them are failed.
Add function isEmptyObject in your controller:
var app = angular.module("myApp", []);
app.controller("noteCtrl", function ($scope) {
$scope.draft = {};
$scope.note = {};
$scope.notes = [];
$scope.save = function() {
if ($scope.button == "Save") {
$scope.draft = angular.copy($scope.note);
} else {
$scope.note = $scope.draft;
}
};
$scope.cancel = function() {
$scope.note = {};
}
$scope.isEmptyObject = function (obj) {
for (var i in obj) if (obj.hasOwnProperty(i)) return false;
return true;
};
});
Or you can use this function:
$scope.isEmptyObject = function (obj) {
return Object.keys(obj).length === 0;
}
HTML:
<span data-ng-hide="isEmptyObject(draft)" style="color:green">Your note has been saved.</span>
Please initialize null instead of empty object
$scope.draft = null;
And html should be
<span data-ng-hide="!draft" style="color:green">Your note has been saved.</span>
You can chek the condition inline in your span.
HTML:
<span data-ng-hide="Object.keys(draft).length === 0" style="color:green">Your note has been saved.</span>
I use a directive that is declared like this :
(function (directives) {
var FilterDirective = (function () {
function FilterDirective() {
var directive = {};
directive.restrict = 'A';
directive.scope = true;
directive.controller = elasticui.controllers.FilterController;
directive.link = function (scope, element, attrs, filterCtrl) {
scope.$watch(element.attr('eui-filter') + " | euiCached", function (val) { return scope.filter.filter = val; });
var enabled = false;
var enabledAttr = element.attr('eui-enabled');
if (enabledAttr) {
scope.$watch(enabledAttr, function (val) { return scope.filter.enabled = val; });
enabled = scope.$eval(enabledAttr);
}
scope.filter = {
filter: scope.$eval(element.attr('eui-filter') + " | euiCached"),
enabled: enabled
};
filterCtrl.init();
};
return directive;
}
return FilterDirective;
})();
directives.FilterDirective = FilterDirective;
directives.directives.directive('euiFilter', FilterDirective);
})
The controller of the directive is :
(function (controllers) {
var FilterController = (function () {
function FilterController($scope) {
this.scope = $scope;
}
FilterController.prototype.init = function () {
var _this = this;
if (this.scope.filter.filter) {
var isEnabled = this.scope.filters.contains(this.scope.filter.filter);
if (!isEnabled && this.scope.filter.enabled) {
this.scope.filters.add(this.scope.filter.filter);
isEnabled = true;
}
}
this.scope.filter.enabled = isEnabled;
this.scope.$watch('filter.enabled', function (newVal, oldVal) {
if (newVal !== oldVal) {
_this.updateFilter();
}
});
this.scope.$watch('filter.filter', function (newVal, oldVal) {
if (!elasticui.util.EjsTool.equals(oldVal, newVal)) {
if (oldVal) {
_this.scope.filters.remove(oldVal);
}
_this.updateFilter();
}
});
};
FilterController.prototype.updateFilter = function () {
if (!this.scope.filter.filter) {
return;
}
if (this.scope.filter.enabled) {
this.scope.filters.add(this.scope.filter.filter);
}
else {
this.scope.filters.remove(this.scope.filter.filter);
}
};
FilterController.$inject = ['$scope'];
return FilterController;
})();
controllers.FilterController = FilterController;
})
Actually, the directive has a scope containing a filter object which contains two attributes filter : { enabled : boolean, filter : object} and the directive is used like this :
<label class="btn" ng-model="filter.enabled"
eui-filter="ejs.TermFilter('field','value')" btn-checkbox>
when the button is clicked the filter.enabled is set. My purpose is to add a behavior that will permit to change filter.enabled value via a function external to the directive.
The directive will look like this :
<label class="btn" ng-model="filter.enabled"
eui-filter="ejs.TermFilter('field','value')" eui-enable-fn="fn(somevariable)" btn-checkbox>
where fn will take the somevariable and set it to the filter.enabled.
Thanks in advance,
If you want to enable/disable a filter through the pressure of a button why not declare a filter with the property eui-enabled set to a custom toggling variable?
In other words it would result as:
HTML:
<label class="btn" eui-filter="..." eui-enabled="my_toggling_variable">
<button type=button ng-click="toggleVar()"></button>
JS:
myApp.controller('myCtrl', ['$scope', function($scope) {
$scope.my_toggling_variable = false;
$scope. toggleVar = function(){
$scope.my_toggling_variable = !$scope.my_toggling_variable;
};
}]);
Hope to have understood well the topic.
I am getting a headache trying to do this, I have a very big controller which I need to split, this controller has 177 lines already so I need 2 controllers. Once I try to split it my app breaks down.
Maybe you could help by giving suggestions on how to start and what I have to evaluate first. I am new to Angular and I thought it would be easier.
'use strict';
angular.module('capilleira.clickAndGambleMobile.controllers')
.controller('LinesController', function($scope, $timeout, $state,
$stateParams, $ionicLoading, $rootScope, LinesFactory, BetSlipFactory) {
$scope.picksCount = false;
var validateSanitizeLineParams = function() {
var lineParamsOk = false;
if (validator.isAlphanumeric($stateParams.customerId) &&
validator.isNumeric($stateParams.familyGameId) &&
validator.isNumeric($stateParams.games) &&
validator.isNumeric($stateParams.top) &&
validator.isNumeric($stateParams.sports) &&
validator.isLength($stateParams.leagues.trim(), 1) &&
validator.isAscii($stateParams.leagues.trim()) &&
validator.isLength($stateParams.periods.trim(), 1) &&
validator.isAscii($stateParams.periods.trim())) {
lineParamsOk = true;
_.each($stateParams.periods.split(','), function(periodId) {
if (!validator.isAlpha(periodId)) {
lineParamsOk = false;
}
});
_.each($stateParams.leagues.split(','), function(leagueId) {
if (!validator.isNumeric(leagueId)) {
lineParamsOk = false;
}
});
}
return lineParamsOk;
};
$scope.lineItems = [];
$rootScope.spinnerTitle = 'Loading lines';
$ionicLoading.show({
templateUrl: 'templates/loaders.html',
scope: $rootScope
});
$scope.doRefresh = function() {
if (validateSanitizeLineParams()) {
LinesFactory.getLines($stateParams).then(function(linesPerLeague) {
_.each(linesPerLeague, function(linesPerParent) {
_.each(linesPerParent, function(lines) {
_.each(lines, function(line) {
_.each(line.rows, function(row) {
if (!row.noSpread) {
line.displaySpreadButton = true;
}
if (!row.noTotal) {
line.displayTotalButton = true;
}
if (!row.noMoneyLine) {
line.displayMoneyLineButton = true;
}
});
if (line.displaySpreadButton) {
line.displaySpread = true;
} else if (line.displayTotalButton) {
line.displayTotal = true;
} else if (line.displayMoneyLineButton) {
line.displayMoneyLine = true;
}
});
});
});
$scope.lineItems = linesPerLeague;
if (!$scope.lineItems[0].length) {
$state.go('app.noLines');
}
$ionicLoading.hide();
$scope.addLineSelections();
}, function(err) {
console.log(err);
$rootScope.spinnerTitle = 'Wrong Params';
$ionicLoading.show({
templateUrl: 'templates/loaders.html',
scope: $rootScope
});
$timeout(function() {
$ionicLoading.hide();
$state.go('app.login');
}, 1500);
});
}else {
$rootScope.spinnerTitle = 'Wrong Params';
$ionicLoading.show({
templateUrl: 'templates/loaders.html',
scope: $rootScope
});
$timeout(function() {
$ionicLoading.hide();
$state.go('app.login');
}, 1500);
}
$scope.$broadcast('scroll.refreshComplete');
};
$scope.doRefresh();
$scope.showLine = function(lineType, line) {
switch (lineType) {
case 'spread':
line.displayTotal = false;
line.displayMoneyLine = false;
line.displaySpread = false;
$timeout(function() {
line.displaySpread = true;
}, 50);
break;
case 'total':
line.displaySpread = false;
line.displayMoneyLine = false;
line.displayTotal = false;
$timeout(function() {
line.displayTotal = true;
}, 50);
break;
case 'moneyline':
line.displaySpread = false;
line.displayTotal = false;
line.displayMoneyLine = false;
$timeout(function() {
line.displayMoneyLine = true;
}, 50);
break;
}
};
$scope.addLineToBetSlip = function(line, row, type) {
var spreadSelected = (row.spreadSelected && type === 'spread'),
totalSelected = (row.totalSelected && type === 'total'),
moneyLineSelected = (row.moneyLineSelected && type === 'moneyline');
if (spreadSelected || totalSelected || moneyLineSelected) {
BetSlipFactory.remove(line, row, type);
}else {
BetSlipFactory.add(line, row, type);
}
$scope.picksCount = !$scope.picksCount;
};
$scope.addLineSelections = function() {
BetSlipFactory.getBetSlip().then(function(betSlip) {
var flattenLines = _.flatten($scope.lineItems),
lineFound, row;
_.each(betSlip, function(slip) {
lineFound = _.find(flattenLines, function(line) {
return line.gameId === slip.gameId &&
line.part === slip.part &&
line.lineTypeName === slip.lineTypeName;
});
if (lineFound) {
row = _.find(lineFound.rows, function(row) {
return row.nss === slip.nss;
});
if (row) {
switch (slip.type) {
case 'spread':
row.spreadSelected = true;
break;
case 'total':
row.totalSelected = true;
break;
case 'moneyline':
row.moneyLineSelected = true;
break;
}
}
}
});
});
};
});
This works as expected.
var app = angular.module('plunker', []);
var FatCtrl1 = function($scope){
$scope.stuff1 = this.hello;
};
var FatCtrl2 = function($scope){
$scope.stuff2 = "World";
};
app.controller('FatCtrl', function($scope) {
this.hello = "Hello";
FatCtrl1.apply(this, arguments);
FatCtrl2.apply(this, arguments);
});
Beware of closures. Variables within FatCtrl are not available in the partitions. 'this' is used to share data.
Why is readingController not able to use the Romanize service? It always says Romanize is undefined inside the function. How can I get the service in scope?
var readingController = function (scope, Romanize){
scope.currentMaterial = scope.sections[scope.sectionNumber].tutorials[scope.tutorialNumber].material;
Romanize;
}
var app = angular.module('Tutorials', ['functions', 'tutorials']).controller('getAnswers', function ($scope, $element) {
$scope.sectionNumber = 0;
$scope.tutorialNumber = 0;
$scope.questionNumber = 0;
$scope.sections = sections;
$scope.loadFromMenu = function (sec, tut, first) {
if (tut === $scope.tutorialNumber && sec === $scope.sectionNumber && !first) {//if clicked on already playing tut
return;
}
if (tut !== undefined && sec !== undefined) {
$scope.tutorialNumber = tut;
$scope.sectionNumber = sec;
}
for (var x in sections) {
sections[x].active = "inactive";
for (var y in sections[x].tutorials){
sections[x].tutorials[y].active = "inactive";
}
}
var section = sections[$scope.sectionNumber];
section.active = "active";
section.tutorials[$scope.tutorialNumber].active = "active";
$scope.questionNumber = 0;
$scope.currentTutorialName = sections[$scope.sectionNumber].tutorials[$scope.tutorialNumber].name;
$scope.$apply();
if ($scope.sectionNumber === 0){
readingController($scope, app.Romanize);
}else if ($scope.sectionNumber === 1){
conjugationController($scope);
}
};
$scope.loadFromMenu(0,0, true);
var conjugationController = function (){
var loadNewVerbs = function (scope) {
scope.currentVerbSet = scope.sections[scope.sectionNumber].tutorials[scope.tutorialNumber].verbs;
if (scope.currentVerbSet === undefined) {
alert("Out of new questions");
return
}
scope.verbs = conjugate(scope.currentVerbSet[scope.questionNumber]);
scope.correct = scope.verbs.conjugations[0].text;
fisherYates(scope.verbs.conjugations);
scope.$apply();
};
loadNewVerbs($scope);
$scope.checkAnswer = function (answer) {
if($scope.sectionNumber === 0 && $scope.tutorialNumber === 0 && $("video")[0].currentTime < 160){
$scope.message = "Not yet!";
$(".message").show(300).delay(900).hide(300);
return;
}
answer.colorReveal = "reveal-color";
if (answer.text === $scope.correct) { //if correct skip to congratulations
$scope.questionNumber++;
setTimeout(function () {
loadNewVerbs($scope);
$scope.$apply();
}, 2000);
} else { //if incorrect skip to try again msg
if ($scope.sectionNumber === 0 && $scope.tutorialNumber === 0) {
start(160.5);
pause(163.8)
}
}
};
};
});
app.factory('Romanize', ['$http', function($http){
return{
get: function(){
$http.get(scope.sections[scope.sectionNumber].romanizeService).success(function(data) {
$scope.romanized = data;
});
}
};
}])
Update: based on comments/discussion below:
For a service to be instantiated, it has to be injected somewhere – not just anywhere – somewhere where Angular accepts injectables. Just inject it into your getAnswers controller – .controller('getAnswers', function ($scope, $element, Romanize) – then pass it to your "controller" function: readingController($scope, Romanize).
Since I don't think readingController is a real Angular controller, you can name the arguments whatever you want, so scope should be fine.
Original attempt at an answer:
Inject $scope not scope into your controller:
var readingController = function ($scope, Romanize){
Then I don't get any errors: Plunker.