$scope is not defined in a directive controller - angularjs

I have a very basic demo app. The HTML:
<div ng-app="MyApp" ng-controller="Shapes">
<shapes></shapes>
</div>
and my js file:
app.directive('shape',
function () {
var directive = {};
directive.restrict = 'E';
directive.template = '<span>Shape Name: {{shape.Name}}, Shape Area: {{shape.Area}}</span>';
directive.scope = {
shape: "=name"
};
return directive;
});
app.directive('shapes',
function () {
var controller = [
'$scope', '$http', function ($scope, $http) {
$scope.height = 0, $scope.width = 0;
$http.get('/api/Values').then(function (respnse) {
$scope.shapes = respnse.data;
});
$scope.addShape = function () {
$http.post('api/Values', { Width: 1, Height: 1 }).then(function (res) { });
};
}
];
var directive = {};
directive.restrict = 'E';
directive.template = '<div ng-repeat="shape in shapes"><shape name="shape"></shape></div>' +
'<div>Height:<input type="text" ng-model="height"></input>, Width:<input type="text" ng-model="width"></input>' +
'<button ng-click="addShape()">Add Shape</button></div>';
directive.controller = controller;
return directive;
});
My problem - in the $scope.addShape function, $scope is not defined. Why? $http is defined and working well.

Related

AngularJs : Custom Directive Scope is Undefined

I want to put the value from the other controller to my custom directive , but the value in the watch function in the directive is coming undefined .Here is my code :
directive code :
function associates($templateCache,$compile) {
var directive = {};
directive.restrict = 'E';
directive.controller = 'associatesTypeaheadController';
directive.controllerAs = 'ctrlTA';
directive.template = $templateCache.get('typeahead.template.html');
directive.scope= {
"options": '='
};
directive.link=link;
return directive;
}
function link(scope, element, attrs) {
scope.$watch('options.reload', function(data){
console.log(data); //Still undefined
});
}
Controller :
employeeDetails.opts = {
"reload": false,
"refresh": false
};
employeeDetails.reload = function () {
employeeDetails.opts.reload = true;
console.log('there');
return employeeDetails.opts.reload;
};
employeeDetails.reload();
After lot's of struggle, i found the solution :
Directive To share between two controller:
directive.restrict = 'E';
directive.controller = 'associatesTypeaheadController';
directive.controllerAs = 'ctrlTA';
directive.template = $templateCache.get('typeahead.template.html');
directive.compile = function (element, attributes) {
var linkfunction = function ($scope) {
if ($scope.employeeDetails && $scope.employeeDetails.hint) {
$scope.hint = $scope.employeeDetails.hint;
}
if ($scope.ctrl && $scope.ctrl.hint) {
$scope.hint = $scope.ctrl.hint;
}
};
return linkfunction;
};
return directive;
}
Controller A:
employeeDetails.scope = $scope;
employeeDetails.hint = "Reports To";
Controller A HTML Scope :
<associates></associates>
Directive HTML :
<md-autocomplete
flex id="associate-typehead"
md-input-name='autocompleteField'
md-input-minlength='2' md-input-maxlength='50'
md-no-cache='ctrlTA.noCache'
md-selected-item='ctrlTA.selectedItem'
md-search-text='ctrlTA.searchText'
ng-model-options='{debounce : 1000}'
md-floating-label={{hint}}
md-items='item in ctrlTA.querySearch(ctrlTA.searchText)'
md-item-text='item.username' >
<md-item-template>
<span md-highlight-text='ctrlTA.searchText'>{{item.lastName + ', ' + item.firstName + ' (' + item.username + ')'}}</span>
</md-item-template>
</md-autocomplete>

isolate scope communication from directive to directive?

