Unable to call Angular directive method - angularjs

I've got an Angular view thusly:
<div ng-include="'components/navbar/navbar.html'" class="ui centered grid" id="navbar" onload="setDropdown()"></div>
<div class="sixteen wide centered column full-height ui grid" style="margin-top:160px">
<!-- other stuff -->
<import-elements></import-elements>
</div>
This is controlled by UI-Router, which is assigning the controller, just FYI.
The controller for this view looks like this:
angular.module('pcfApp')
.controller('ImportElementsCtrl', function($scope, $http, $location, $stateParams, $timeout, Framework, OfficialFramework) {
$scope.loadOfficialFrameworks();
// other stuff here
});
The <import-elements> directive, looks like this:
angular.module('pcfApp').directive('importElements', function($state, $stateParams, $timeout, $window, Framework, OfficialFramework) {
var link = function(scope, el, attrs) {
scope.loadOfficialFrameworks = function() {
OfficialFramework.query(function(data) {
scope.officialFrameworks = data;
$(".ui.dropdown").dropdown({
onChange: function(value, text, $item) {
loadSections($item.attr("data-id"));
}
});
window.setTimeout(function() {
$(".ui.dropdown").dropdown('set selected', data[0]._id);
}, 0);
});
}
return {
link: link,
replace: true,
templateUrl: "app/importElements/components/import_elements_component.html"
}
});
I was under the impression that I'd be able to call the directive's loadOfficialFrameworks() method from my controller in this way (since I'm not specifying isolate scope), but I'm getting a method undefined error on the controller. What am I missing here?

The problem is that your controller function runs before your link function runs, so loadOfficialFrameworks is not available yet when you try to call it.
Try this:
angular.module('pcfApp')
.controller('ImportElementsCtrl', function($scope, $http, $location, $stateParams, $timeout, Framework, OfficialFramework) {
//this will fail because loadOfficialFrameworks doesn't exist yet.
//$scope.loadOfficialFrameworks();
//wait until the directive's link function adds loadOfficialFrameworks to $scope
var disconnectWatch = $scope.$watch('loadOfficialFrameworks', function (loadOfficialFrameworks) {
if (loadOfficialFrameworks !== undefined) {
disconnectWatch();
//execute the function now that we know it has finally been added to scope
$scope.loadOfficialFrameworks();
}
});
});
Here's a fiddle with this example in action: http://jsfiddle.net/81bcofgy/

The directive scope and controller scope are two differents object
you should use in CTRL
$scope.$broadcast('loadOfficialFrameworks_event');
//And in the directive
scope.$on('loadOfficialFrameworks_event', function(){
scope.loadOfficialFrameworks();
})

Related

how realize click event on directive?

Please help fix the script.
i make simply directive:
angular.module('menus', [])
.directive("epMenu", ['$timeout', '$state',
function ($timeout, $state) {
return {
link: function (scope, element, attrs) {
scope.goTo = function(link) {
console.log('go to', link);
};
var navigationElem = angular.element("<div class='ep-menu-navigation'><li ng-click='scope.goTo('main')'>qwerty</li></div>");
angular.element('body').append(navigationElem);
},
restrict: "EACM"
}
}]);
but it does not work. I need to when you press the button, start function goTo()
Now the console following error message:
VM436 angular.js:12520 Error: [$injector:unpr] Unknown provider: $stateProvider <- $state <- epMenuDirective
live example
$state is a provider that is registered in the ui.router module, so you have to lay that dependency:
angular.module('menus', ["ui.router"])
Also, if you are building the template dynamically in the link function, you have to compile it so that angular can apply its actions to it:
.directive("epMenu", ['$timeout', '$state', '$compile',
function ($timeout, $state, $compile) {
return {
link: function (scope, element, attrs) {
scope.goTo = function(link) {
console.log('go to', link);
};
var navigationElem = angular.element("<div class='ep-menu-navigation'><li ng-click='goTo('main')'>qwerty</li></div>");
$compile(navigationElem)(scope, function(cloned){
angular.element('body').append(cloned);
});
},
restrict: "EACM"
}
}]);
You also had some other errors in your code:
Missing the angular ui router script
Using scope.goTo instead of just goTo
Not escaping the quotes in the goTo function
Using jqLite, you cannot use angular.element('body'), instead use the $document service
Here is a working example.
First of all you should have ui.router injected as dependency to your module.
angular.module('menus',["ui.router"])
If you want to change the state when clicked on a link created by a directive, create a controller for the directive.
use the $state.go in the controller to redirect.
In my answer, I used a controller, which changes the state to menu when clicked on the link of directive.
Here is the code to achieve it,
(function() {
angular.module('menus')
directive('epMenu', function () {
var controller = ['$scope','$state', function ($scope,$state) {
$scope.goTo = function(link) {
$state.go(link)
};
}],
template = "<div class='ep-menu-navigation'><li ng-click='goTo('main')'>qwerty</li></div>";
return {
restrict: 'EA', //Default in 1.3+
controller: controller,
template: template
};
});
}());
The error inside your code is because it does not find dependency for $state. you have to add angular.module('menus', ["ui.router"]) because $state is registered inside ui-router.also you have to add proper js for ui-router. You can bind click event inside link function.
angular.module('menus', ["ui.router"]).directive("epMenu", ['$timeout', '$state',
function($timeout, $state) {
return {
link: function(scope, element, attrs) {
element.find('button').bind('click', function() {
console.log("click");
});
},
restrict: "EACM"
}
}
]);
angular.module('menus',
["ui.router"])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.3.1/angular-ui-router.min.js"></script>
<div ng-app="menus">
<ep-menu>
this is normal text
<button>
Click Me
</button>
</ep-menu>
</div>

How to call custom directive in scope function of another controller

I am new to AngularJS. Can anyone tell me with example that how to call a custom directive in a scope function of another controller.
For example I have a controller with a function as follows:
angularApp.controller('sample_Ctrl', function ($scope, $http, $timeout, $rootScope, $location) {
$scope.showReport = function(id) {
};
});
I created a customDirective as follows:
var showModalDirective = function ($timeout) {
return {
restrict: 'E',
templateUrl: '/Partials/template1.html',
};
};
angularApp.directive('showModal', showModalDirective);
So how to call this directive in the showReport function, and how can I pass id to template URL?
You can't call directive in controller. It should be a service.
Or you can use directive in view:
Directive:
var showModalDirective = function ($timeout) {
return {
restrict: 'E',
scope: {
modalId: '='
},
templateUrl: '/Partials/template1.html',
};
};
angularApp.directive('showModal', showModalDirective);
Controller:
angularApp.controller('sample_Ctrl', function ($scope, $http, $timeout, $rootScope, $location) {
$scope.showModal = false;
$scope.showReport = function(id) {
$scope.showModal = true;
$scope.modalId = id;
};
});
View:
<div ng-if="showModal">
<show-modal modal-id="modalId"></show-modal>
</div>
showModal directive is call when showModal variable is true.
To use your directive first u need to create HTML element with same name as your directive then provide data for that directive from your controller. Suppose there is a div in your html page.
<div show-modal> </div>
Then on this page your template1.html will call internally through directive and suppose there is some html element in template1.html like
code in your controller -
angularApp.controller('sample_Ctrl',function() {
$scope.firstName= "My First Name"
})

Call a function after the directive is loaded angularjs

I have a isolated directive, my controller looks like:
app.controller('ZacksController', ['$scope', '$http', 'ngDialog', '$timeout', function($scope, $http, ngDialog, $timeout){
//some code here
}]);
The HTML in the file looks like:
<div class="income-older-block" ng-show="selectedAge!=1">
<income-form></income-form>
</div>
I have a directive in related HTML folder,
app.directive("incomeForm", ['$timeout', function ($timeout) {
function link($scope) {
var hello = function () {
alert("1");
}
$timeout(hello, 0);
}
return {
restrict: 'E',
templateUrl: "app/zacks/your-income/income-form/income-form.html",
link: link,
controller: function ($scope, $timeout) {
$scope.$watch("zacks.AgeRet.value",function(newValue,OldValue,scope){
if (newValue){
alert((newValue));
}
});
}
}
}]);
I want to alert after I load the directive in the page, the alert appears at initial page itself. May I know what to do?
The actual problem is I'm using a rz-slider and want to initialize it once the directive is loaded to DOM., as its not taking the values provided. Is there any other approach for this problem?
<rzslider rz-slider-model="zacks.AgeRet.value" rz-slider-floor="zacks.AgeRet.floor" rz-slider-ceil="zacks.AgeRet.ceil"></rzslider>
In case if the timeout works, I'm planning to initialize something like this:
$timeout(function () {
$scope.$broadcast('rzSliderForceRender');
});
UPDATE
Added a controller in the directive, so now I'm able to get the value when I move the slider, but still not able to initialize the value of the slider.
I kind of found 2 solutions for these kind of problems, but still not serving the purpose of what I really need with rz-slider.
Solution 1
HTML:
<div ng-controller="ZacksController">
<after-render after-render="rzSliderForceRender">element</after-render>
</div>
JS:
var app = angular.module('myApp',[]);
app.directive('afterRender', ['$timeout', function ($timeout) {
var def = {
restrict: 'E',
link: function (scope, element, attrs) {
$timeout(scope.$eval(attrs.afterRender), 0); //Calling a scoped method
}
};
return def;
}]);
app.controller('ZacksController', ['$rootScope', '$scope', '$http', ' $timeout', function($rootScope, $scope, $http, $timeout){
$scope.rzSliderForceRender = function()
{
alert('Fired!');
};
}]);
Solution 2
HTML:
<div class="income-older-block" ng-show="selectedAge!=1">
<income-form></income-form>
</div>
JS:
app.controller('ZacksapiController', ['$rootScope', '$scope', '$http', 'ngDialog', '$timeout', 'dataService', function($rootScope, $scope, $http, ngDialog, $timeout, dataService){
$scope.$watch('selectedAge', function(newValue, oldValue) {
if (newValue !== oldValue) {
$timeout(function() {
alert("reCalcViewDimensions");
$scope.$broadcast('rzSliderForceRender'); // This is not working, but alert works.
}, 0);
}
});
Update:
Triggered the window resize event inside the $timeout, but this would be a temporary hack. Would be great if someone help me out with the real approach to solve the problem.
$timeout(function() {
window.dispatchEvent(new Event('resize'));
$scope.$broadcast('rzSliderForceRender');
}, 0);

how to inject locals to a controller dynamically?

We mostly write our controllers in this fashion:
app.controller('MyCtrl', function($scope, $location) {
//do something with $scope & $location
});
I am writing a directive, and I am faced with a scenario where I have to render a view based on a certain controller instance. The directive will be called as follows:
<my-directive src="srcVar" controller="myctrl"></my-directive>
This directive takes care of loading the template specified by srcVar and instancing the controller using the $controller service. So there are few lines in my code that does like:
$controller(çtrlExp, {'$scope' : scope.$new() });
The above works for simple cases where the controller has only one argument. For the above controller example, you can stuff work the following manner:
var locals = { '$scope' : $scope.$new(), '$location' : $injector.get('$location') };
$controller('MyCtrl', locals);
Now how to write it for a generic case, where the user's controller can include any number of injectable constructs like services, values, constants, etc, all of which are usually defined during module creation.
Ps: if you are looking for workable code...refer my github repo: https://github.com/deostroll/ngFrame/blob/master/app/scripts/viewutils.js . This is still a work in progress sort of library.
You only need to add the $scope in your locals the rest will be done automatically by Angular's DI.
Please have a look at the demo below or this jsfiddle.
angular.module('demoApp', [])
.controller('mainController', MainController)
.directive('myDirective', MyDirective);
function MainController($scope, $location) {
this.testTemplate = 'testTemplate.html';
console.log($location, $scope); // everything available here!
}
MainController.$inject = ['$scope', '$location'];
function MyDirective($compile) {
return {
restrict: 'E',
template: 'hello',
controller: function($scope, $attrs, $controller){
console.log($attrs.controller, $scope);
$controller($attrs.controller, {'$scope': new $scope.$new()});
},
compile: function(element, attrs, $scope) {
var template = angular.element(document.getElementById(attrs.src))
.html();
console.log(template);
element.replaceWith(template);
return function(scope, element, attrs) {
//template is compiled now and in DOM, add scope variable
scope.hello='Hello from directive';
};
}
};
}
MyDirective.$inject = ['$compile'];
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="demoApp">
<script type="text/ng-template" id="testTemplate.html">
<div>
<h1>Test template</h1>
{{hello}}
</div>
</script>
<my-directive src="testTemplate.html" controller="mainController"></my-directive>
</div>

$scope doesn't apply to compiled template in directive

I have the following directive for a popup with multiple templates:
app.directive('popup', function ($http, $rootScope, $templateCache, $compile, $parse, Popup) {
return {
link: function ($scope, $element, $attrs) {
$element.bind('click', function () {
var data = {
index: $attrs.index
}
$http.get('/partial/' + $attrs.popup + '.html', {cache: $templateCache}).success(function (tplContent) {
var mainElement = angular.element(document.getElementById('main'));
mainElement.append($compile(tplContent)($scope));
Popup.show(data);
});
});
}
}
});
I've pinned a visibility flag to $rootScope (to make the popup visible by css) along with index and item that came in data object. and the popup template looks like this:
<section class="popup" ng-class="{'show': popup.visibility}">
<h1>{{data[popup.index].title}}<h1>
<p>{{data[popup.index].message}}<p>
</section>
The directive loads the popup template and appends it to my mainElement, but doesn't apply scope to. so popup doesn't appear and no data is bound to it.
you need to pass $scope.$parent instead of $scope for compiling. because you are in the child $scope.

Resources