Call Angular nested functions - javascript

I'm trying to call a nested function in Angular. I've formatted the functions in such a way in order to neaten up code, yet when invoking the function through an ng-click it doesnt seem to work.
In my case, a scope conflict occurs because the variable name is taken by the local scope, so I've named a controller and invoked it as a child property of the controller, but no success.
I've created a jsfiddle to demonstrate what I mean:
https://jsfiddle.net/838L40hf/16/
HTML:
<div class="InviteAppContainer" ng-app="InviteApp">
<div ng-controller="InviteController as cntrl">
<button ng-click="alert()">
Alert, standard
</button>
<div ng-repeat="invite in invites">
<button ng-click="cntrl.invite.alert(invite.name)">
Alert, {{invite.name}}
</button>
</div>
</div>
</div>
JS:
var InviteApp = angular.module('InviteApp',[])
.controller('InviteController', function($scope) {
$scope.invites = {
0 : {
"name":"invite 1",
},
1 :{
"name" : "invite 2"
}
};
$scope.invite = {
alert : function(name) {
alert(name);
}
};
$scope.alert = function() {
alert("alert2!");
};
});

If you're using the controller as syntax, you should be binding things to this instead of $scope if you wish to access them through the aliased controller name.
Simply changing the binding from $scope.invite to this.invite will do the trick.
https://jsfiddle.net/pL4wc10n/

You should use this
Javascript
var InviteApp = angular.module('InviteApp',[])
.controller('InviteController', function($scope) {
// controllerAs : cntrl
var that = this;
$scope.invites = {
0 : {
"name":"invite 1",
},
1 :{
"name" : "invite 2"
}
};
// USING THIS you have cntrl.function
that.invite = {
alert : function(name) {
alert(name);
}
};
$scope.alert = function() {
alert("alert2!");
};
});