I am new to AngularJS and got confused with directive concept.
I am updating scope.markers in my second directive via $scope.delete function but changes are not reflecting on first directives,as I am using two way data binding isolate scope, so it should reflect. any solution will be a great help.
I have my first directive as:
app.directive('damageDiagram', function () {
return {
retrict: 'AE',
scope: {
imgsrc: '=', markers: '=', pointsrc: '=', dtype:'='
},
templateUrl: 'app/components/inspections/damage-diagram.html',
link: function (scope, element, attrs) {
}
}
});
and second directive as:
app.directive('damageMarker', function ($mdDialog,inspectionService,$timeout) {
return {
restrict: 'AE',
require: '?^damageDiagram',
scope: {
marker: '=',
pointsrc: '=',
dtype:'=',
markers: '='
},
template: '<img ng-src="{{pointsrc}}" />',
link: function (scope, elem, attr) {
elem.on("click",function(e){
showDialog();
function showDialog($event) {
var parentEl = angular.element(document.body);
$mdDialog.show ({
parent: parentEl,
targetEvent: $event,
template:
'<form name="clientForm" novalidate >'+
'<md-dialog aria-label="List dialog">' +
' <md-dialog-content>'+
'<md-input-container>'+
'<md-select ng-model="dtypeValue" class="dialog-close" placeholder="Select dtype">'+
'<md-option ng-repeat="opt in dtype">{{opt}}</md-option>'+
'</md-select>'+
'</md-input-container>'+
'<md-input-container class="md-block">'+
'<label>Comment</label>'+
'<input required name="name" ng-model="comment" class="dialog-close">'+
'<div ng-messages="clientForm.name.$error">'+
'<div ng-message="required">This is required.</div>'+
'</div>'+
'</md-input-container>'+
' </md-dialog-content>' +
' <div class="md-actions" layout="row" layout-align="end center">' +
' <md-button ng-click="closeDialog()" class="md-primary">' +
' Close' +
' </md-button>' +
'<md-button ng-disabled="clientForm.$invalid" ng-click = "save()" class="md-primary">'+
'Save'+
'</md-button>'+
'<md-button ng-disabled="clientForm.$invalid" ng-click = "delete()" class="md-primary">'+
'Delete'+
'</md-button>'+
' </div>' +
'</md-dialog>'+
'</form>',
controller: DialogController
});
function DialogController($scope, $mdDialog) {
$scope.dtypeValue = scope.dtype[scope.marker.dtype.toUpperCase()];
$scope.dtype = scope.dtype;
$scope.comment = scope.marker.comment;
$scope.marker = scope.marker;
$scope.closeDialog = function() {
$mdDialog.hide();
}
$scope.save = function(){
console.log($scope.marker.id);
console.log($scope.dtypeValue);
console.log($scope.comment);
var dataSend = {};
dataSend.id = $scope.marker.id;
dataSend.comment = $scope.comment;
for(var key in $scope.dtype) {
if($scope.dtype[key] == $scope.dtypeValue) {
dataSend.dtype = key;
}
}
inspectionService.updateDiagram(dataSend).then(function(response){
console.debug("response ; "+response);
$mdDialog.hide();
scope.marker.id = response.id;
scope.marker.comment = response.comment;
scope.marker.dtype = response.dtype;
});
}
$scope.delete = function(){
var dataSend = {};
dataSend.id = $scope.marker.id;
var param = {};
param.inspection=$scope.marker.inspection;
inspectionService.deleteDiagramMarker(dataSend).then(function(response){
inspectionService.getDiagram(param).then(function(response){
$timeout(function() {
scope.$apply(function(){
scope.markers = response.results;
})
},2000);
console.debug("response ; "+response);
$mdDialog.hide();
});
});
}
}
}
});
console.log(scope.marker.top, scope.marker.left, elem);
}
}
});
My html code for damage-diagram directive is as follows:
<damage-diagram imgsrc="imgsrc" pointsrc="pointsrc" dtype="dtype"
markers="inspection.damage_diagram">
</damage-diagram>
and my html code for damage-directive is as follow:
<div style="position:relative">
<img id="23467129" ng-src="{{imgsrc}}" style="position:relative" width="100%" />
<div ng-repeat="marker in markers"
marker="marker"
markers="markers"
dtype="dtype"
pointsrc="pointsrc"
damage-marker>
</div>
</div>
controller is as follows:
app.controller('InspectionDetailCtrl', ['$scope', 'inspectionService', '$stateParams', 'Restangular','$rootScope',
function ($scope, inspectionService, $stateParams, Restangular, $rootScope) {
$scope.updateDamageImage = {};
$scope.insp_id = $stateParams.inspId;
$scope.damageImagesShow = false;
$scope.comments = [];
$scope.types = [];
$scope.selectTypeDelete = false;
$scope.commentDelete = false;
$scope.selectTypeDeleteBefore = true;
$scope.commentDeleteBefore = true;
init($scope.insp_id);
console.log("Fetching details for scope", $scope.insp_id);
function init(insp_id)
{
inspectionService.inspections.customGET(insp_id, {type: 'full'})
.then(function (data) {
$scope.inspection = data;
}, function (err) {
$scope.inspection = null;
});
}
$scope.pointsrc="app/components/inspections/pointer.png";
$scope.dtype = {
'S': 'Scratch (minor)',
'DS': 'Deep Scratch',
'D': 'Dents',
'WD': 'Wheel Damage',
'CW': 'Cracked Window',
'FT': 'Flat Tire',
'BL': 'Broken (lights)'
};
}]);
First of all two way binding in directive doesn't works like that any change reflected in main controller can be seen in directive but not other way any change in directive won't be reflected in main controller.
But there is a solution you can create an object in main controller
var x={};
x.value='to be passed in directive'
then you use same variable in directive since only once instance of object is created so any change in any directive will be reflected everywhere.

