I created a directive, I´m using in my template:
<data-input> </data-input>
In my directive (dataInput) I have the template (...data-input.html).
In that template the html-code says:
<input ng-change="dataChanged()" ... > ...
In the JavaScript it says:
scope.dataChanged= function(){ //change the data }
Works perfectly, but know I need to safe the changed data to a variable in my controller (that has a different scope of course).
The template, where I put the <data-input> </data-input> belongs to the final scope, I´m trying to reach.
I tried it via parameter, but it didnt work.
Thanks
Here are your options:
You can nest controllers if possible. This will create scope inheritance and you will be able to share variables of the parent.
You can use $rootscope from both the controllers to share data. Your dataChanged function can save anything to the $rootscope
You can use angular.element to get the scope of any element. angular.element('data-input').scope() returns it's cope.
This is not recommended. But there are circumstances in which people use global space to communicate between angular and non-angular code. But I don't think this is your case.
You can use Angular Watches to see changes is some variable, like this
eg:
$scope.$watch('age + name', function () {
//called when variables 'name' or 'age' changed
//Or you can use just 'age'
});
Related
When I try to call one controller method from another controller in the angular javascript, an error occurs. I think something is missing there. If anyone has the complete method to do this, please give me the answer.
Being able to share a variables throught different controllers it's a common issue in angular.
Your function can be seen as an object: you'll need to share it's reference throught scopes.
To perform that, you can either:
.
Use a service:
You can see services as singletons object that are injected in your controllers: a common object shared throught your controllers.
You can use this wonderful guide to understand how to share your scope variables by using services (this guide uses factory and not services, but for your needs will be ok).
If you still wants to use services, follow this answer.
.
Use scope hierarchy:
Sometimes you have a hierarchy between controllers (and thus, scopes).
Let's assume you have an HTML formatted this way:
<div ng-controller="parentController">
<!-- some content here...-->
<div ng-controller="childController">
<!-- some content here...-->
</div>
</div>
This structure will produces two different scopes: one for parentController, and one for childController.
So, you childController scope will be able to access parentController scope by simply using $parent object. For example:
myApp.controller('parentController', function($scope) {
$scope.functionExample = function(){
console.log('hey there');
};
})
myApp.controller('childController', function($scope) {
$scope.functionExample();
});
Note: the parent object is accesible directly in your child scope, even if declared in your parent.
But beware of this method: you can't never really sure of your scope hierarchy, so a check if the variable is defined should be always needed.
.
Use $rootScope:
You can use rootscope as a common shared object to share yours. But beware: this way, you'll risk to pollute your $rootScope of variables when $rootScope should be used for other uses (see message broadcasting).
Anyway, see this answer for an example use case.
.
Use Messages:
By using messages, you can share objects references, as shown in this example:
myApp.controller('parentController', function($scope, $rootScope) {
var object = { test: 'test'}
$rootScope.$broadcast('message', message)
console.log('broadcasting');
});
myApp.controller('childController', function($scope, $rootScope) {
$scope.$on('message', function(message){
console.log(message);
});
});
.
In the end, i suggest you to use a common service for all your variables or, where you can, use the scope hierarchy method.
If two controllers are nested in one controller,
then you can simply call the other by using:
$scope.parentMethod();
Angular will search for parentMethod() function starting with the current scope up to it reach the $rootscope
View:
<div ng-controller="MainPageController as mpc">
<h1>{{mpc.city}}</h1>
</div>
Controller:
app.controller('MainPageController',['GetCityService', function(GetCityService){
this.city = null;
this.getCityService = GetCityService;
this.getCityService.getCity().success(function(data){
this.city = data.city
console.log(this.city); // Prints the city correctly.
});
}]);
I am trying to use "controller as" method to access values from controller. My service returns the city name correctly which is assigned to controller variable "this.city". but I am unable to bind the value in the View" (I see nothing displayed in the view)
Please let me know if I'm doing it wrong.
PS: I tried to use $scope, it perfectly works. I am unable to understand why I am unable to bind the controller variable" and If possible suggest when $scope and this variables should be used.
Thanks in advance :)
Since the original question has been answered, I'll try to explain the answer. It's just a matter of understanding 'this' in javascript.
Within your controller function 'this' refers to the controller itself. With in your success callback 'this' no longer refers to the controller (I'm guessing the window if you're not in strict mode?).
By saving 'this' to a variable, such as 'vm', whenever you refer to vm you know that you are referencing the controller, and not whatever 'this' refers to in that context (which can change).
On another note, now that we have all these features in 1.5, it's best to not use $scope anymore and to use 'controllerAs' all the time. I also suggest moving to components instead of using stand-alone controllers.
I'm wondering why i should use isolated scope directives? I can always get some data with service inside my controller and those data will be available inside directive that don't have isolated scope, if i want to use that directive in other place, i can just send request and get the data.... Right?
When you create directive with isolated scope, you must get the data and pass it to directive..
What is the advantage of using isolated scope directives?
Why i should use it and when?*
Because it makes your directive an own module(design-wise, Im not talking about angular.modules ;-) with a clear defined self-contained interface, which means that it is reusable in any context. It also makes its code readable, as everything that the directive works with is in the directives code and not in some magic parent scope that it relies on. Lets look at an example without an isolated scope:
Controller:
angular.module("carShop",[])
.controller("CarStorefrontController",function(){
//For simplicity
this.cars = [
{ name: 'BMW X6', color: 'white' },
{ name: 'Audi A6', color: 'black' }
];
});
Directive:
angular.module("carShop")
.directive("carList",function(){
return {
template: ['<ul>',
'<li ng-repeat="car in vm.cars">',
'A {{car.name}} in shiny-{{car.color}}',
'</li>',
'</ul>'].join("")
};
});
Page:
<div ng-app="carShop" ng-controller="CarStorefrontController as vm">
<h2>Welcome to AwesomeCarShop Ltd. !</h2>
<p>Have a look at our currently offered cars:</p>
<car-list></car-list>
</div>
This works, but is not reusable. If I want to display a list of cars somewhere else in my application, I need to rename my controller there to vm and have it have a field named cars containing my array of cars for it to work. But if I change my directive to
angular.module("carShop")
.directive("carList",function(){
return {
scope: { cars: '=' },
template: [ /* same as above */ ].join("")
};
});
and change <car-list></car-list> on my storefront page to <car-list cars="vm.cars"></car-list>", I can reuse that directive everywhere by just passing in any array of cars without caring where that array came from. Addiionally, I can now replace my Controller on the storefront page with a completely different one, without having to change my directive definition(and without changing all the other places where I use car-list).
It really comes down to the same reason why you should not put all your javascript variables into one global scope so they are easily accessible from everywhere: reusability, readability, maintainability - that is what you get by modularizing and encapsulating your code, by going for low coupling and high cohesion following the black-box-principle.
Directive with isolated scope basically used when you want to create a component that can be reusable throughout your app, so that you can use it anywhere on you angular module.
Isolated scope means that you are creating a new scope which would not be prototypically inherited from the parent scope. But you could pass values to directive that you want to required from the parent scope. They can be pass in the various form through the attribute value, there is option that you can use scope: {} to make directive as isolated scope.
# - Means interpolation values like {{somevalue}}
= - Means two way binding with parent scope variable mentioned in
attribute {{somevalue}}
& - Means you can pass method reference to directive that can be call from the directive
Isolated scope created by using $scope.$new(true); where $scope is current scope where the directive tag is placed.
As your saying, you are going to store data in Service, rather than make an isolated scope. But this approach never going to work for you as service is singleton thing which is having only one instance. If you want to use your directive multiple times then you need to make another variable in service that would take the data required for other element. If that directive count increased to 10, then think how you service will look like, that will look really pathetic.
By using isolated scope, code will look more modularize, code re-usability will be improved. Isolated scope variables never going to conflict with the other variables which is of same name in parent scope.
I'm a bit confused with the use of $scope in controllers and of scope in directives. Please verify if my understanding is correct (and also provide some alternative ways how to do this).
Let's say I have an html:
<div ng-controller="app1_Ctrl">
.
.
.
<input type="text" ng-model="value"/>
<input type="checkbox" />
<button ng-click="submit()"></button>
</div>
And my main.js
(function() {
angular.module('mainApp', ['app1']);
})();
And my app1 looks like this (based on official AngularJS documentation here)
(function() {
var app = angular.module('app1', []);
app.controller('app1_Ctrl', ["$scope", function($scope) {
.
.
.
}]);
app.directive('app1_Dir1', [function() {
function link(scope, element, attr) {
scope.$watch(attr.someAttrOfCheckBox, function() {
// some logic here
});
function submit() {
// some logic here
}
}
return link;
}]);
})();
How does $scope.value passed in scope in directive so that I can do some manipulations there? Will ng-click fire the function submit() in the directive link? Is it correct to use scope.$watch to listen for an action (ticked or unticked of course) in checkbox element?
Many thanks to those who can explain.
By default, directive scope is controller $scope; but it means the directive is directly dependent on your controller and you need a different controller for each instance of the directive you want to use. It is usually considered a best practice to isolate your directive scope and specifically define the variables you wish to pass it from your controller.
For this, you will need to add a scope statement to your directive :
scope {
label :'#',
context : '=',
function : '&'
}
and update your view :
<my-directive label="labelFromController" context="ctxtFromController" function="myFunction()" ></my-directive>
The symbols denote the kind of thing you wish to pass through : # is for one-way binding (as a string in your directive), = is for two-way binding of an object (which enables the directive to update something in your controller), and & is for passing a function.
There are a lot of additional options and subtleties that are best explained by the Angular doc https://docs.angularjs.org/guide/directive. There are also some nice tutorials out there (e.g. http://www.sitepoint.com/practical-guide-angularjs-directives/)
Your submit() function is not attached to anything, so you won't be able to call if from your viewer. You need to define it as scope.submit = function() ... in your link function if you wish to access it.
You can use $watch for this kind of thing, but there are usually other more elegant ways to achieve this by leveraging the fact that angular already "watches" the variables it is aware of and monitors any changes he can (this can be an issue when some external service changes data for exemple, because angular cannot listen to events it is not made aware of). Here, you can probably simply associate the ng-model directive to your input checkbox to store its true/fale (checked/unchecked) value, and the ng-change or ng-click directives to act on it. The optimal solution will mostly depend on the exact nature of your business logic.
Some additional thoughts :
The HTML insides of your directive should be packaged in an inline template field, or in a separate HTML file referenced by the templateUrl field in your directive.
In your HTML code above, your directive is not referenced anywhere. It should be an element, attribute or class (and your directive definition should reflect the way it can be called, with the restrict field). Maybe you have omitted the line containing the directive HTML, but as it stands, your directive doesn't do anything.
To my knowledge, you don't need to return link. Think of it as the "body" of your directive, where you define the variables and functions you will call in the HTML.
Your directive doesn't actually need HTML code and the above thoughts might be irrelevant if you are going in a different direction, but encapsulating some kind of view behaviour that you want to reuse is probably the most common use of directives.
How to set ng-controller as an expression from the $scope?
According to the documentation:
ngController – {expression} – Name of a globally accessible
constructor function or an expression that on the current scope
evaluates to a constructor function.
But how to evaluate scope expression as a controller for controllers that have been registered with module .controller?
For example:
Layout:
<div ng-controller="myExpr"></div>
JavaScript (define controller):
app.controller('myCtrl', ['$scope', '$timeout', function () { ... }];
JavaScript (parent scope):
$scope.myExpr = ...;
What should be in myExpr to use myCtrl as a controller via expression?
I've tried $controller('myCtrl')... not working...
P.S. If controller has been defined via globally accessible function.. it's possible to provide it as myExpr. But what to do if it has been defined so?
The expressions that ng-controller accept are a bit wierd. So you can do this by writing your controller slightly differently (but read below as for why you probably don't want to).
function myCtrl($scope) {
$scope.value = 'Stuff';
}
This is a controller and will work like normal for this case. Like in this example: http://jsbin.com/ubevel/2/edit
So why not do it?
First of all this is not a good way to define things from a testing perspective. Secondly, this allows you to set the controller dynamically on load but it won't allow you to change things after that. If you change the value of myExpr after the page has loaded the controller will not change.
So what to do?
I would highly suggest looking at using a service instead. Swap out your actions by supplying your outer controller with a service that you then change in the same manner you are now trying to change the inner controller. So something like: http://jsbin.com/ubevel/5/edit
This service can be swapped out on the fly, changing it will change the actions that are available in the scope.
You could also use an include, but this would result in duplicate html. I personalty am fine with this since I am against reusing html for two different types objects (sooner or later you want to change one but not the other and it becomes a mess). But a lot of people would object to that.
An extra note: There are probably nicer ways to do it with controllers, I probably haven't looked at all angles, but I just don't think controllers are the right tool for this case.