var InviteApp = angular.module('InviteApp',[])
.controller('InviteController', function($scope) {
$scope.invites = {
0 : {
"name":"invite 1",
},
1 :{
"name" : "invite 2"
}
};
$scope.invite = {
alert : function(name) {
alert(name);
}
};
$scope.alert = function() {
alert("alert2!");
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="InviteAppContainer" ng-app="InviteApp">
<div ng-controller="InviteController as cntrl">
<button ng-click="alert()">
Alert, standard
</button>
<div ng-repeat="i in invites">
<button ng-click="invite.alert(i.name)">
Alert, {{i.name}}
</button>
</div>
</div>
</div>
What I edit:
<div ng-repeat="i in invites">
<button ng-click="invite.alert(i.name)">
Alert, {{i.name}}
</button>
</div>

Related

Why can't I print the value from input type using AngularJS?

Here is a very simple application that lets the user change his name. However, there is some bug in this code. Whenever the user writes something into input, the change is not reflected in the welcome header.
app.js
'use strict';
angular
.module('angularScopesBug', [])
.controller("WelcomeController", function ($scope) {
$scope.name = 'John Doe';
$scope.getName = function() {
return $scope.name;
};
})
.controller("EditingController", function($scope) {
$scope.editMode = false;
$scope.changeName = function() {
$scope.editMode = true;
};
$scope.closeEditor = function() {
$scope.editMode = false;
};
})
.directive("nameEditor", function () {
return {
template: 'Write your name: <input type="text" ng-model="name">'
};
});
index.html
<div ng-app="angularScopesBug" ng-controller="WelcomeController">
<h1>Hello, {{ getName() }}</h1>
<div ng-controller="EditingController">
<button ng-click="changeName()" ng-show="!editMode">Change your name</button>
<div ng-show="editMode">
<name-editor ng-show="editMode"></name-editor>
<button ng-click="closeEditor()" ng-show="editMode">Close name editor</button>
</div>
</div>
</div>
The header should change according to the input.
Always use an object in ng-model.
Primitives have no inheritance and the nested controller is using a child scope. When that child scope is created it gets the primitive name as value.
When it is an object the object is inherited so that updating property values will be reflected in all references to that object
angular
.module('angularScopesBug', [])
.controller("WelcomeController", function($scope) {
$scope.model = {
name: 'John Doe'
};
})
.controller("EditingController", function($scope) {
// simplified for clarity
})
.directive("nameEditor", function() {
return {
template: 'Write your name: <input type="text" ng-model="model.name">'
};
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.11/angular.min.js" ></script>
<div ng-app="angularScopesBug" ng-controller="WelcomeController">
<h1>Hello, {{ model.name }}</h1>
<div ng-controller="EditingController">
<button ng-click="editMode = true" ng-show="!editMode">Change your name</button>
<div ng-show="editMode">
<name-editor ng-show="editMode"></name-editor>
<button ng-click="editMode = false" ng-show="editMode">Close name editor</button>
</div>
</div>
</div>
Your two controllers have different $scope values. Since the editor is inside the EditingController $scope, it is changing a .name value in the lower scope, not in the higher WelcomeController $scope.
Try giving yourself a parent method to modify the value;
$scope.changeName = function(str) {
$scope.name = str;
};
And then calling that method with the new name from the child.

Angular click event not working, what gives?

I'm building a silly little calculator in an attempt to learn AngularJS. I'm attempting to use ng-click trigger events to update the "screen" of my calculator. Here's my code:
var calcApp = angular.module('NodeCalc', []);
calcApp.controller('CalcController', ['$scope', function ($scope) {
$scope.memory = {
recall: function() {
console.log('memory recall');
},
clear: function() {
console.log('memory clear');
},
add: function(value) {
console.log('memory add');
}
}
$scope.buttons = {
memory: [
{text: 'mrc', action: $scope.memory.recall},
{text: 'm-', action: $scope.memory.clear},
{text: 'm+', action: $scope.memory.add},
]
};
}]);
<body ng-app="NodeCalc" ng-controller="CalcController">
<form class="calc">
<p class="calc-display">
<input type="text" name="res" id="res" value="0" class="calc-display-input" onfocus="this.blur()">
</p>
<p class="calc-row">
<button ng-repeat="button in buttons.memory" type="button" class="calc-button calc-button-gray" ng-click="{{button.action}}">{{button.text}}</button>
<button type="button" class="calc-button calc-button-red calc-button-big" onclick="a('/')">/</button>
</p>
So, I know it has to do with the way I'm attaching my memory functions to my scope object. Can I not use scope in this way on the ng-click directive? I'm not really sure how else to achieve my goal here. The buttons render correctly, but I get a huge angular error-barf in my console related to:
Error: [$parse:syntax] http://errors.angularjs.org/1.5.8/$parse/syntax?p0=%7B&p1=invalid%20key&p2=2&p3=%7B%7Bbutton.action%7D%7D&p4=%7Bbutton.action%7D%7D
ng-click expression should be ng-click="button.action()"
While using ng-* directive, one is not suppose to wrap it with mustache, passed expression will be parsed by angular
var calcApp = angular.module('NodeCalc', []);
calcApp.controller('CalcController', ['$scope',
function($scope) {
$scope.memory = {
recall: function() {
console.log('memory recall');
},
clear: function() {
console.log('memory clear');
},
add: function(value) {
console.log('memory add');
}
}
$scope.buttons = {
memory: [{
text: 'mrc',
action: $scope.memory.recall
}, {
text: 'm-',
action: $scope.memory.clear
}, {
text: 'm+',
action: $scope.memory.add
}, ]
};
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="NodeCalc" ng-controller="CalcController">
<form class="calc">
<p class="calc-display">
<input type="text" name="res" id="res" value="0" class="calc-display-input" onfocus="this.blur()">
</p>
<p class="calc-row">
<button ng-repeat="button in buttons.memory" type="button" class="calc-button calc-button-gray" ng-click="button.action()">{{button.text}}</button>
<button type="button" class="calc-button calc-button-red calc-button-big" onclick="alert('/')">/</button>
</p>
One idea is just calling a function defined on $scope in the ng-click (the normal way) and passing the $index from the ng-repeat.
ng-click="clicked($index)"
$scope.clicked = function(index) {
var fn = $scope.buttons.memory[index].action;
fn();
}

Angularjs directive link call function in ng-click

My directive works fine, but I'd like to use it in ng-click However, the function inside link can't be triggered.
http://jsfiddle.net/ovzyro1f/
<div ng-app="editer" ng-controller="myCtrl" class="container">
<div class="parents">
<div ng-repeat="item in items" class="wrap" sibs>
<span>{{item.name}}</span>
<button ng-click="">click</button>
</div>
</div>
</div>
JS
function myCtrl($scope) {
$scope.editedItem = null;
$scope.items = [{
name: "item #1",
thing: "thing 1"
}, {
name: "item #2",
thing: "thing 2"
}, {
name: "item #3",
thing: "thing 3"
}];
$scope.show = false; //ignore this line
}
var editer = angular.module('editer', []);
editer.directive('sibs', function() {
return {
link: function(scope, element, attrs) {
element.bind('click', function() {
element.parent().children().addClass('unclicked');
element.removeClass('unclicked');
})
scope.myClick = function() {
element.parent().children().addClass('unclicked');
element.removeClass('unclicked');
}
},
}
});
I'd like to call the function in ng-click please see this one http://jsfiddle.net/ovzyro1f/2/ to remove sib from div ng-repeat="item in items" class="wrap"
<button ng-click="myClick()">click</button>
You should avoid to manipulate the DOM like we do in jQuery.
In Angular we think differently: it's the data which transforms automatically the DOM when the data changes (https://docs.angularjs.org/guide/databinding). Most of the time you never have to make the changes manually.
In doing so, you generally don't need to use the link function. You can have a controller (like in your example) or a directive with a controller (https://docs.angularjs.org/guide/directive).
Finally I just modified a little bit your controller and your template.
HTML
<div ng-app="editer" ng-controller="myCtrl" class="container">
<div class="parents">
<div ng-repeat="item in items" class="wrap" sibs>
<span ng-class="{ unclicked: !item.selected }">{{ item.name }}</span>
<button ng-click="selectItem(item)">click</button>
</div>
</div>
</div>
JS
function myCtrl($scope) {
$scope.items = [...];
$scope.selectItem = function (item) {
// reset all the items
$scope.items.forEach(function (i) {
i.selected = false;
});
// set the new selected item
item.selected = true;
}
}

Angular uibModal, Resolve, Unknown Provider

I am trying to expose a "generic" modal - using Angular's $uibModal - through a service. Here is the definition of that service:
angular.module('app').service('CustomModalService', ['$uibModal', function ($uibModal) {
var openCustomModal = function (size, title, message) {
var actionToPerformOnConfirm = action;
var modalInstance = $uibModal.open({
templateUrl : 'templates/CustomModal.html',
size: size,
resolve: {
title: title,
message: message
}
});
};
return {
openCustomModal: openCustomModal
};
}]);
Nothing too complicated, above. However, it is not working. If I remove the resolve property from the object, the service works; however, if I include the resolve property, I get the Unknown Provider error originating from that property.
The documentation for the resolve property reads:
(Type: Object) - Members that will be resolved and passed to the
controller as locals; it is equivalent of the resolve property in the
router.
The objective is to be able to provide a template for the modal that utilizes these properties in its DOM, e.g. :
<div ng-controller="CustomModalController">
<div class="modal-header">
<h3 class="modal-title">{{title}}</h3>
</div>
<div class="modal-body">
{{message}}
</div>
<div class="modal-footer">
<button class="ad-button ad-blue" type="button" ng-click="confirmAction()"></button>
<button class="ad-button ad-blue" type="button" ng-click="cancelAction()"></button>
</div>
</div>
What am I missing that is causing this error to be thrown?
You have two problems:
You need to define the controller in your modal config
Your resolve object needs to be a map of string: function, where string is the name of the dependency that will be injected into your modal's controller, and function is a factory function that will be used to provide that dependency when the controller is instantiated.
Working example: JSFiddle
JavaScript
angular.module('myApp', ['ui.bootstrap'])
.controller('MyModalController', MyModalController)
.directive('modalTrigger', modalTriggerDirective)
.factory('$myModal', myModalFactory)
;
function MyModalController($uibModalInstance, items) {
var vm = this;
vm.content = items;
vm.confirm = $uibModalInstance.close;
vm.cancel = $uibModalInstance.dismiss;
};
function modalTriggerDirective($myModal) {
function postLink(scope, iElement, iAttrs) {
function onClick() {
var size = scope.$eval(iAttrs.size) || 'lg'; // default to large size
var title = scope.$eval(iAttrs.title) || 'Default Title';
var message = scope.$eval(iAttrs.message) || 'Default Message';
$myModal.open(size, title, message);
}
iElement.on('click', onClick);
scope.$on('$destroy', function() {
iElement.off('click', onClick);
});
}
return {
link: postLink
};
}
function myModalFactory($uibModal) {
var open = function (size, title, message) {
return $uibModal.open({
controller: 'MyModalController',
controllerAs: 'vm',
templateUrl : 'templates/CustomModal.html',
size: size,
resolve: {
items: function() {
return {
title: title,
message: message
};
}
}
});
};
return {
open: open
};
}
HTML
<script type="text/ng-template" id="templates/CustomModal.html">
<div class="modal-header">
<h3 class="modal-title">{{vm.content.title}}</h3>
</div>
<div class="modal-body">
{{vm.content.message}}
</div>
<div class="modal-footer">
<button class="ad-button ad-blue" type="button" ng-click="vm.confirm()">
confirm
</button>
<button class="ad-button ad-blue" type="button" ng-click="vm.cancel()">
cancel
</button>
</div>
</script>
<button modal-trigger size="'sm'" title="'Hello World!'" message="'This is a test'">
Click Me
</button>

AngularJS Custom Filter getting invoked multiple times with one change

Following are my questions on the code described below -
On page load, my below custom sort filter is getting called multiple times. why?
On selecting a select option (from directive), I want to trigger filter based on selected value (using two way binding). Here again filter is being called thrice. Why?
Can some one point me to answers? I know that Angular does a Dirty Check by comparing old and new digest, but why multiple and 3rd invocation (as mentioned in the above questions).
I have below directive -
angular.module("customDirectives", [])
.directive("sortAll", function () {
return {
restrict: "E",
scope: {
columns: "=sortcolumns",
optionselected: "=selectedoption"
},
templateUrl: '../Views/Directives/SortAll.html',
controller: function ($scope) {
$scope.sortOptions = [];
var asc = 'Ascending';
var desc = 'Descending';
$scope.getSortOptions = function () {
angular.forEach($scope.columns, function (item) {
$scope.sortOptions.push({ name: item + '-' + asc, value: asc });
$scope.sortOptions.push({ name: item + '-' + desc, value: desc });
});
$scope.optionselected = $scope.sortOptions[1];
return $scope.sortOptions;
}
}
};
});
Directive HTML -
<select name="sortOptions" id="sortOptions" class="form-control width-20percent pull-right"
ng-options="option.name for option in sortOptions"
ng-init="getSortOptions()"
ng-model="optionselected"></select>
And below Filter -
angular.module("customFilters", [])
.filter("sort", function ($filter) {
return function (data, sortOption) {
console.log(sortOption);
if (angular.isArray(data) && angular.isObject(sortOption)) {
var options = sortOption["name"].split('-');
var xc = options[1] == 'Ascending' ? false : true;
return $filter("orderBy")(data, options[0], xc);
} else {
return [];
}
}
});
Now my Controller code -
angular.module("productStore")
.controller("ProductListCtrl", function ($scope, $filter) {
});
And the main controller which gives the data -
angular.module("productStore")
.constant("dataUrl", "http://localhost:57398/testdata/Products.json")
.constant("productColumns", ["name","price","description"])
.controller("MainCtrl", function ($scope, $http, dataUrl, productColumns) {
$scope.data = {};
$scope.productColumns = productColumns;
$http.get(dataUrl)
.success(function (products) {
$scope.data.products = products;
})
.error(function (error) {
$scope.data.error = error;
})
});
HTML -
<body ng-controller="MainCtrl">
<div class="navbar navbar-inverse">
<a class="navbar-brand" href="#">PRODUCT STORE</a>
</div>
<div class="panel-default" ng-controller="ProductListCtrl" ng-hide="data.error" ng-cloak>
<div class="col-xs-8">
<sort-All sortcolumns="productColumns" selectedoption="selectedSortOption"></sort-All>
<div class="well padding-top-0px" ng-repeat="product in data.products | sort:selectedSortOption">
<h3>
<strong>{{product.name}}</strong>
<span class="pull-right label label-primary">
{{product.price | currency}}
</span>
</h3>
<span class="lead">{{product.description}}</span>
</div>
</div>
</div>
</body>
Sample Data -
[{
"category": "Watersports",
"description": "A boat for one person",
"name": "Kayak",
"price": 275,
"id": "05af70919155f8fc"
}]
On Page load -
On changing item in select -

Categories