Angular wire up jQuery plugins after render - angularjs

I have created a directive, ngAfterRender, that I am trying to use to wire up fancybox:
Template
<div ng-after-render="wireUpFancyBox($event)" ng-bind-html="Content"></div>
Directive
.directive('ngAfterRender', ['$timeout', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
$timeout(function () {
scope.$eval(attrs.ngAfterRender, { $element: element });
});
}
};
}]);
Controller
$scope.wireUpFancyBox = function ($element) {
$element.find('a').fancybox($.extend(true, {}, fancyboxOptions, {
scrolling: 'auto',
type: 'inline'
}));
};
Unfortunately, the wireUpFancyBox() method is not called when the HTML Content binding changes.
What can I do here?

If you want wireUpFancyBox called every time the ng-bind changes then you want something like this
myApp.directive('ngAfterRender', ['$timeout', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.$watch(function () {
return scope.$eval(attrs.ngBindHtml);
},
function (value) {
$timeout(function () {
scope.$eval(attrs.ngAfterRender, { $element: element });
});
});
}
};
}]);
Here we put a watch on the evaluated ng-bind-html expression and then fire off your eval on a change.
fiddle

Related

How to adapt directive auto-focus to auto-focus="true"/"false"?

I'm not much in Angularjs and use the directive that put focus on particular element. It looks as following:
appModule.directive('autoFocus', function ($timeout) {
return {
restrict: 'AC',
link: function (scope, element, attrs) {
$timeout(function () {
element[0].focus();
}, 0);
}
};
});
The usage looks as following:
<button auto-focus class="uui-button lime-green btn" ng-click="copyToClipboard()">
Copy
</button>
I'd like to rearrange directive above to have ability to write: auto-focus="true" or auto-focus="false".
UPDATE:
I've updated code as shown below, but it doesn't work that is the focus is always there regardless I write auto-focus="true" or auto-focus="false".
appModule.directive('autoFocus', function ($timeout) {
return {
restrict: 'AC',
link: function (scope, element, attrs) {
var hasFocus = attrs.autoFocus;
if (hasFocus) {
$timeout(function () {
element[0].focus();
}, 0);
}
}
};
});
You can set the property to true or false and access the value via attrs.autoFocus inside directive's link function
Edit:
appModule.directive('autoFocus', function ($timeout) {
return {
restrict: 'AC',
link: function (scope, element, attrs) {
var hasFocus = attrs.autoFocus;
if (hasFocus ==="true") {
$timeout(function () {
element[0].focus();
}, 0);
}
}
};
});

Access require controller in a directive controller

app.directive('mainCtrl', function () {
return {
controller: function () {
this.funcA = function(){}
}
};
});
app.directive('addProduct', function () {
return {
restrict: 'E',
require: '^mainCtrl',
link: function (scope, lElement, attrs, mainCtrl) {
mainCtrl.funcA()
}
};
});
I don't want to use the link method but the controller method.
Is there a way to get the mainCtrl in the controller method of the directive addProduct.
something like:
app.directive('addProduct', function () {
return {
restrict: 'E',
require: '^mainCtrl',
controller: function (scope, mainCtrl) {
mainCtrl.funcA()
}
};
});
You'd still need to use the link function because the controllers are injected there. What you could, however, is request your directive's own controller and then set the other required controller as its property:
app.directive('addProduct', function () {
return {
restrict: 'E',
require: ['addProduct','^mainCtrl'],
controller: function ($scope) {
// this.mainCtrl is still not set here
// this.mainCtrl.funcA(); // this will cause an error
// but typically it is invoked in response to some event or function call
$scope.doFuncA = function(){
this.mainCtrl.funcA();
}
},
link: function(scope, element, attrs, ctrls){
var me = ctrls[0], mainCtrl = ctrls[1];
me.mainCtrl = mainCtrl;
}
};
});
Since AngularJS 1.5, you can use the $onInit lifecycle hook of the controller. As written in the documentation of require, when defining require as an object and setting bindToController to true, the required controllers are added to the controller as properties after the controller has been constructed, but before the $onInit method is run. So the code would look like this:
app.directive('mainCtrl', function () {
return {
controller: function () {
this.funcA = function(){}
}
};
});
app.directive('addProduct', function () {
return {
restrict: 'E',
require: {
myParentController: '^mainCtrl'
},
bindToController: true,
controller: function ($scope) {
this.$onInit = function() {
this.myParentController.funcA();
};
}
};
});
Here is my solution:
app.directive('mainCtrl', function () {
return {
controllerAs: 'main',
controller: function () {
this.funcA = function(){}
}
};
});
app.directive('addProduct', function () {
return {
restrict: 'E',
require: '^mainCtrl',
controller: function ($scope) {
$scope.main.funcA();
}
};
});
Pass the controller to the scope on the link function then accessing the scope on controller. Like this:
app.directive('mainCtrl', function () {
return {
controller: function () {
this.funcA = function(){}
}
};
});
app.directive('addProduct', function () {
return {
restrict: 'E',
require: '^mainCtrl',
link: function (scope, lElement, attrs, mainCtrl) {
scope.ctrl=mainCtrl;
},controller:function($scope){
$scope.ctrl.funcA();
}
};
});

