I open a dialog with mdDialog when checking a checkbox (in the example checkbox 1).
Within this dialog there are several input fields, which should be connected via ng-modell to a controller - but it seems, that is not the scope used by the main controller (in the example myCtrl).
How can I use the same scope in the dialog and myCtrl? I tried locals to access the parent scope, but this didn't work.
Here is the plunker:
[https://plnkr.co/edit/9biRK5oskpQRhRWyeHWd](https://plnkr.co/edit/9biRK5oskpQRhRWyeHWd)
just pass the $scope to $mdDialog.show
$mdDialog.show({
scope: $scope,
controller: function () {
// ...
}
});
Remove the loacals. It can use myCtrls scope.
<md-radio-group ng-model="dialog_radio1">
Then in the cancel function I was able to log the selected radio value from myCtrls' scope.
$scope.cancel = function () {
$mdDialog.cancel();
console.log($scope.dialog_radio1);
};
Related
I'm trying to figure out how to make a child directive communicate with it's parent directive
I basically have this html markup
<myPanel>
<myData dataId={{dataId}}></myData>
</myPanel>
In the myData directive, if there is no data available, I want to hide the myPanel.
In the myData directive controller I've tried
$scope.$emit('HideParent');
And in the myPanel controller I've tried
$scope.$on('HideParent', function () { $scope.hide = true; });
And also
$scope.$watch('HideParent', function () { if (value) { $scope.hide = true; }});
In either situation, the myPanel directive isn't receiving the $emit
You may create controller in myPanel directive.
Then require this controller in myData directive. And when child directive has no data, call controller method to hide parent.
For example in you parent (myPanel) directive:
controller: function($scope, $element){
$scope.show = true;
this.hidePanel = function(){
$scope.show = false;
}
}
In myData directive require this controller:
require:'^myPanel'
And then, call controller function when you need
if (!scope.data){
myPanelCtrl.hidePanel();
}
Look this Plunker example
The answers above where the parent directive's controller is required is a great one if there truly is a dependency. I had a similar problem to solve, but wanted to use the child directive within multiple parent directives or even without a parent directive, so here's what I did.
Your HTML.
<myPanel>
<myData dataId={{dataId}}></myData>
</myPanel>
In your directive simply watch for changes to the attribute.
controller: function ($scope, $element, $attrs) {
$attrs.$observe('dataId', function(dataId) { $scope.hide = true; } );
}
It's explicit and simple without forcing a dependency relationship where one may not exist. Hope this helps some people.
So, we have a directive grid, which exposes directive controller to the angular controller, so that controller can call functions on directive (just like a form controller).
Now, in my angular controller every thing works as long as I access the directive controller from a callback action eg. some $scope.xx which are called on some event like click or any thing.
But when i try to access the directive controller at controller initialization time, the directive controller is undefined, that means, directives link function is not called yet.
Here's some code
function controller() {
$scope.init = function() {
$scope.grid.search(xx)
}
$scope.init() // this will fail, because $scope.grid is undefined.
$scope.onClick = function() {
$scope.grid.search(xx) // this will work
}
}
is there any other way, other then watching the $scope.grid, to have the $scope.grid.search called on controller initialization
You can just broadcast event from link function in your directive.
<div ng-controller="MyCtrl">
<div abc></div>
</div>
myApp.directive('abc', function($rootScope) {
return {
restrict: "EA",
link: function(scope, element, attrs){
$rootScope.$broadcast("initialize");
}
}
});
function MyCtrl($scope) {
$scope.$on("initialize", function(){
alert("Link function has been initialized!");
});
}
I've created JSFiddle for you.
The problem is that parent controllers are always called before child controllers.
If you want to run code after your child directive is initialized, you can create a directive and put the initialization code inside of its link function.
This works because parent link functions are always called after child link (and controller) functions.
What is the best way to pass a function to an angular ui bootstrap modal dialog? I created a method in my modal controller that calls $scope.$parent.myMethod() like so:
$scope.isChosen = function(concept) {
return $scope.$parent.isChosen(concept);
};
This works, but I would rather pass the function to the modal in a similar way to how functions are passed to directives. I've tried to use the modal "resolve" parameter to do this but without success. Is it possible to resolve a function for a modal, and if so, what is the syntax? If not possible, is there any other way to do this other than accessing the parent scope?
Edit: Here is a plunk attempting to pass a method to a modal, it is a little simplified but represents what I'm trying to do: http://plnkr.co/edit/eCjbZP
When you are defining your modal , you must resolve like this :
// here is the controller where you want to trigger the modal to open
$scope.openModal = function () {
// somewhere in your html , you may click on a button to envoke openModal()
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: ModalInstanceCtrl,
size: size,
resolve: {
isChosen: function () {
return $scope.isChosen;
}
}
});
};
And later on , in your modalCtr you can inject isChosen like this :
app.controller('modalCtrl',function($scope,isChosen){
// now you can use your isChosen function however you want
});
If a directive has an isolated scope, will the controller field be taken into account ?
If yes, how does exactly the directive access this controller ?
Yes, the isolated scope has nothing to do with the controller. Your problem is more of how to work with the controller (it use doesn't change in regard of the scope type).
A controller is useful when you want a directive to be required. If you have a directive called menu and another directive called menu-item and you want for example register all your menu-item in the menu directive, you create a controller.
When your menu-item does a require: 'menu' what it requires is the menu controller, not the directive itself.
Then you can have a directive like:
angular.module('app').directive('menu', function() {
return {
scope: {},
controller: function($scope) {
$scope.foo = "foo";
this.register = function(scope) {
// register child here
};
}
});
$scope.foo can be accessed by menu template, but this.register can't.
When you require menu in your menu-item you can't access $scope.foo but you can access this.register.
TL;DR; Scope type and having a controller are not related.
Example: http://plnkr.co/edit/fzbSnhxN9rp4Ct5kvB9i?p=preview
I've created a directive to wrap a jQuery plugin, and I pass a config object for the plugin from the controller to the directive. (works)
In the config object is a callback that I want to call on an event. (works)
In the callback, I want to modify a property on the controller's $scope, which does not work. Angular does not recognize that the property has changed for some reason, which leads me to believe that the $scope in the callback is different than the controller's $scope. My problem is I just don't why.
Can anybody point me in the right direction?
Click here for Fiddle
app.js
var app = angular.module('app', [])
.directive('datepicker', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
// Uncommenting the line below causes
// the "date changed!" text to appear,
// as I expect it would.
// scope.dateChanged = true;
var dateInput = angular.element('.datepicker')
dateInput.datepicker(scope.datepickerOpts);
// The datepicker fires a changeDate event
// when a date is chosen. I want to execute the
// callback defined in a controller.
// ---
// PROBLEM:
// Angular does not recognize that $scope.dateChanged
// is changed in the callback. The view does not update.
dateInput.bind('changeDate', scope.onDateChange);
}
};
});
var myModule = angular.module('myModule', ['app'])
.controller('MyCtrl', ['$scope', function ($scope) {
$scope.dateChanged = false;
$scope.datepickerOpts = {
autoclose: true,
format: 'mm-dd-yyyy'
};
$scope.onDateChange = function () {
alert('onDateChange called!');
// ------------------
// PROBLEM AREA:
// This doesnt cause the "date changed!" text to show.
// ------------------
$scope.dateChanged = true;
setTimeout(function () {
$scope.dateChanged = false;
}, 5000);
};
}]);
html
<div ng-controller="MyCtrl">
<p ng-show="dateChanged">date changed!</p>
<input type="text" value="02-16-2012" class="datepicker" datepicker="">
</div>
There are a number of scope issues at work in your demo. First , within the dateChange callback, even though the function itself is declared inside the controller, the context of this within the callback is the bootstrap element since it is within a bootstrap handler.
Whenever you change angular scope values from within third party code , angular needs to know about it by using $apply. Generally best to keep all third party scopes inside the directive.
A more angular apprroach is to use ng-model on the input. Then use $.watch for changes to the model. This helps keep all the code inside the controller within angular context. Is rare in any angular application not to use ng-model on any form controls
<input type="text" class="datepicker" datepicker="" ng-model="myDate">
Within directive:
dateInput.bind('changeDate',function(){
scope.$apply(function(){
scope[attrs.ngModel] = element.val()
});
});
Then in Controller:
$scope.$watch('myDate',function(oldVal,newVal){
if(oldVal !=newVal){
/* since this code is in angular context will work for the hide/show now*/
$scope.dateChanged=true;
$timeout(function(){
$scope.dateChanged=false;
},5000);
}
});
Demo: http://jsfiddle.net/qxjck/10/
EDIT One more item that should change is remove var dateInput = angular.element('.datepicker') if you want to use this directive on more than one element in page. It is redundant being used in directive where element is one of the arguments in the link callback already, and is instance specific. Replace dateInput with element
The changeDate event bound to the input seems to be set up to fire outside of the Angular framework. To show the paragraph, call $scope.$apply() after setting dateChanged to true. To hide the paragraph after the delay, you can use $apply() again inside the function passed to setTimeout, but you're likely to keep out of further trouble using Angular's $timeout() instead.
Fiddle