Call a function after the directive is loaded angularjs - 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);

Related

ng-show along with ng-bind-html in an isolated scope - directive is not working

I have referred to the previously asked question here -> same kind of question
but the answers did not help.
This is my master HTML:
<!--ss.com header starts here-->
<ss-header show="showHeader"></ss-header>
<!--ss.com header ends here-->
The {{headerHTML}} doesn't show the HTML in the template below:
This is my directive template - ss_header.html:
<header data-ng-show="{{show}}">
<div class="ss-header" data-ng-bind-html="headerHTML|convertAsHtml"></div>
</header>{{headerHTML}}
<div data-ng-show="{{show}}">Sample text to show ng-show is working properly in this directive.</div>
This is my directive.js
smartPrintApp.directive("ssHeader", ['$compile', function($compile) {
return {
restrict: "E", //directive for element only
//replace: true, //replace the custom tag
scope:{
show:'=show'
},
link: function(scope, element, attrs) {
scope.$watch(attrs.unsecureBind, function(newval) {
element.html(newval);
$compile(element.contents())(scope);
});
},
templateUrl: 'common/header/ss_header.html',
}
}]);
And this is my controller:
var smartPrintApp = angular.module("smartPrintApp",['ngRoute','ngResource','ngCookies', 'ngAria', 'ngAnimate']);
smartPrintApp.filter("convertAsHtml", ['$sce', function($sce){ return $sce.trustAsHtml}]);
smartPrintApp.controller("dotComController",['$scope', '$resource', 'serviceInfo', 'fetchLocalData', 'fetchServiceData', '$sce', '$window', '$http', '$compile', '$interpolate', function($scope, $resource, $service, fetchLocalData, fetchServiceData, $sce, $window, $http, $compile, $interpolate) {
/* Get Data from Server */
var reqData = {
"key1":"value1",//variables to send if needed
"key2":"value2"//variables to send if needed
};
var generalServ = new fetchServiceData($service.api.SERV_RESP);
generalServ.save(reqData).$promise.then(function(response){
/* Site Layout Modifications */
$scope.headerHTML = $interpolate(response.siteLayout.headerHtmlContent)($scope);
$scope.showHeader = response.siteLayout.headerEnabled;
/* Site Layout Modifications */
},function(err){
console.log('error in fetching service data')
});
/* Get Data from Server */
}]);
Any help is appreciated.
It doesn't show because you're using isolated scope but haven't passed in the header html. So currently {{headerHTML}} in your directive template is undefined. Change your isolated scope to:
scope:{
show:'=show',
headerHTML: '=header'
},
And your HTML to:
<ss-header show="showHeader" header="headerHTML"></ss-header>

Unable to call Angular directive method

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

Angularjs directive require is not finding the parent directive controller