How to append a custom angular directive into another custom angular directive?

I have two custom angular directives and one appends the second repeatedly. The problem is that although the tag is appended, the template of the directive is not. When I manually put it in, it works.
See this jsfiddle: http://jsfiddle.net/HB7LU/5555/
Here is the code where the appending takes place:
myApp.directive('formList', function () {
return {
template: '<my-form></my-form>',
require:'^repeatableForm',
restrict: 'E',
link: function (scope, element, attrs, repeatableFormCtrl) {
scope.add = function () {
console.log("test");
element.append('appended <my-form></my-form>'); // apended<my-form></my-form> will appear but not the contents of <my-form>
};
}
};
});
You have to use $compile service to manually compile your my-form directive like this:
myApp.directive('formList', function ($compile) {
return {
template: '<my-form></my-form>',
require:'^repeatableForm',
restrict: 'E',
link: function (scope, element, attrs, repeatableFormCtrl) {
scope.add = function () {
console.log("test");
var newForm = $compile('<span>appended </span><my-form></my-form>')(scope);
element.append(newForm);
};
}
};
});
Example JSFiddle: http://jsfiddle.net/9L3whcqc/

Angular JS passing parameter to scope function from directive

Assume that I have a directive like this
<div my-directive callback='doSomething(myArg)'></div>
angular.module('directives').directive('myDirective', function() {
return {
restrict: 'A',
scope: {
callback: '&'
},
link: function(scope, element, attrs) {
element.bind('someEvent', function() {
scope.callback({myArg: 'bla'});
});
}
}
});
If I want to pass a parameter to my scope's function, I have to do scope.callback({myArg: 'bla'}). I wonder if there's a way pass the argument without having to specify its name?
Use can use shared service in this case and inject it to directive:
angular.module("yourAppName", []).factory("mySharedService", function($rootScope){
var mySharedService = {};
mySharedService.values = {};
mySharedService.setValues = function(params){
mySharedService.values = params;
$rootScope.$broadcast('dataPassed');
}
return mySharedService;
});
And after inject it to directive. For example:
app.directive('myDirective', ['mySharedService', function(mySharedService){
return {
restrict: 'C',
link: function (scope, element, attrs) {
mySharedService.setValues(//some value//);
}
}
}]);
Then, you can get necessary value in controller.
function MyCtrl($scope, mySharedService) {
$scope.$on('dataPassed', function () {
$scope.newItems = mySharedService.values;
});
}

When i require ngModel controller how do I access a property of the model controller

I am using ng-repeat and setting a model with it similar to the following
<div ng-repeat="thing in things" ng-model="thing" my-directive>
{{thing.name}}
</div>
then in my directive it looks something like this
.directive("myDirective, function () {
return {
require: 'ngModel',
link: function(scope, lElement, attrs, model) {
console.log(model.name);// this gives me 'NAN'
}
}
})
My question is how can I access the values in the model? I tried model.$modelValue.name but that did not work.
If you want to bind in a scoped value then you can use the '=' in an isolated. This will appear on the scope of your directive. To read the ng-model directive, you can use =ngModel:
.directive("myDirective", function () {
return {
scope: {
model: '=ngModel'
}
link: function(scope) {
console.log(scope.model.name); // will log "thing"
}
}
});
.directive("myDirective", function () {
return {
require: 'ngModel',
link: function(scope, lElement, attrs, model) {
console.log(attrs.ngModel); // will log "thing"
}
}
})
If your directive does not have isolated or child scope then you can do this:
.directive('someDirective', function() {
return {
require: ['^ngModel'],
link: function(scope, element, attrs, ctrls) {
var ngModelCtrl = ctrls[0];
var someVal;
// you have to implement $render method before you can get $viewValue
ngModelCtrl.$render = function() {
someVal = ngModelCtrl.$viewValue;
};
// and to change ngModel use $setViewValue
// if doing it in event handler then scope needs to be applied
element.on('click', function() {
var val = 'something';
scope.$apply(function() {
ngModelCtrl.$setViewValue(val);
});
});
}
}
});

Resources