Passing scope data from a function to parent scope? - angularjs

In my controller I have a $http call which returns a json string which I then want to pass to a directive to be added to a map. The string is being passed from the controller to the directive fine but not from from the $http function within the controller to the directive.
wmm.controller('wapMapClr', ['$rootScope', '$scope', '$window', '$http', function ($rootScope, $scope, $window, $http) {
$scope.geobj = {};
$scope.geobj.geoprop = ""
// Search by postcode
// create a blank object to hold our form information
$scope.formData = {};
$scope.pcSearch = function () {
$scope.data = {};
$http.post('api/api.php', { postcode: $scope.formData } )
.success(function (result) {
$scope.geobj = {geoprop : result.json_string};
console.log($scope.geobj.geoprop);
Any help would really appreciated. Thanks

Promises are asynchronous, so you don't know when the promise returns, so it won't be immediately available for you
Your directive has a controller method, from where you can fire the $http call which you can access.
You can use $emit/$brodcast to listen to events passed from controller to your directive.
I am not sure what error you get, here is a fiddle with $timeout used which is async which works.
var myApp = angular.module('myApp',[]);
myApp.directive('passObject', function() {
return {
restrict: 'E',
scope: { obj: '=' },
template: '<div>Hello, {{obj.prop}}!</div>'
};
});
myApp.controller('MyCtrl', function ($scope, $timeout) {
$scope.obj = { prop: "world" };
$timeout(function(){
$scope.obj = { prop: "from timeout" };
},10000);
});
https://jsfiddle.net/jt6j82by/

Thanks Thalaivar. I modified the code you gave and it worked. See below:
wmm.controller('wapMapClr', ['$scope', '$window', '$http', function ($scope, $window, $http) {
$scope.geobj = {};
// Search by postcode
// create a blank object to hold our form information
$scope.formData = {};
$scope.pcSearch = function () {
$scope.data = {};
$http.post('api/api.php', { postcode: $scope.formData } )
.success(function (result) {
$scope.geobj = {geoprop : result.json_string};
Then in the directive...
wmm.directive('tchOlMap', function () {
var MAP_DOM_ELEMENT_ID = 'tchMap';
return {
restrict: 'E',
//BELOW IS THE LINE I CHANGED TO MAKE IT WORK!
scope: false,
replace: true,
template: '<div id="' + MAP_DOM_ELEMENT_ID + '" class="full-height"></div>',
link: function postLink(scope, element, attrs) {

Related

Angular directive load orders: Unknown Provider when used in Controller

I am trying to use a directive, click-anywhere-but-here, in my header HTML, using controller navCtrl. Angular is throwing error:
Unknown provider: clickAnywhereButHereProvider <-
I'm thinking this has to do with how I'm using gulp to concatenate the JS files. I checked the concatenated main.js files with all JS, and see that navCtrl is defined above the clickAnywhereButHere directive. Not sure if this matters at all since the controller isn't using the directive at all, only the header.html file.
<header ng-controller="navCtrl">
<a click-anywhere-but-here="clickedSomewhereElse()" ng-click="clickedHere()">
<li>study</li>
</a>
</header>
How can I force the header to wait until clickAnywhereButHere directive is loaded before complaining?
Edit: Code:
navCtrl.js: I've gutted out a lot of the unrelated code
angular
.module('DDE')
.controller('navCtrl', ['$rootScope', '$location', '$scope', 'Modal', 'Auth', '$window', '$state', 'deviceDetector',
function($rootScope, $location, $scope, Modal, Auth, $window, $state, deviceDetector) {
$scope.clicked = '';
$scope.clickedHere = function(){
$scope.clicked = 'stop that';
console.log('clicked on element');
};
$scope.clickedSomewhereElse = function(){
console.log('clicked elsewhere');
$scope.clicked = 'thanks';
};
$scope.headings = [
{page: 'contact', route: '#/contact'}
];
}
]);
clickAnywhereButHere.js directive:
angular.module('DDE')
.directive('clickAnywhereButHere', function($document, clickAnywhereButHereService){
return {
restrict: 'A',
link: function(scope, elem, attr, ctrl) {
var handler = function(e) {
e.stopPropagation();
};
elem.on('click', handler);
scope.$on('$destroy', function(){
elem.off('click', handler);
});
clickAnywhereButHereService(scope, attr.clickAnywhereButHere);
}
};
});
clickAnywhereButHereService.js Service:
angular.module('DDE')
.factory('clickAnywhereButHereService', function($document){
var tracker = [];
return function($scope, expr) {
var i, t, len;
for(i = 0, len = tracker.length; i < len; i++) {
t = tracker[i];
if(t.expr === expr && t.scope === $scope) {
return t;
}
}
var handler = function() {
$scope.$apply(expr);
};
$document.on('click', handler);
// IMPORTANT! Tear down this event handler when the scope is destroyed.
$scope.$on('$destroy', function(){
$document.off('click', handler);
});
t = { scope: $scope, expr: expr };
tracker.push(t);
return t;
};
});
Both the directive and service are present in my min file:
You need to take into account the fact that your JS is minified.
So change this
.directive('clickAnywhereButHere', function($document, clickAnywhereButHereService){
to this
.directive('clickAnywhereButHere',
['$document', 'clickAnywhereButHereService',
function($document, clickAnywhereButHereService){
//...
}])

Using AngularJS service to update scope in other controller from other module

I am a newbie for AngularJS so maybe I am looking at this the wrong way. If so, please point me in the right direction.
Basically I want to update some DOM elements that reside in another controller in another module.
I am trying to send data through a service but it seems that it is not updated on the destination scope.
var mainModule = angular.module('main', []);
var appModule = angular.module('app', ['main']);
appModule.controller("appCtrl", function ($scope, $routeParams, mainService) {
$scope.mainService = mainService;
var initialize = function () {
$scope.mainService.currentID = $routeParams.productId;
}
initialize();
});
mainModule.factory('mainService', function () {
var mainService = { currentID: 0 };
return mainService
});
mainModule.controller('mainCtrl', ['$scope', 'mainService', function ($scope, mainService) {
$scope.mainService = mainService;
$scope.function1Url = "function1/" + $scope.mainService.currentID;
$scope.function2Url = "function2/" + $scope.mainService.currentID;
//CurrentID is always 0!!
}]);
I expect that when calling the initialize() function in the appCtrl, it will see the currentID param in the service which is also used by the mainCtrl.
For updating controller using service, I strongly recommend you to use $rootScope.$broadcast and $rootScope.$on. Here is an example of how you can do it, and link to a blog:
$rootScope.$broadcast('myCustomEvent', {
someProp: 'Sending you an Object!' // send whatever you want
});
// listen for the event in the relevant $scope
$rootScope.$on('myCustomEvent', function (event, data) {
console.log(data); // 'Data to send'
});
http://toddmotto.com/all-about-angulars-emit-broadcast-on-publish-subscribing/
Here is your working solution:
var mainModule = angular.module('main', []);
var productModule = angular.module('product', ['main']);
productModule.service('mainService', ['$rootScope', '$timeout', function ($rootScope, $timeout) {
this.method1 = function () {
alert('broadcast');
$rootScope.$broadcast('myCustomEvent', {
newValue: 'This is an updated value!'
});
}
}]);
productModule.controller('mainCtrl', ['$scope', '$rootScope', function ($scope, $rootScope){
$scope.myValue = 'Initial value';
$rootScope.$on('myCustomEvent', function (event, data) {
$scope.myValue = data.newValue;
alert('received broadcast');
});
}]);
productModule.controller("productCtrl", function ($scope, mainService) {
$scope.mainService = mainService;
$scope.clickMe = 'Click to send broadcast';
$scope.callService = function () {
$scope.clickMe = 'Broadcast send!';
$scope.mainService.method1();
}
});
And HTML:
<body ng-app='main'>
<div ng-controller="mainCtrl"><b>My value:</b>{{myValue}}</div>
<div id="product" ng-controller="productCtrl">
<button ng-click="callService()">{{clickMe}}</button>
</div>
<script type="text/javascript">angular.bootstrap(document.getElementById("product"), ['product']);</script>
</body>
You have a couple different methods of doing this.
I agree with uksz, you should use broadcast/emit to let other scopes know of the change, let them handle as needed.
Broadcast goes to all child scopes of the element
$scope.$broadcast("Message Name", "payload, this can be an object");
Emit goes to all parent scopes of this element
$scope.$emit("message name", "payload, this can be an object");
Other option is you can also require the other controller
appModule.directive('myPane', function() {
return {
require: '^myTabs',
scope: {},
link: function(scope, element, attrs, tabsCtrl) {
tabsCtrl.addPane(scope);
}
};
});
Lastly you can include a function on the scope so you can let the parent scope know what's going on
appModule.directive('myPane', function() {
return {
scope: {
doSomething: '&something'
},
link: function(scope, element, attrs) {
$scope.doSomething(test);
}
};
});

Dynamically how to add directives?

I am trying to create dynamic directives. but i am getting error. any one correct me please?
here is my code :
var data = [{completed:40}, {completed:20}, {completed:55}, {completed:69}, {completed:71}]
var newApp = angular.module('myApp', []);
newApp
.controller('home', ['$scope', function ($scope, $compile) {
$scope.data = data;
for(var i=0; i < $scope.data.length; i++) {
$('.content').append($compile("<title></title>")(scope));
$scope.$apply();
}
}]);
newApp
.directive('title', function ($compile, $http) {
return {
restrict:'E',
replace : true,
template : '<h2 class="que t3">QQQ</h2>',
link : function () {
console.log('link called');
}
}
});
Live Demo
Just missing a few things: http://jsfiddle.net/8vyduzam/2/
var data = [{completed:40}, {completed:20}, {completed:55}, {completed:69}, {completed:71}]
var newApp = angular.module('myApp', []);
newApp
.controller('home', ['$scope', '$compile', function ($scope, $compile) {
$scope.data = data;
for(var i=0; i < $scope.data.length; i++) {
$('.content').append($compile("<title></title>")($scope));
}
}]);
newApp
.directive('title', function ($compile, $http) {
return {
restrict:'E',
replace : true,
template : '<h2 class="que t3">QQQ</h2>',
link : function () {
console.log('link called');
}
}
});
There were three things
In the function, the second parameter accepts an array that lists all the services to inject into the last element of the array, the controller function. So you needed to add '$compile' so that it gets passed in.
You were also using scope when actually compiling the title directive instead of $scope.
During the controller function, it's already in a $digest loop, so you can also remove the extra $apply().

how to pass scope variables from controller to directive

I want to pass the scope variables defined in the 'MapCtrl' to the 'mapCanvas' directive. As the code stands the console.log returns undefined. Does anyone know how I can pass the variables from my controller to my directive? Thanks.
angular.module('Ski').controller('MapCtrl', function($scope, $http) {
'use strict';
$http.get('https://quiet-journey-8066.herokuapp.com/mountains/5').success(function(response) {
console.log(response);
$scope.mountain = response.name;
$scope.lat = response.latitude;
$scope.lng = response.longitude;
});
});
angular.module('Ski').directive('mapCanvas', function() {
return {
link: function(scope, element) {
console.log(scope.lat)
console.log(scope.lng)
console.log(scope.mountain)
};
}
}

How to use controller array notation when a controller is "created on the fly"?

I know how to use Angular's array notation for controllers when these are created "within an app", like this:
angular.module('appName').controller('controllerName', ['$scope', '$http', function ($scope, $http) {
}]);
But what if I have a controller that is used "on the fly"?
I have a directive that creates a bootstrap-ui modal controlled by a controller created "in the moment", this is the code:
angular.module('appName').directive('ngConfirmClick', ['$modal', function($modal) {
// Controller "on the fly"
var modalController = function($scope, $modalInstance) {
$scope.ok = function() {
$modalInstance.close();
};
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
};
return {
restrict: 'A',
scope: {
ngConfirmClick:"&"
},
link: function(scope, element, attrs) {
element.bind('click', function() {
var message = attrs.ngConfirmMessage || "Really?";
var modalHtml = '<div class="modal-header"><h3 class="modal-title">Confirm</h3></div>'
+ '<div class="modal-body">' + message + '</div>'
+ '<div class="modal-footer"><button class="btn btn-default" ng-click="ok()">OK</button>'
+ '<button class="btn btn-default" ng-click="cancel()">Cancel</button></div>';
var modalInstance = $modal.open({
template: modalHtml,
controller: modalController, // Controller reference
backdrop: false,
});
modalInstance.result.then(function() {
scope.ngConfirmClick();
}, function() {
});
});
}
}
}]);
Question is... how do I use array notation in here?
// Controller "on the fly"
var modalController = function($scope, $modalInstance) {
...
};
I know I could register the controller as I usually do with reusable controllers (see my first block of code)... but: should I? is it the only way?
This question can be abstracted and be helpful to others if stated as this:
If a directive needs to create/use a controller and this controller is used only by this directive and nobody else... how should the controller be created? within the app? within what?
You can add an $inject property...
// Controller "on the fly"
var modalController = function($scope, $modalInstance) {
...
};
modalController.$inject = ['$scope', '$modalInstance'];
What you're doing is already fine, you just need to assign modalController using the array notation. You don't need to add it as a reusable controller in the app since it's only used inside the directive.
JAVASCRIPT
var modalController = ['$scope', '$modalInstance', function($scope, $modalInstance) {
$scope.ok = function() {
$modalInstance.close();
};
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
}];
Alternatively, you can also add the dependencies using the $inject property.
var modalController = function($scope, $modalInstance) {
$scope.ok = function() {
$modalInstance.close();
};
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
};
modalController.$inject = ['$scope', '$modalInstance'];

Resources