Dynamically how to add directives? - angularjs

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().

Related

angularjs 1.7.2 - How to call a directive function from a controller?

How do you call a directive's function from within a controller?
I have seen a number of answers to this question, with the solution similar to the following:
https://lennilobel.wordpress.com/tag/calling-directive-from-controller/
I have implemented it as follows:
Directive
angular.module('myApp').directive('myDirective', ['$log',
function ($log) {
function link(scope, element, attributes) {
//function that a controller can call
scope.myFunc = function () {
//Do Something
};
//if the user has provided an accessor, attach the function
if (scope.accessor) {
scope.accessor.myFunc = scope.myFunc;
}
}
return {
link: link,
restrict: 'E',
templateUrl: 'app/myTemplate.html',
scope: {
accessor: '=',
}
}
}
Controller
angular.module('myApp').controller('myCtrl', ['$log', '$q',
function ($log, $q) {
var vm = this;
// empty object that the directive will attach myFunc to
vm.accessor = {};
$q
.all([
//Service Call
])
.then(function (results) {
//manipulate the results of the service call using the
//directives function
vm.callDirective();
},
function (err) {
$log.debug('$q.all err:', err);
});
vm.callDirective = function () {
if (vm.accessor.myFunc) {
vm.accessor.myFunc();
} else {
$log.error('umm, I don\'t have a function to call');
}
};
}
HTML Template
<div ng-controller="myCtrl">
<myDirective accessor="vm.accessor"></myDirective>
</div>
When I run the code, the directive indicates that accessor is undefined. As a result, accessor, in the controller, doesn't have myFunc defined.
How do I get myFunc to execute?
I am using angular 1.7.2
The controller is compiled (an instance created with the resulting scope) before the directive.
In this scenario, it compiles faster than the directive can set the accessor function.
A quick workaround for this is to set a delay before checking if there is an accessor present using $timeout service.
The key is having a Promise object passed to $q.all. This will cause a small delay and allowing for the directive to be compiled.
For real, you'll be having promises that do some network call passed to $q.all instead of doing this workaround with the $timeout service.
Here is how this will go:
index.html
<div ng-controller="myCtrl as vm">
<my-directive accessor="vm.accessor"></my-directive>
</div>
script.js
const myApp = angular.module('myApp', []);
myApp.directive('myDirective', ['$log', myDirective]);
myApp.controller('myCtrl', ['$scope', '$timeout', '$log', '$q', myCtrl]);
function myCtrl($scope, $timeout, $log, $q) {
const vm = $scope.vm;
// empty object that the directive will attach myFunc to
vm.accessor = {};
vm.callDirective = () => {
if (vm.accessor.myFunc) {
vm.accessor.myFunc();
} else {
$log.error("umm, I don't have a function to call");
}
};
const handleSuccess = results => {
//manipulate the results of the service call using the
//directives function
vm.callDirective();
};
const handleError = err => {
$log.debug('$q.all err:', err);
};
$q.all([
//Service Call
$timeout()
])
.then(handleSuccess)
.catch(handleError);
}
function myDirective($log) {
//function that a controller can call
const myFunc = function() {
//Do Something
$log.info('Calling assessor myFunc');
};
const link = function(scope) {
//if the user has provided an accessor, attach the function
if (scope.accessor) {
scope.accessor.myFunc = myFunc;
}
};
return {
link: link,
restrict: 'E',
templateUrl: 'mydirective.html',
scope: {
accessor: '='
}
};
}

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){
//...
}])

Passing scope data from a function to parent scope?

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) {

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);
}
};
});

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)
};
}
}

Resources