AngularJS - follow scope in custom directive - javascript

I have custom directive and want to send some parameters(objects) to this directive.
In directive I have select list based on one of this parameters and I need to follow this parameter.
My directive:
<custom-directive rows="users">
</custom-directive>
and template:
<ui-select ng-model="value">
<ui-select-match allow-clear="true">
{{$select.selected}}
</ui-select-match>
<ui-select-choices repeat="row in rows | filter: $select.search">
<div>
<span ng-bind-html="row | highlight: $select.search"></span>
</div>
</ui-select-choices>
</ui-select>
normally if rows change ui-select will automatically update select list. But if rows is variable from outside of this directive, it wont.
Can I have the same variable outside and inside directive?
I think I can use ng-model, but I'm not sure it is good solution.
EDIT:
My directive code:
.directive('custom-directive', function () {
return {
restrict: 'E',
replace: true,
scope: {
rows: '='
},
templateUrl: 'template.html',
controller: ['$scope', function($scope) {
console.log($scope);
}]
};
})

You can modify your custom directive to work with ng-model in the following way:
directive('customDirective', function() {
return {
// ...
require: '?ngModel', // get a hold of NgModelController
link: function(scope, element, attrs, ngModel) {
if (!ngModel) return;
// Write data to the model
// Call it when necessary
scope.setValue = function(value) {
ngModel.$setViewValue(value);
}
}
};
});
Initial state of ng-model value can be retrieved using that method:
ngModel.$render = function() {
var value = ngModel.$viewValue || [];
};