When I require a controller in a directive, I am getting error saying that, not able to find the controller.
Please see the code with the issue below.
http://plnkr.co/edit/NzmQPA?p=preview
Can someone please have a look at it?
Thanks
You should use a service to communicate between them. Exactly how/what you do depends on your exact needs (there's not enough info in your post).
Side note, I changed your click handler to an ng-click.
Here's an example:
http://plnkr.co/edit/I2TvvV?p=preview
<div search-result-filter></div>
<div search-result-header ng-click="doClick()"></div>
angular.module('mymodule', [])
.controller('mainCtrl', ['$scope',
function($scope) {
$scope.test = "main angular is working";
}
]).controller('searchResultFilterController', ['$scope', 'myService',
function($scope, myService) {
//do something with 'myService'
}
])
.directive('searchResultFilter', [
function() {
return {
replace: true,
controller: 'searchResultFilterController',
template: '<h1>this is the first directive</h1>'
};
}
])
.directive('searchResultHeader', ['myService',
function(myService) {
return {
replace: true,
template: '<button>clickme</button>',
link: function($scope, $elem, $attrs) {
$scope.doClick = function() {
myService.someFn();
};
}
};
}
])
.service('myService', function() {
this.someFn = function() {
alert('this is working');
};
});
You should use require when your directives are related: like an accordion and accordion items.
To communicate between scopes, you should try $on, $emit, $broadcast. In your case, you need to inject rootScope into your directive, and broadcast an event from rootScope:
.directive('searchResultHeader',
function($rootScope) { //inject rootScope
return {
replace: true,
template: '<button>clickme</button>',
link: function($scope, $elem, $attrs) {
$elem.on('click', function() {
$rootScope.$broadcast("someEvent"); //broadcast an event to all child scopes.
});
}
};
}
);
Any scopes interested in the event can subscribe to it using $on:
function($scope) {
$scope.$on("someEvent", function() {
alert('this is working');
});
}
Using events is a way to create decoupled systems.
DEMO

How to pass in templateUrl via scope variable in attribute

I'm trying to pass in the url for the template via a scope variable. The scope will not change so the template doesn't need to update based on it, but currently the scope variable is always undefined.
<div cell-item template="{{col.CellTemplate}}"></div>
Ideally the directive would be:
.directive("cellItem", ["$compile", '$http', '$templateCache', '$parse', function ($compile, $http, $templateCache, $parse) {
return {
scope: {
template: '#template'
},
templateUrl: template // or {{template}} - either way
};
}])
This doesn't work however. I've tried a lot of different permutations in accomplishing the same concept, and this seems the closest, however it still doesn't work.
.directive("cellItem", ["$compile", '$http', '$templateCache', '$parse', function ($compile, $http, $templateCache, $parse) {
return {
scope: {
template: '#template'
},
link: function (scope, element, attrs) {
var templateUrl = $parse(attrs.template)(scope);
$http.get(templateUrl, { cache: $templateCache }).success(function (tplContent) {
element.replaceWith($compile(tplContent)(scope));
});
}
};
}])
I've also tried using ng-include, but that also doesn't evaluate scope variables before compiling. The CellTemplate value is coming from a database call so is completely unknown before evaluation. Any suggestions for getting this working would be greatly appreciated!
Edit:
I'm using angular 1.0.8 and am not able to upgrade to a newer version.
You are not far off at all.
You don't need to use an isolated scope for the directive. You can pass the templateUrl like this:
<div cell-item template="col.CellTemplate"></div>
Then add a watch to detect when the template value changes:
.directive("cellItem", ["$compile", '$http', '$templateCache', '$parse', function ($compile, $http, $templateCache, $parse) {
return {
restrict: 'A',
link: function(scope , element, attrs) {
scope.$watch(attrs.template, function (value) {
if (value) {
loadTemplate(value);
}
});
function loadTemplate(template) {
$http.get(template, { cache: $templateCache })
.success(function(templateContent) {
element.replaceWith($compile(templateContent)(scope));
});
}
}
}
}]);
Here is a working Plunker: http://plnkr.co/edit/n20Sxq?p=preview
If you don't want to deal with the linking logic yourself, or you want the isolate scope, I think this is simpler:
.directive("cellItem", ["$compile", '$http', '$templateCache', '$parse', function ($compile, $http, $templateCache, $parse) {
return {
scope: {
template: '#template'
},
template: "<div ng-include='template'></div>"
};
}])
or:
template:"<ng-include src='template'></ng-include>"
It's an old post but I thought its useful if anyone lands in here for the answer.
You can try the templateUrl function as #caub mentioned in a comment. Same can also be used for components.
.directive("cellItem", ["$compile", '$http', '$templateCache', '$parse', function ($compile, $http, $templateCache, $parse) {
return {
templateUrl: function(element, attrs) {
return attrs.template || 'someDefaultFallback.html';
}
};
}]);
We don't need any of the injected dependencies here. Hope this helps someone.

AngularJS: Error: No controller: form

HTML:
<div ng-app="my-app" ng-controller="AppController">
<ng-form name="myform">
<gtux-el></gtux-el>
</ng-form>
</div>
JS
var app = angular.module('my-app', [], function () {
})
app.controller('AppController', function ($scope) {
})
app.directive('gtInputMsg', ['$compile', '$interpolate', '$log', function($compile, $interpolate, $log) {
function link($scope, element, attrs, ctrls) {
var modelCtrl = ctrls[0], formCtrl = ctrls[1], msgCtrl = ctrls[2];
element.on('click', function() {
console.log('gt-input-msg:click', element)
});
};
return {
require : ['ngModel', '^form', 'gtInputMsg'],
link : link
};
}]);
app.directive('gtuxTextfield', ['$compile', '$timeout', '$log', function($compile, $timeout, $log) {
return {
restrict : 'E',
template : '<input type="text" name="field" gt-input-msg ng-model="fieldvalue" />',
replace : true
};
}]);
app.directive('gtuxEl', ['$compile', '$timeout', '$log', function($compile, $timeout, $log) {
function link($scope, $element, attrs, ctrl) {
//here compile is used because in my use case different elements are used based on some option values. A service provides the template to be used based on a key
var $cr = $compile('<gtux-textfield></gtux-textfield>')($scope);
$($cr).appendTo($element);
}
return {
restrict : 'E',
link : link,
transclude : true
};
}]);
Error
Error: No controller: form
at Error (<anonymous>)
at getControllers (http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.js:4278:19)
at http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.js:4284:24
Demo: Fiddle
As far I can see there is a ng-form element at the top of the hierarchy which should provide the from controller to the gtInputMsg directive.
How can this be rectified?
You're compiling the <gtux-textfield> element without it being under the form in the DOM as you haven't appended it yet. Try:
var $cr = $('<gtux-textfield></gtux-textfield>').appendTo($element);
$compile($cr)($scope);
In your gtuxEl link function.
Demo: Fiddle

Resources