angular 1.5: watch doesn't call when i toggle the ui - angularjs

I my template i have:
<md-checkbox ng-model="$ctrl.isAdOps" aria-label="isAdOps">
isAdOps {{ $ctrl.isAdOps }}
</md-checkbox>
In my component:
(function (app) {
app.component('homeComponent', {
templateUrl: 'partials/home-partial.html',
bindings: {
isAdOps: '='
},
controller: ['$scope', '$state', function ($scope, $state) {
var self = this;
$scope.$watch(
self.isAdOps,
function (isAdOps) {
$scope.$broadcast('isAdOpsToggled', isAdOps);
}
);
}]
});
})(myApp);
why doesn't the watch called when i toggle the md-checkbox?

Angular $watch first parameter should of type string/function
string: Evaluated as expression
function(scope): called with current scope as a parameter.
replace self.isAdOps with 'isAdOps' in $watch or alternativly use a function syntax with $scope.

You are better off using the ng-change directive:
<md-checkbox ng-model="$ctrl.isAdOps"
ng-change="$ctrl.isAdOpsChanged($ctrl.isAdOps)"
aria-label="isAdOps>
isAdOps {{ $ctrl.isAdOps }}
</md-checkbox>
It avoids using $scope and adding a watcher.
For more information, see Writing Components without Watchers.
See also, AngularJS Developer Guide - Component-based application architecture.
Components should follow a few simple conventions:
Inputs should be using < and # bindings. The < symbol denotes one-way bindings which are available since 1.5. The difference to = is that the bound properties in the component scope are not watched, which means if you assign a new value to the property in the component scope, it will not update the parent scope. Note however, that both parent and component scope reference the same object, so if you are changing object properties or array elements in the component, the parent will still reflect that change. The general rule should therefore be to never change an object or array property in the component scope. # bindings can be used when the input is a string, especially when the value of the binding doesn't change.
Outputs are realized with & bindings, which function as callbacks to component events.
This will make the migration to Angular 2+ easier.

Related

Override controller functions in directive is a good idea?

I have a generic functionality implemented inside a controller. When i write a directive is it good idea to extend those controller functions inside the directive ?
Like in below implementation inside the link function.
var superCancel = scope.cancel;
// Overriding the cancel function from the controller
scope.cancel = function() {
if(element.hasClass('ng-dirty')){
element.removeClass("ng-dirty");
}
// Calling controller cancel
superCancel();
};
If your directive html is coming inside the controller in html then you can use $parent instead of rewriting
in directive:
$scope.$parent.cancel(); // only if controller coming as parent
If the controller is not coming as parent it's better to use a service or factory to implement that
Read here for more
It is better to have the directive use an attribute to set a parent scope value.
For example:
JS
app.directive("setModelApi", function() {
return {
require: "ngModel",
link: function(scope,elem,attrs, ngModelCtrl) {
scope.$eval(attrs.setModelApi, {$api: ngModelCtrl})
}
}
});
In the above example, the setModelApi directive evaluates the Angular Expression defined by the set-model-api attribute with $api exposing the ngModelController.
HTML
<input ng-model="x" set-model-api="xmodel=$api">
<button ng-click="xmodel.$setPristine()">Set Pristine</button>
The setModelApi directive sets the xmodel scope variable to the ng-model-controller API.
The button invokes the $setPristine method of the ng-model-controller API.
From the Docs:
$setPristine();
Sets the control to its pristine state.
This method can be called to remove the ng-dirty class and set the control to its pristine state (ng-pristine class). A model is considered to be pristine when the control has not been changed from when first compiled.
-- AngularJS ngModelController API Reference -- $setPristine
By using an HTML directive attribute to define the scope variable to which the API attaches, different inputs can use the directive and the connections are exposed in the HTML.
The DEMO on JSFiddle

AngularJS: How a Directive without a Controller can work

just reading a write up from this link http://weblogs.asp.net/dwahlin/creating-custom-angularjs-directives-part-6-using-controllers
it was hard me like novice in ng to understand their code sample. just tell me a example where people would write directive without controller ?
their code
(function() {
var app = angular.module('directivesModule');
app.directive('isolateScopeWithController', function () {
var controller = ['$scope', function ($scope) {
function init() {
$scope.items = angular.copy($scope.datasource);
}
init();
$scope.addItem = function () {
$scope.add();
//Add new customer to directive scope
$scope.items.push({
name: 'New Directive Controller Item'
});
};
}],
template = '<button ng-click="addItem()">Add Item</button><ul>' +
'<li ng-repeat="item in items">{{ ::item.name }}</li></ul>';
return {
restrict: 'EA', //Default in 1.3+
scope: {
datasource: '=',
add: '&',
},
controller: controller,
template: template
};
});
}());
Directive usage:
Attribute: <div isolate-scope-with-controller datasource="customers" add="addCustomer()"></div>
Element: <isolate-scope-with-controller datasource="customers" add="addCustomer()"></isolate-scope-with-controller>
How we can pass customer data directly to directive. basically we have model in controller and populate model and then pass that model data to directive via isolated scope or directive use controller scope. I am very confused the how above code can work, please help me to understand. thanks
The scenario that is being considered implies that the directive will be used in a part of the application, that already has a declared controller, the scope of which contains the properties datasource and add. In turn, new controllers will be instantiated for each of the instances of the directive and will have their own isolate scope.
In reality, it is much more common to create directives that do not have a controller, but rather use the link function. These directives can either rely on the parent controller, sometimes perform DOM manipulation, bind to JS events or simply serve as means to encapsulate part of your application.
You can find a good example of a directive that does not create its own controller here. It is taken from the Angular docs. You will find that it does not even belong to a parent scope in this case meaning that no controller is involved. In reality, an element like this would most probably report to a parent controller, which would then do something with the position.
You can read more about directives, the link function, and how directives work with controllers here.