Calling parent directive method from child directive through attrs

This is the scenario :
<export-team>
<ul>
<li>
<button buy-ticket="{{data}}" buy-callback="onBuyTicket()">buy</button>
</li>
<li>
<button buy-ticket="{{data}}" buy-callback="onBuyTicket()">buy</button>
</li>
</ul>
</export-team>
The buyTicket directive
(function() {
'use strict';
angular
.module('myApp')
.directive('buyTicket', buyTicket);
/** #ngInject */
function buyTicket($parse, ngDialog, authService, APPCONFIG, $rootScope, shareToken, contestsFactory, shareCurrentTicket, shareIdSession, shareSessionAAMS, $location) {
var vm = this;
var directive = {
restrict: 'A',
link : function(scope, element, attributes) {
var buyCompatible = attributes['buyCompatible'];
function addZero(i) {
if (i < 10) {
i = "0" + i;
}
return i;
}
var buyTicket = function(contest) {
var d = new Date();
var y = d.getFullYear();
var m = addZero(d.getMonth()+1);
var day = addZero(d.getDate());
var h = addZero(d.getHours());
var min = addZero(d.getMinutes());
var s = addZero(d.getSeconds());
var date = ''+y+m+day+h+min+s+'';
var transactionId = $rootScope.TRANSACTIONID;
var currentTOKEN = shareToken.get();
var data = {
idSessione:currentTOKEN, // ->TOKEN
userAgent:navigator.userAgent,
sessioneAAMS:contest.aams_session_id,
gameId:APPCONFIG.GAME_ID,
transactionId:transactionId,
dateTime:date,
buyIn:contest.buy_in
}
var dialogLoading = ngDialog.open({
closeByDocument : false,
closeByEscape : false,
showClose : false,
id : 'ft-modal-loading',
controller: ['$scope', function($scope){
$scope.bodyUrl = 'app/components/modals/body/loading.html';
$scope.title = 'Acquisto Ticket';
$scope.error = 'Il sistema sta procedendo all\'acquisto del ticket';
}]
});
contestsFactory.buyTicket(data).success(function(response){
dialogLoading.close();
if (response.esito == "0") {
if (!buyCompatible) {
shareCurrentTicket.set(response.ticketSogei);
shareSessionAAMS.set(contest.aams_session_id);
shareIdSession.set(contest.id_session);
$location.path('my-contests/'+contest.id_contest+'/'+contest.contest_status);
}
} else {
var message = response.descrizione;
var ids = ngDialog.getOpenDialogs();
var dialogError = ngDialog.open({
id : "ft-modal-error-2",
controller: ['$scope', function($scope){
$scope.bodyUrl = 'app/components/modals/body/error.html';
$scope.title = 'Errore';
$scope.error = message;
}]
});
}
})
.error(function(){
var dialogErrorNotEndled = ngDialog.close('ft-modal-loading');
ngDialog.open({
id : 'ft-modal-error',
controller: ['$scope', function($scope){
$scope.bodyUrl = 'app/components/modals/body/error.html';
$scope.title = 'Errore';
$scope.error = 'Il servizio non è attualmente disponibile';
}]
});
})
}
var openConfirmBuyTicket = function(contest) {
contest = JSON.parse(contest);
if (ngDialog.isOpen('ft-modal-contest-detail')) {
ngDialog.close('ft-modal-contest-detail');
};
if (!authService.isLogged()) {
ngDialog.open({
controller: ['$scope', function($scope){
$scope.bodyUrl = 'app/components/modals/body/not_logged.html';
$scope.title = 'Spiacenti';
$scope.error = 'Devi essere loggato per poter partecipare ad un contest';
}]
});
} else {
ngDialog.openConfirm({
controller: ['$scope', function($scope){
$scope.title = 'CONFERMA';
$scope.bodyUrl = 'app/components/modals/body/confirm_buy.html';
$scope.contest_name = contest.name_contest;
$scope.buy_in = contest.buy_in;
$scope.currency = APPCONFIG.CURRENCY_SYMBOL;
}],
}).then(function (confirm) {
buyTicket(contest);
}, function(reject) {
});
}
}
element.on('click', function(e){
var contest = attributes['buyTicket'];
openConfirmBuyTicket(contest);
})
}
};
return directive;
}
})();
The export directive
(function() {
'use strict';
angular
.module('myApp')
.directive('exportTeam', exportTeam);
/** #ngInject */
function exportTeam(contestsFactory, ngDialog, APPCONFIG, formatDateFactory) {
var vm = this;
var directive = {
restrict: 'AE',
transclude: true,
controller : function($scope) {
$scope.test = function() {
alert('hey');
}
},
link : function(scope, element, attributes) {
element.on('click', function(e){
var ticket = attributes['exportTeam'];
var id_session = attributes['idsession'];
scope.openExportTeamDialog(ticket, id_session, false);
})
scope.openExportTeamDialog = function(ticket, aams_session_id, afterSave) {
ngDialog.open({
id : 'ft-modal-exportTeam-detail',
className : 'ngdialog ngdialog-theme-default ft-dialog-exportTeam',
controller: ['$scope', 'contestsFactory', 'APPCONFIG', function($scope, contestsFactory, APPCONFIG){
$scope.title = "Aggiungi contest compatibili";
$scope.bodyUrl = 'app/components/modals/body/exportTeam.html';
$scope.contentLoading = true;
$scope.currency = APPCONFIG.CURRENCY_SYMBOL;
$scope.afterSave = afterSave;
$scope.CompatibleContests = [];
contestsFactory.getCompatibleContests(ticket).then(function(response){
angular.forEach(response.data[0], function(item, i){
var multientryOptions = [];
if(item.multientry > 1) {
item.isMultientry = false;
var n = parseInt(item.multientry);
for (i = 1; i <= n; i++) {
multientryOptions.push({
text : i+" team",
value : i
})
}
item.multientryOptions = multientryOptions;
item.multientryOptionSelected = multientryOptions[0];
}else{
item.isMultientry = true;
};
})
$scope.CompatibleContests = response.data[0];
$scope.contentLoading = false;
})
}]
});
}
scope.openExportTeamDialog('N3E94100A725F9QG', 'M3E921013C6DCFCT', false);
}
};
return directive;
}
})();
The buy-ticket directive makes an http call, on the response i want to be able to call the onBuyTicket method of the <export> directive.
I'm trying to understand the best way to do that.
Thanks everyone
This sample show to you how can call an function from your directive
In this sample you can see we just insert data in our directive, and then we handle the data and other action in the directive.
var app = angular.module("app", []);
app.controller("ctrl", function ($scope) {
$scope.dataFromYourController = [
{ name: "Concert Jennifer", value: 200 },
{ name: "007", value: 100 }
];
})
.directive("export", function () {
var template = "<div>" +
"<ul>" +
"<li ng-repeat=\"array in arrays\">" +
"<button ng-click=\"onBuyTicket()\">buy Ticket {{array.name}}</button><hr>" +
"</li>" +
"</ul>" +
"</div>";
return {
restrict: "E",
template: template,
scope: {
data: "="
},
link: function (scope, elem, attrs, ngModel) {
scope.arrays = scope.data;
scope.onBuyTicket = function () {
alert("calling function from directive");
}
}
};
})
<!doctype html>
<html ng-app="app" ng-controller="ctrl">
<head>
</head>
<body>
<h1>call action from your directive</h1>
<export data="dataFromYourController"></export>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</body>
</html>
#eesdil
var directive = {
restrict: 'AE',
transclude: true,
controller : function($scope) {
$scope.onBuyTicket = function() {
alert('hey');
}
}
}
Ho can I call that from the buy-ticket directive ?
Use the $parent
<button buy-ticket="{{data}}" buy-callback="$parent.onBuyTicket()">buy</button>
So the expor directive something like:
var directive = {
restrict: 'AE',
template: '<ng-tansclude></ng-transclude>',
transclude: true,
controller : function($scope) {
$scope.onBuyTicket = function() {
alert('hey');
}
}
}
UPDATED:
see the plunker:
https://plnkr.co/edit/fmyJ4oPLvTiI0TzO7h1b?p=preview
It really depends what you can call and what you cannot based on the scopes... here if you would remove the scope from the export directive would work without the $parent also as export would share the same scope as the parent (main view)
The best way to communicate events from a child directive to a parent directive (or controller) is to use the $emit method of the scope.
What you want to do is take an ng-click event, get additional information with an $http call, and $emit an event with the additional information to be used by your parent directive (or controller).
HTML
<button buy-ticket="data" ng-click="onBuyTicket()">buy</button>
The directive:
angular.module("myApp").directive("buyTicket", function($http) {
function linkFn(scope,elem,attrs) {
scope.onBuyTicket = function() {
var buyData = scope.$eval(attrs.buyTicket);
var url = someFunction(buyData);
$http.get(url).then (function (response) {
var httpData = response.data;
scope.$emit("buyTicket.click", buyData, httpData);
});
};
};
return {
restrict: "AE",
link: linkFn
};
});
In the parent controller:
$scope.$on("buyTicket.click", function (buyData, httpData) {
console.log(buyData);
console.log(httpData);
});
Notice that I used the $eval method to get the data from the variable named by the buy-ticket attribute.
When choosing a name for the event, I recommend including the name of the directive in the event's name. It makes it clear the source of the event and is unlikely to be duplicated elsewhere.