You can use link instead of the controller to handle functionality with rows. Setting rows : '=' should already take care of the two-way binding.
.directive('custom-directive', function () {
return {
restrict: 'E',
replace: true,
scope: {
rows: '='
},
templateUrl: 'template.html',
link: function(scope, element, attr) {
console.log(scope.rows);
};
};
})
EDIT:
Add a watch to the value that you need to do behavior on external change.
.directive('custom-directive', function () {
return {
restrict: 'E',
replace: true,
scope: {
rows: '='
},
templateUrl: 'template.html',
link: function(scope, element, attr) {
scope.$watch('rows', function(oldValue, newValue) {
console.log('This is the newValue : ' + newValue);
};
};
};
})

Related

Angular call controller function from nested custom directive

I could use some help.
I have a controller containing a function called "loadInformation()". Inside this controller I use a service which is doing some DOM-Manipulation using custom directives.
These are the directives:
First custom Directive:
angular.module('...').directive('ngInputString', function () {
return {
restrict: 'AE',
replace: 'true',
scope: {
savedInformation: '=',
type: '#',
values: '='
},
templateUrl: 'directives/templates/inputString.html',
link: function (scope, element, attrs) {
scope.filterOb = {ttype: scope.type};
}
}
});
HTML file:
<div>
<input ttype="{{type}}" type="text" placeholder="{{param.name}}" value='{{param.value}}'
class='clean-input form-control'/>
<ng-saved-information type="STRING" saved-information="savedInformation"></ng-saved-information>
</div>
Nested custom directive:
angular.module('semtrosApp').directive('ngSavedInformation', function () {
return {
restrict: 'AE',
replace: 'true',
scope: {
savedInformation: '=',
type: '#'
},
template: '<ul><li ng-repeat="information in savedInformation | filter:filterOb">{{information.value}}<button type="button" ng-click="..?..">Use Information</button></li></ul>',
link: function (scope, elem, attrs) {
scope.filterOb = {ttype: scope.type};
}
}
});
When I dont use nested directives, it works just fine with that piece of code:
elem.bind('click', function() {
scope.$apply(function() {
scope.loadInformation();
});
});
But when they are nested, the second custom diretive is just looking inside the scope of the parent directive. Any idea how to pass through the function call?
It looks like the ngInputString directive already takes in some data and passes it down to ngSavedInformation. Why not have it take in a loadInformation callback as well and pass it down?
angular.module('...').directive('ngInputString', function () {
return {
scope: {
savedInformation: '=',
type: '#',
values: '=',
loadInformation: '&'
},
// etc
}
});
<ng-saved-information type="STRING" saved-information="savedInformation" load-information="loadInformation()"></ng-saved-information>
angular.module('semtrosApp').directive('ngSavedInformation', function () {
return {
scope: {
savedInformation: '=',
type: '#',
loadInformation: '&'
},
// etc
}
});
Also, instead of manually creating a click handler, you could do the following in the template:
ng-click="loadInformation()"

Passing Object to directive through attribute and containing variable

I wanna transfer a $scope.data to directive through its attribute in a Object, Can I achieve below format with any solution but not through separated attribute?
<custom-directive detail="{index:1, data: {{data}}}">
</custom-directive>
And the scope is set to below in directive
scope: {detail: "="}
One solution could be
return {
restrict: 'E',
require: 'ngModel',
scope: {
model: '=ngModel',
},
link: function(scope, element, attributes, ngModel) {
if (!ngModel) {
return;
}
console.log(scope.model); // your passed data
}
}
and then
<custom-directive ngModel="data"></custom-directive>
Now you will have your $scope.data passed to the directive inside scope.model. But note that, any change in scope.model in directive will reflect in $scope.data too.
To avoid that, you can simpley change ngModel.
return {
restrict: 'E',
scope: {
data: '=myData',
},
link: function(scope, element, attributes) {
console.log(scope.data); // your passed data
}
}
and then
<custom-directive my-data="data"></custom-directive>
You just write data only in your object, it will automatically resolves from your controller. Do as below:
HTML
<custom-directive detail="{index:1, data: data}">
</custom-directive>
Directive
myApp.directive('customDirective', function() {
return {
restrict:"AE",
scope:{
detail:"="
},
link:function(scope,ele,attrs) {
alert(JSON.stringify(scope.detail));
}
}
});
Fiddle Demo
You still have the solution to create your object in your controller :
$scope.detail = {index:1, data:$scope.data};
and to give it to your directive :
<custom-directive detail="detail"></custom-directive>

How to change ng-model scope value in custom Angular directive?

There is the following custom directive code:
angular.module('app.directives', ['ng']).directive('liveSearch', [
'$compile', '$timeout', function($compile, $timeout) {
return {
restrict: 'E',
replace: true,
require: 'ngModel',
scope: {
ngModel: '=',
liveSearchCallback: '=',
liveSearchSelect: '=?',
liveSearchItemTemplate: '#',
liveSearchWaitTimeout: '=?',
liveSearchMaxResultSize: '=?',
liveSearchResultField: '=?'
},
template: "<input type='text' />",
link: function(scope, element, attrs, controller) {
scope.$watch('selectedIndex', function(newValue, oldValue) {
var item;
item = scope.results[newValue];
if (item) {
scope.ngModel = '10';
element.val(item[attrs.liveSearchResultField]
}
});
}
}}]);
It's not a complete code of my directive, but it's enough to understand the problem. Also view code:
<live-search class="form-control" id="search1" live-search-callback="mySearchCallback" live-search-result-field="title"ng-model="skill" type="text"></live-search>
Value: {{ skill }}
<button class="btn btn-success" type="submit" ng-click="createEmployee">Сохранить</button>
My controller code:
$scope.createEmployee = function() {
console.log($scope.skill);
};
As you can see my custom directive has 'ng-model' attribute with name of variable 'skill'. How can I change 'skill' value in my custom directive? My way doesn't work. Thanks in advance!
In your link: try using
link:function(scope, element, attrs, ngModel){
scope.$watch('selectedIndex', function(newValue, oldValue) {
var item;
item = scope.results[newValue];
if (item) {
ngModel.$setViewValue(10);
ngModel.$render() // will update the input value as well
element.val(item[attrs.liveSearchResultField];
}
});
}
also it seems you have missed space between to separate ng-model attribute in your HTML
<live-search class="form-control" id="search1" live-search-callback="mySearchCallback" live-search-result-field="title" ng-model="skill" type="text"></live-search>
See the documentation here
When you use require: 'ngModel', the 4th parameter passed to your link function is ngModelController. Use method $setViewValue to update the value of ngModel passed to your directive, then call $render() if your view needs to be updated as well.
link: function(scope, element, attrs, ngModelController) {
scope.$watch('selectedIndex', function(newValue, oldValue) {
var item;
item = scope.results[newValue];
if (item) {
ngModelController.$setViewValue(10);
ngModelController.$render();
element.val(item[attrs.liveSearchResultField]
}
});
}

How to make two way binding between angular directives without using "dots"

Suppose, I have controller:
angular.module('tf').controller('Ctrl', function($scope){
$scope.params = {
orderBy: null
};});
And a directive "common":
angular.module('tf').directive("common", function() {
return {
restrict: 'E',
replace: true,
template: '<div><outer order-by="orderBy"><inner order-by-field="name1"></inner><inner order-by-field="name2"></inner></outer></div>',
controller: function ($scope) {
},
scope: {
orderBy: '='
},
link: function (scope, element, attrs) {
}
}});
Controller is using directive within it's template:
<div ng-app="tf">
<div ng-controller="Ctrl">
<common order-by="params.orderBy"></common>
<div style="color:red">{{params.orderBy}}</div>
</div>
This directive is using directive "outer":
angular.module('tf').directive("outer", function() {
return {
restrict: 'E',
transclude: true,
replace: true,
template: '<div ng-transclude></div>',
controller: function ($scope) {
this.order = function (by) {
$scope.orderBy = by
};
},
scope: {
orderBy: '=',
}
}});
Which is parent for the directive "inner":
angular.module('tf').directive("inner", function() {
return {
require: '^outer',
restrict: 'E',
transclude: true,
replace: true,
template: '<div ng-click="onClicked()">{{orderByField}}</div>',
controller: function ($scope) {
$scope.onClicked = function () {
$scope.outer.order($scope.orderByField);
}
},
scope: {
orderByField: '#'
},
link: function (scope, element, attrs, outer) {
scope.outer = outer;
}
}});
The directive "outer" shares "order" method with directive "inner" by it's controller. The directive "inner" is accessing it by using "require" mechanism.
For some reason, this is not working as expected (Property of the controller isn't updated each time it's changed by directive). If I place "orderBy" into object (e.g. {"order": {"by": null }} ) and use object instead of string value, everything is working as expected ( controller scope is properly updated by the directive). I know about "always use dots" best practices principle, but I don't wanna use it here, because it would make my directive's API less intuitive.
Here is jsfiddle:
http://jsfiddle.net/A8Vgk/1254/
Thanks

AngularJS directive to directive communication throwing an error about controller not found

I have 2 directives, one for searching and one for pagination. The pagination directive needs to access the search directive to find out what property we're currently searching by. When I load the page though, it throws an error saying Error: [$compile:ctreq] Controller 'search', required by directive 'pagination', can't be found!. However I have a controller setup in my search directive.
Here is my search directive:
angular.module('webappApp')
.directive('search', function ($route) {
return {
templateUrl: 'views/search.html',
restrict: 'E',
scope: {
searchOptions: '=',
action: '=',
currentProperty: '=',
currentValue: '='
},
controller: function($scope) {
$scope.searchBy = $scope.searchOptions[0].text;
$scope.searchByProperty = $scope.searchOptions[0].property;
$scope.setSearchBy = function(event, property, text) {
event.preventDefault();
$scope.searchBy = text;
$scope.searchByProperty = property;
};
$scope.search = function() {
$scope.searching = true;
$scope.currentProperty = $scope.searchByProperty;
$scope.currentValue = angular.element('#searchCriteria').val();
$scope.action($scope.searchByProperty, $scope.currentValue, function() {
$scope.searching = false;
});
};
$scope.reload = function() {
$route.reload();
};
}
};
});
Here is my pagination directive:
angular.module('webappApp')
.directive('pagination', function () {
return {
templateUrl: 'views/pagination.html',
restrict: 'E',
require: '^search',
scope: {
basePath: '#',
page: '=',
sort: '='
},
link: function(scope, element, attrs, searchCtrl) {
console.debug(searchCtrl);
scope.searchByProperty = searchCtrl.searchByProperty;
}
};
});
In order for one directive to use another's controller by use of require, it needs to either share the same element as the controller containing directive, or it has to be a child of it.
You can't use require in the way you have, where the elements are siblings.
Angular docs about directives, including require
If it doesn't make sense to rearrange the DOM in the way I've described, you should inject a service into both directives which contains the data/methods you wish to share between the two.
Note: you could also experiment with the $$nextSibling / $$prevSibling properties of the directives' scopes, but this would present only a very fragile solution
You cannot use require in directive like that, however , since the only thing you need to pass between directives is a string , just bind them to the same property in parent controller (it can be parent directive controller):
...
<div ng-app='app' ng-controller='MyCtrl as ctrl'>
<my-dir-one s1='ctrl.message'></my-dir-one>
<my-dir-two s2='ctrl.message'></my-dir-two>
and first directives:
app.directive('myDirOne', function ($route) {
return {
templateUrl: 'views/my-dir-one.html',
restrict: 'E',
scope: {
s1: '=',
second directive
app.directive('myDirTwo', function ($route) {
return {
templateUrl: 'views/my-dir-one.html',
restrict: 'E',
scope: {
s2: '=',

Categories