I have a question regarding the directive and controller.
I want to pass the data from directive to controller in my case.
template html
<img ng-src=“{{url}}” image-detect />
<div>width: {{width of image}}</div> // how do I show the value here
<div>height: {{height of image}}</div>
directive
(function () {
angular
.module(‘myApp’)
.directive(‘imageDetect’, imageDetect);
function imageDetect() {
var directive = {
'restrict': 'A',
'controller': imageController
};
return directive;
}
function imageController($scope, $element) {
$element.on('load', function() {
$scope.imageWidth = $(this).width();
$scope.imageHeight = $(this).height();
//not sure what to do to pass the width and height I calculate in directive to the parent
});
}
})();
How do I pass imageWidth and imageHeight to the parent scope and show it in the template? Thanks a lot!
Two methods come to my mind:
You can have an object something like imageDimention declared on base/parent/whatever scope and then have it shared with directive through scope isolation and then from directive's controller's scope you can access that imageDimention object and set its imageWidth and imageHeight properties. Setting these properties in directive will take effect in base/parent/whatever scope as well:
Example of scope isolation, copied from angular documentation
angular
.module('yourapp')
.directive('myImage', function() {
return {
restrict: 'E',
scope: {
imageDimention: '=imageDimention'
},
controller: 'ImageController'
};
});
Then in ImageController's scope you can access same imageDimention object
Create a ContextService this can be used for sharing data between different angular components not just these image properties. Its good to have a ContextService somewhat generic so that it can be reused/general purpose.
ContextService can be something like:
angular.module('yourapp')
.factory('ContextService', ContextService);
function ContextService() {
var service = {};
var data = {};
service.set = set;
service.get = get;
function set(key, value) {
if(key !== null && key !== undefined){
data[key] = value;
}
}
function get(key) {
return data[key];
}
return service;
}
And then you can inject this service into your angular components(controllers/directives/other services) and access it as some kind of global objects because services are singleton, this will serve you as data sharing module.
In your case, you probably have a controller that is attached to view, so assuming you have that controller, should declare an object say image on this controllers scope:
$scope.image = {
url: 'imageUrl'
width: '0px',
height: '0px',
}
Then your html template should probably be something like this:
<img ng-src="{{image.url}}" image-detect />
<div>width: {{image.width}}</div>
<div>height: {{image.height}}</div>
And your directive should look like this:
(function () {
angular
.module(‘myApp’)
.directive(‘imageDetect’, imageDetect);
function imageDetect() {
var directive = {
'restrict': 'A',
'scope': {
'image': '=image'
},
'controller': imageController
};
return directive;
}
function imageController($scope, $element) {
$element.on('load', function() {
//here you can access image object from scope which is same as controller that is attached to the view
$scope.image.width = $(this).width();
$scope.image.height = $(this).height();
});
}
})();
I hope this might help...
Related
The goal here is to have two different directives that are technically siblings share functionality. I will either use one or the other, never one inside the other.
However, the second directive will have all the capability of the first with some small additions. Because of this, I would like the functionality to inherit from the "Parent" directive to the "Child".
I'm achieving this by re-using the same directive definition object from the Parent on the Child, with the exception of the controller/template fields being changed.
This was all working well up until I hit the watchers from my ParentDirCtrl. For some reason the watcher seems to be set up correctly watching mydir.obj1 and yet somehow inside the watcher callback function mydir.obj1 becomes undefined.
I'm assuming something about _.extend/$controller is changing how the $scopes work so mydir.obj1 isn't defined in the ParentDirCtrl, but I'm not sure why that would be the case.
Plunk
angular.module('plunker', [])
// lodash
.constant('_', _)
.controller('MainCtrl', function($scope, $timeout) {
$scope.obj = {
name: 'John',
age: 30,
};
})
.controller('ParentDirCtrl', function($scope) {
var mydir = this;
mydir.doStuffInParent = function() {
alert('executed from the parent directive');
}
$scope.$watch('mydir.obj1', function() {
// ====================================
// ERROR
// Why is 'mydir.obj1' undefined when
// occupation is set?
// ====================================
mydir.obj1.occupation = 'Meteorologist';
});
})
.directive('parentDirective', parentDirective)
.directive('childDirective', function() {
// borrow the directive definition object from the parent directive
var parentDDO = parentDirective();
// uodate the template and controller for our new directive
parentDDO.template = [
'<div>',
'<p ng-click="mydir.doStuffInParent()">{{mydir.obj1.name}}</p>',
'<p ng-click="mydir.doStuffInChild()">{{mydir.obj1.age}}</p>',
'</div>'
].join('');
parentDDO.controller = function($scope, $controller, _) {
// extend 'this' with the Parent's controller
var mydir = _.extend(this, $controller('ParentDirCtrl', { $scope: $scope }));
mydir.doStuffInChild = function() {
alert("executed from the child directive");
};
};
return parentDDO;
});
// this will be moved to the top during declaration hoisting
function parentDirective() {
return {
restrict:'E',
scope: {},
bindToController: {
obj1: '=',
},
template: '<div>{{mydir.obj1}}</div>',
controller: 'ParentDirCtrl',
controllerAs: 'mydir',
};
}
obj1 is populated on the child controller instance - that's why mydir.obj1 is undefined in the parent watcher. You can access obj1 directly via scope or by using the reference passed into the watcher:
$scope.$watch('mydir.obj1', function(val) {
$scope.mydir.obj1.occupation = 'Meteorologist';
// or
val.occupation = 'Meteorologis';
});
There is no scope inheritance here - both controllers operate on the same scope. Controller-AS syntax is what confuses you - I'd get rid of it to make things clearer.
I would like to set attrs to undefined from unit test, I tried few approaches and didn't get to set it to undefined. Below is my directive:
angular.module('myApp').directive('someElement', function () {
var directive = {};
directive.restrict = 'E';
directive.replace = true;
directive.transclude = true;
directive.templateUrl = function (element, attrs) {
var template = '';
if(attrs) { // would like to invoke this in unit test and set it to `undefined`
//do something
}
return template;
};
directive.scope = {...};
directive.compile = function () {
//do something
return directive;
});
here is a the snippet, assume that directive is compiled and $digest cycle is triggered. here is what i got in the test:
it('should set attrs to undefined', function () {
.....
attrs = {};
scope.$apply();
expect(attrs).toBeUndefined(); // I want this to pass!!
});
It is possible to get original directive factory from within a test and modify it before compiling and even unit-test it's methods, but I'am not sure if this approach to test directives is right.
If you take a look at the source code, you can see that angular registers each directive as a factory with a 'Directive' suffix. Knowing that, you can inject your directive factory in a test:
var directiveFactory;
beforeEach(inject(function ($injector) {
// considering that you directive is called 'myEl'
directiveFactory = $injector.get('myElDirective')[0];
}));
Here one should use [0]'s because factories are returned in array - that's the way angular deals with directives, which have an option multiElement: true (if you have this option enabled, then maybe this trick won't work).
As a result the variable directiveFactory now holds an actual factory, so original templateUrl could be replaced and fake called with undefined attrs:
it('should do something when attrs are undefined', function () {
var template = '<my-el></my-el>';
// backup original function
var originalTemplateUrl = directiveFactory.templateUrl;
// replace with fake one
directiveFactory.templateUrl = function (element, attrs) {
// call original function with undefined attrs
return originalTemplateUrl(element, undefined);
};
var element = $compile(template)($scope);
$scope.$digest();
// expect whatever
});
Now you could examine calls to your directive's original templateUrl and find out that attrs are undefined.
See the plunker
I have one BaseController with common functions which my all other controllers inherit .
The controller is like this
function BaseController () {
this.defaultFilters = {};
this.doStuff = function ($scope) {
$scope.myobj.value = 1;
this.otherfunction();
};
I inherit that in my controller like this
BaseController.call($scope);
Now in my do stuff function i need to pass $scope because myobj is only visible there.
Now i want to know that how can i pass that in my template because i want to call that function when some click on some button
ng-click="doStuff(scope)"
Everything that you associate with your controller's scope, so you just associate your scope with some variable and i guess that will do the job.
Something like this :
app.controller(function($scope) {
$scope.scope = $scope;
});
But if you go by some standard approach, i suggest moving these common functions inside some service, injecting this service into each controller and using it in the views.
Something like this :
app.service("constantService", function() {
this.data = {}; // This will represent your common data.
this.commonFunction = function() {
};
});
app.controller(function() {
$scope.constantService = constantService;
// You can now use $scope.constantService.data as your reference for data, and then can copy it to some local $scope variable whenever required.
});
ng-click="constantService.commonFunction()"
I'm trying to create a directive that will be used in multiple places in the app and I want it to opt into a controller.
When using the controller method
return {
...
controller: 'BlogDashCourseCtrl',
...
}
it gets the controller fine. But when I require it
return {
...
require: '^BlogDashCourseCtrl',
...
link: function($scope, iElm, iAttrs) {
$scope.title = iAttrs.title; // Do not share me with other directives
if($scope.title === $scope.step) { // $scope.step comes from a shared scope
...
}
}
}
it can't find the controller.
I don't want the controller to be called multiple times. I just want the directives to share a scope, have a private scope, too (so $scope in the directive doesn't bubble up) and do some stuff with a service.
A directive is attached to a DOM node. A DOM node can't have two scopes. Either you share the parent scope or you create an isolated one and explicitly inherit stuff from it.
BlogDashCourseCtrl:
this.step = 'whatever'; //maybe $scope.step
SomeDirective:
return {
...
require: '^BlogDashCourseCtrl',
...
link: function($scope, iElm, iAttrs, blogDashCourseCtrl) {
$scope.title = iAttrs.title; // Do not share me with other directives
if($scope.title === blogDashCourseCtrl.step) { // $scope.step comes from a shared scope
...
}
}
}
Notice that blogDashCourseCtrl does not reference the $scope of that directive/controller, but the reference itself.
There really is extensive documentation on this, with examples.
I have a ParentController and a ChildController, which look like this:
ParentController:
app.controller("ParentController", function($scope) {
// define preload method, expose to template
$scope.preload = function() {
console.log("parent");
};
// call preload function
$scope.preload();
});
ChildController:
app.controller("ChildController", function($scope) {
$controller("ParentController", {
$scope: $scope,
});
// override preload method here
$scope.preload = function() {
console.log("child")
};
});
As you can see, both Controllers define $scope.preload(), which is called upon instantiation in the ParentController. My goal is for ChildControllerto overwrite this method, so that when it is called, it logs a different string to the console.
Is this possible? If not, how else would I go about reusing all methods defined in ParentControllerin my ChildController?
To share the data among independent controllers, Services can be used. Create a service with the data model that needs to be shared. Inject the service in the respective controllers.
In the following example, Service is used to store the variable x. The independent controllers will check the value of X whenever needed.
angular.module('myApp', [])
.service('myService', function () {
var x=5 ;
return {
increase : function() {
x++;
},
getX : function() {
return x;
}
};
})
function ControllerA($scope, myService) {
$scope.x = 1;
$scope.incrementDataInService= function() {
myService.increase();
}
$scope.syncDataWithService= function() {
$scope.x = myService.getX();
}
}
function ControllerB($scope, myService) {
$scope.x = 1;
$scope.incrementDataInService= function() {
myService.increase();
}
$scope.syncDataWithService= function() {
$scope.x = myService.getX();
}
}