angularjs and calling function in the same object

I use two simple form with angularjs
<form ng-controller="ctrs.ctr1">
<input type="text" placeholder="Name" ng-model="name" />{{getName()}}
</form>
and
<form ng-controller="ctrs.ctr2">
<input type="text" placeholder="Name" ng-model="name" />{{getName()}}
</form>
and s small script for e.g showing name twice
var ctrs = {
nameTwoTimes: function(name) {
return name+" "+name;
},
ctr1: function($scope, $timeout) {
$scope.name = '';
$scope.getName = function() {
return $scope.name+" "+$scope.name;
};
},
ctr2: function($scope, $timeout) {
$scope.name = '';
$scope.getName = function() {
this.nameTwoTimes($scope.name);
};
}
};
How to use the function nameTwoTimes, that in calling it is not "undefined"? Thanks.
Like this:
var nameTwoTimes: function(name) {
return name+" "+name;
};
var ctrs = {
ctr1: function($scope, $timeout) {
$scope.name = '';
$scope.getName = function() {
return $scope.name+" "+$scope.name;
};
},
ctr2: function($scope, $timeout) {
$scope.name = '';
$scope.getName = function() {
nameTwoTimes($scope.name);
};
}
};

AngularJS Chart Directive - Data loaded in async service not updating chart

