I have a form directive that needs to communicate with a form-sections directive to tell it to change is layout. The change is triggered by a form-header directive.
<form>
<form-header></form-header>
<form-sections></form-sections>
</form>
I injected the form directive's controller in the form-header, so that the header can call the changeLayout function.
form directive:
compile: function(scope, element , attrs) {
element.find('form-sections').attr('current-view', 'tabs'); // initial layout
},
controller: function($scope, $element, $attrs) {
this.changeLayout = function(layout) {
$element.find('form-sections').attr('current-view', layout);
};
}
form-sections directive:
link: function (scope, element, attrs) {
attrs.$observe('currentView', function(currentView) {
console.log('observe:'currentView);
})
}
$observe is triggered only once, by the form directive's compile function, but not by it's controller. I tried it with an isolated scope in the form-section directive and use $watch, without any success..
Thanks as always!
Related
I have my directive and I want to change the controller and html of a small section of the page onclick of a submit button. Changeing the HTML is working but changing the controller is not.
The line: attrs.ngController = "wordlistsPageController";
is not working. Please let me know what I can do to dynamically set the controller since this does not work.
It is a little web app game, so I do not want to change the whole page, just the game section between 3 game pages.
Here is my directive:
myApp.directive('gamePanel', function ($compile) {
return {
restrict: 'E',
templateUrl: "templates/wordlistPage.ejs",
link : function(scope, elem, attrs) {
attrs.ngController = "wordlistsPageController";//NOT WORKING
scope.submitWordlist = function() {
//change html
elem.html('<ng-include src="\'templates/gameOptionsDialogPage.ejs\'"></ng-include>');
$compile(elem.contents())(scope);
//change controller
attrs.ngController = "optionsPageController";///NOT WORKING
};
scope.backToWordlist = function() {
elem.html('<ng-include src="\'templates/wordlistPage.ejs\'"></ng-include>');
$compile(elem.contents())(scope);
attrs.ngController = "wordlistsPageController";///NOT WORKING
};
}//end link
}
});//end directive
How to Dynamically Change a Directive Controller
To instantiate different controllers inside a directive linking function, use the $controller service.
angular.module("myApp").directive('gamePanel', function ($controller) {
return {
restrict: 'E',
template: "<p>Game Panel Template</p>",
//controller: "pageController as vm",
link : function(scope, elem, attrs, ctrl, transclude) {
var locals = { $scope: scope,
$element: elem,
$attrs: attrs,
$transclude, transclude
};
scope.vm = $controller("pageController", locals);
}//end link
}
});
The above example instantiates a controller and puts it on scope as vm.
Best practice
The injected locals should follow the conventions of the locals provided by the $compile service. For more information on $compile service injected locals, see AngularJS $compile service API Reference API -- controller.
Beware: memory leaks
When destroying scopes, all watchers created on those scopes should be de-registered. This can be done by invoking scope.$destroy().
When destroying controllers, all watchers created by the controller should be de-registered. This can be done by invoking the de-register function returned by each $watch.
How do I create a directive that injects a fixed position modal or a directly underneath the element that the directive is attached to? So in the directive I would want scope.$on to trigger injecting directly below the element.
Here's my code so far:
angular.module('mobileDashboardApp')
.directive('httpErrorMessage', function (HTTPErrors) {
return {
template: '<div></div>',
link: function(scope, element, attrs) {
console.log("hello");
scope.$on(HTTPErrors.badRequest, function (event, args) {
// Inject template directly below 'element' <-- part I'm not sure how to do
});
}
};
});
I have an angular directive (restrict to an element), its have an attribute to link to a function (using "&" attribute)
for example:
app.directive('myDirective', function () {
return {
restrict: 'E',
scope: {
myonclick: "&",
},
controller: 'myController',
template: '<button ng-click="clicked()">Click me!</button>'
}
});
i'm assigning a function to the attribute like this:
<my-directive myonclick="inScopeFunction()"></my-directive>
its work fine if the function is inside the angular controller scope.
but i cant assign it to a function outside the angular scope.
there is a working example at plunker
Thanks
Since inScopeFunction() is a global method, you can call it directly inside your directive. Register a click listener on the element inside directive and call inScopeFunction() in listener callback. Here is the updated plunk
link: function postLink (scope, element, attrs) {
element.on('click', function () {
window[attrs.myonclick]()
});
}
html:
<my-directive myonclick="outScopeFunction"></my-directive>
Your controller will have access to global objects so you can access it from there
$scope.inScopeFunction = outScopeFunction
To access it from your directive controller you could pass in a string and call the global object from there
<my-directive myonclick="'outScopeFunction'"></my-directive>
...
app.controller('myController', function($scope) {
$scope.clicked = function(){
window[$scope.myonclick()]()
}
});
How can I access a directive's ngModelController from another directive?
The scenario
I'm creating a type ahead widget, which is composed of a typeAhead directive and autoCompletePopUp directive.
AutoCompletePopUp directive will interact with the typeAhead using typeAhead's controller.
But I don't know how to call typeAhead's $setViewValue from autoCompletePopUp when an item is selected.
Why not just add a function to the controller for typeAhead that calls $setViewValue on itself. In the context of typeAhead's controller you should have access to the scope. You can put the ngModelController for typeAhead on the scope if needed. Something like this:
angular.module("myModule").directive("typeAhead", function() {
return {
require: "ngModel",
controller: function($scope) {
this.setValue = function(value) {
$scope.ngModelController.$setViewValue(value);
};
},
link: function(scope, element, attributes, ngModelController) {
scope.ngModelController = ngModelController;
},
};
});
angular.module("myModule").directive("typeAhead", function() {
return {
require: "typeAhead",
link: function(scope, element, attributes, typeAheadController) {
scope.someAction = function(value) {
typeAheadController.setValue(value);
};
},
};
});
(protect against minification and move controllers into separate objects / files as desired; done inline here for convenience)
I am trying to call a function in a controller, which is part of a custom angular directive, following is the code,
Method 1: (Doesn't work: Controller's function doesn't write to the console)
HTML:
<div ng-app="MyApp">
<my-directive callback-fn="ctrlFn(arg1)"></my-directive>
</div>
JS:
var app = angular.module('MyApp', []);
app.directive('myDirective', function() {
return {
restrict: 'E',
scope: { someCtrlFn: '&callbackFn' },
link: function(scope, element, attrs) {
scope.someCtrlFn({arg1: 22});
},
controller: function ($scope) {
$scope.ctrlFn = function(test) {
console.log(test);
}
}
}
});
When I remove the directive's controller from it and and create a new controller it works,
Method 2: (Works: Controller's function does write to the console)
HTML:
<div ng-app="MyApp" ng-controller="Ctrl">
<my-directive callback-fn="ctrlFn(arg1)"></my-directive>
</div>
JS:
var app = angular.module('MyApp', []);
app.directive('myDirective', function() {
return {
restrict: 'E',
scope: { someCtrlFn: '&callbackFn' },
link: function(scope, element, attrs) {
scope.someCtrlFn({arg1: 22});
}
}
});
app.controller('Ctrl', function ($scope) {
$scope.ctrlFn = function(test) {
console.log(test);
}
});
I would like know how to get the behavior of Method 2 in Method 1 i.e., to be able to call the directive's controller's function from directive's attribute.
Any help is greatly appreciated, Thank you!
In Method 1, you are creating an isolated scope and defining a scope value someCtrlFn that takes in a function from the parent scope that is using your directive. The function to use is specified by the attribute callbackFn.
The way directives work with these scope items is that they are expected to be assigned from things that are on the parent scope that is active when the directive is used. So, if you have a controller Ctrl as in your Method 2, then use the directive within that scope, your directive is trying to match the what you defined in the attribute to what is available on Ctrl's scope.
So, in your first example, it's looking for a function called ctrlFn on the parent scope, but there isn't one. It will not try to look for it on the directive's controller. This is why Method 2 works, because there is a parent scope where ctrlFn is defined, and the directive is able to properly invoke that expression.
The purpose of these scope attributes is to allow directives to bind to values or functions on a parent scope to facilitate communication. For example, to give the directive data that it will display or modify, or allow the parent to define a function the directive can invoke for a callback during an event or what have you. The parent scope cannot move into the directive's scope and force the directive's scope to use its own defined items (unless you set it up so your directive uses a default value or function if the attribute is omitted or whatever).
They are not used so a directive can define things on its scope that it uses internally. If these things are internal to the directive, you can simply add them to the scope during link or whatever is suitable.
Did you mean something like this?
var app = angular.module('MyApp', []);
app.directive('myDirective', function() {
return {
restrict: 'E',
scope: { },
link: function(scope, element, attrs) {
// defines ctrlFn that can be used later by this directive's template or controller
scope.ctrlFn = function(test) {
console.log(test);
}
// immediately invokes ctrlFn to log a message, just here to illustrate
scope.ctrlFn('Hello World!');
}
}
});
Achieved it using $rootScope instead of $scope within the directive's controller
HTML:
<div ng-app="MyApp">
<my-directive callback-fn="ctrlFn(arg1)"></my-directive>
</div>
JS:
<script>
var app = angular.module('MyApp', []);
app.directive('myDirective', function($rootScope) {
return {
restrict: 'E',
scope: { someCtrlFn: '&callbackFn' },
link: function(scope, element, attrs) {
scope.someCtrlFn({arg1: 22});
},
controller: function ($scope) {
$rootScope.ctrlFn = function(test) {
console.log(test);
}
}
}
});
</script>