ionic, pass scope variable to popover scope

I would like to pass a variable from the view scope ($scope.sort) into the popover scope.
Once set in the popover, the variable 'goes out' of it with no issue though, with code below in$scope.closeSortPopover`.
I thought the popover scope was the same as the scope of the controller it comes from.
But it seems it's not the case.
Where is the popover scope ?
If I understand correctly from here the popover scope is a child of the controller scope. So how can I access it... ?
FYI my popover is a list of <ion-radio>s offering different sorting opions for the big list in my main view.
html:
<button ng-click="openSortPopover($event, sort)">
controllers.js
$ionicPopover.fromTemplateUrl('templates/computeSortPopover.html', {
scope: $scope
}).then(function(popover) {
$scope.sortPopover = popover;
});
$scope.openSortPopover = function($event, sort) {
$scope.sortPopover.show($event);
};
$scope.closeSortPopover = function(sort) {
$scope.sortPopover.hide();
$scope.sort = sort;
};
In the controller js, you are assigning the scope of the controller to the scope of the popover. Thus all the methods in the controller scope are accessible via the popover view. In other words, both the controller view and popover view share the same scope.
In fact you might think this a violation of mvc as one controller cannot own two views, but with angular, you actually can't otherwise.
To create isolated scope, your only option is directives.
I discovered that due to javascript inheritence, the scope variable must be contained into an object to be passed to inherited scopes.
So instead of this:
$scope.sort = ....
I declared this in my controller:
$scope.data={};
$scope.data.sort = ....
and used in my html (both in the initial view and in the popover templates):
data.sort
Documented here apparently: https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance

Angular scopes and binding data to a directive

Consider
angular.module('App').directive('errors',function() {
return {
restrict: 'A',
controller:function() {
var self = this;
self.closeErrors = function() {
self.errors = [];
self.hasErrors = false;
}
},
controllerAs: 'errorsCtrl',
templateUrl: 'errors.html'
}
when called with
<div errors="otherCtrl.errors"></div>
the object errors comes from another controller.
I know i can add
scope: {errors:"="},
and then access it in my controller via
$scope.errors;
but when I assign it to
self.errors = $scope.errors.
self.errors never gets updated when it is changed in the parent.
So my question is, how can I let this work that whenerver my parentcontroller changes the errors object it is also changed in the errorsCtrl.
(Also I do know I can access errors directly in my template without the controller, but I simply want to use my errorsCtrl)
Add bindToController: true to your directive.
http://blog.thoughtram.io/angularjs/2015/01/02/exploring-angular-1.3-bindToController.html
Angular 1.3 introduces a new property to the directive definition
object called bindToController, which does exactly what it says. When
set to true in a directive with isolated scope that uses controllerAs,
the component’s properties are bound to the controller rather than to
the scope.
That means, Angular makes sure that, when the controller is
instantiated, the initial values of the isolated scope bindings are
available on this, and future changes are also automatically
available.

Sharing data between directives using attributes instead of services

I wanted to make a directive that would essentially act like a specialized input field. After some logic & user input, the 'value' attribute would be populated with a string of comma separated timeslots (hh:mm).
<time-slot value=""></time-slot>
becomes
<time-slot value="01:00,02:00,03:00"></time-slot>
I'd like to provide the flexibility for anyone to place a scope reference in the 'value' attribute tag -- whenever the attribute value is updated, so is the scope reference. (In the code below, myModel.times would be in the MyController scope).
<div ng-controller="MyController">
<time-slot value="{{ myModel.times }}"></time-slot>
</div>
I have had no problems accessing the 'value' attribute in the directive. However, I have not achieved two-way binding -- myModel.times never captures the changed value, even though the contents of the attribute have been changed when I inspect the element during runtime. I am using $attrs.$set to alter the value attribute.
I'm not sure if I'm missing something conceptually, or just missing some extra syntax. To keep this directive modular and shareable, I don't want to use a service to share data between the controller and directive, nor do I want to use a cascading scope. I think it would be optimal if the value attribute can simply be referenced by a scope variable and used as desired, much like a simple input tag:
<input ng-model="model.someText"></input>
An example with two-way data binding: See plunkr.
angular.module('myApp', [])
.directive('timeSlots', function() {
return {
scope: { value: '=' },
link: function($scope, $elem, $attrs) {
// you can access $scope.value here (after it has been interpolated)
}
};
})
.controller('MainCtrl', ['$scope', function($scope) {
$scope.value = 42;
}]);
In HTML:
<div ng-controller="MainCtrl">
<time-slots value="value"></time-slots>
</div>

Resources