I am having one chart directive created, and I am bootstrpping the app after loading google api. In following code, a simple data table is working fine. But when I load data from server in async manner, chart is not being displayed.
Controller
'use strict';
myNetaInfoApp.controller('allCandidatesController', [
'$scope','allCandidates2009Svc', '$timeout',
function ($scope, allCandidates2009Svc, $timeout) {
$scope.data1 = {};
$scope.data1.dataTable = new google.visualization.DataTable();
$scope.data1.dataTable.addColumn("string", "Party");
$scope.data1.dataTable.addColumn("number", "qty");
$scope.data1.dataTable.title = "ASDF";
$timeout( function (oldval, newval) {
allCandidates2009Svc.GetPartyCriminalCount().then(function(netasParty) {
var i = 0;
for (var key in netasParty) {
$scope.data1.dataTable.addRow([key.toString(), netasParty[key]]);
i++;
if (i > 20) break;
}
});
});
$scope.dataAll = $scope.data1;
//sample data
$scope.data2 = {};
$scope.data2.dataTable = new google.visualization.DataTable();
$scope.data2.dataTable.addColumn("string", "Name");
$scope.data2.dataTable.addColumn("number", "Qty");
$scope.data2.dataTable.addRow(["Test", 1]);
$scope.data2.dataTable.addRow(["Test2", 2]);
$scope.data2.dataTable.addRow(["Test3", 3]);
}
]);
Service
'use strict';
myNetaInfoApp.factory('allCandidates2009Svc', ['$http', '$q',
function ($http, $q) {
var netas;
return {
GetPartyCriminalCount: function () {
var deferred = $q.defer();
$http.get('../../data/AllCandidates2009.json')
.then(function (res) {
netas = res;
if (netas) {
var finalObj = {};
_.each(netas.data, function(neta) {
finalObj[neta.pty] = finalObj[neta.pty] ? finalObj[neta.pty] + 1 : 1;
});
deferred.resolve(finalObj);
}
});
return deferred.promise;
}
};
}]);
Directive
"use strict";
var googleChart = googleChart || angular.module("googleChart", []);
googleChart.directive("googleChart", function () {
return {
restrict: "A",
link: function ($scope, $elem, $attr) {
var dt = $scope[$attr.ngModel].dataTable;
var options = {};
if ($scope[$attr.ngModel].title)
options.title = $scope[$attr.ngModel].title;
var googleChart = new google.visualization[$attr.googleChart]($elem[0]);
$scope.$watch($attr.ngModel, function (oldval, newval) {
googleChart.draw(dt, options);
});
}
};
});
HTML
<div ng-controller="allCandidatesController">
<div class="col-lg-6">
<h2>Parties and Candidates with Criminal Charges</h2>
<div google-chart="PieChart" ng-model="dataAll" class="bigGraph"></div>
<!--<p><a class="btn btn-primary" href="#" role="button">View details »</a></p>-->
</div>
<div class="col-lg-6">
<h2>Heading</h2>
<div google-chart="BarChart" ng-model="data2" class="bigGraph"></div>
</div>
</div>
I think you need to wrap your function body in allCandidates2009Svc factory with scope.$apply(). But the return deferred.resolve() will be outside scope.$apply().
function asyncGreet(name) {
var deferred = $q.defer();
setTimeout(function() {
// since this fn executes async in a future turn of the event loop, we need to wrap
// our code into an $apply call so that the model changes are properly observed.
scope.$apply(function() {
deferred.notify('About to greet ' + name + '.');
if (okToGreet(name)) {
deferred.resolve('Hello, ' + name + '!');
} else {
deferred.reject('Greeting ' + name + ' is not allowed.');
}
});
}, 1000);
return deferred.promise;
}
Read the docs here
http://docs.angularjs.org/api/ng.$q

Resources