I have a problem. I have a controller:
app.controller("controllerLots", ["$scope", "factoryClient", "factoryLots", "factoryContracts", "fillTicketFactory", function ($scope, factoryClient, factoryLots, factoryContracts, fillTicketFactory) {
var vm1 = this;
vm1.lot = {};
vm1.lots = [];
vm1.client = {};
vm1.ticket = {};
vm1.clientFindDB = [];
// vm1.filterName =vm1.client.clientName +""+vm1.client.clientFn+"" +vm1.client.clientPatroncyr;
// vm1.filterPassport = vm1.client.clientSerialPass + vm1.client.clientNumberPass;
vm1.selectClient =function(client){
vm1.client=client;
vm1.filterName = vm1.client.clientName +''+vm1.client.clientFn +''+vm1.client.clientPatroncyr;
vm1.filterPassport = vm1.client.clientSerialPass +""+vm1.client.clientNumberPass;
}
vm1.addLot = function () {
if (angular.isUndefined(vm1.lot.orderNumberLot) || vm1.lot.n <= 0) {
vm1.lots.push(vm1.lot);
vm1.lot.n = vm1.lots.length;
}
vm1.lot = {};
}
and I have a directive in which I want to put some functions from above controller, this is my directive now looks like:
app.directive("searchDir", [function(){
return{
scope:{
filterName: '=',
filterPass:'='
},
templateUrl:'searchDirective.html',
replace:true,
controllerAs:'ctrl',
controller:controller
} }]);
How can I use those functions inside my directive? thanks.
How can I use those functions inside my directive?
I guess what you are asking is how you can use those functions in your directive template. For that you need to add all the properties, that you want to access in the template, to the "scope".
In your directive change the controller property of the directive like:
controller: 'controllerLots'
And in your controller, instead of adding properties to "this", add the properties to "$scope".
Related
my main controller code is as follows
var self = this;
self.info = {};
Restangular.all('detail').post(this.postbody).then(function(data){
// this.items = data.tiles;
self.items = Restangular.copy(data);
self.info = self.items.pd;
console.log(self.info);
});
the template for my main controller is as follows
<div ng-controller="prddetail as prd">
<pdinfotile dat=prd.info></pdinfotile>
<div>
In my directive I have used # for one way binding and displayed the value but when new values gets updated it is not getting displayed after call from restangular and these values are not updated in directives attribute. How do I proceed thanks in advance.
Note:
angular.module('brandmarshal')
.directive('pdinfotile',function () {
return{
scope:{
dat:'#'
},
restrict : 'E',
templateUrl:'../../templates/pd/pd_info.html',
controllerAs:'ctrl',
bindToController:true,
controller: function() {
var self = this;
self.obj = JSON.parse(self.dat);
console.log(self.obj);
}
}
});
You have the wrong binding value setup.
# is for passing in a string, etc.
= is for two way binding - kind of hacky considered now with enforcing 1 way flow.
< is the single way binding you are looking for!
for example:
angular.module('brandmarshal')
.directive('pdinfotile',function () {
return{
scope:{
dat:'<' //<------single way binding or try a = for 2 way binding
},
restrict : 'E',
templateUrl:'../../templates/pd/pd_info.html',
controllerAs:'ctrl',
bindToController:true,
controller: function() {
var self = this;
self.obj = JSON.parse(self.dat);
console.log(self.obj);
}
}
});
Here is code:
Directive code:
angular.module('app', ['localytics.directives', 'ngLoadScript'])
.directive("home", function() {
return {
restrict: "A",
replace: true,
template: "<div ng-include='var'>{{var}}</div>",
controller: function($scope) {
//loading home page - as default
$scope.var = "/tfm/home.html"
//on change the page changed dynamically!
$scope.change = function(where) {
$scope.var = where;
}
}
}
})
I WANT TO CALL chanage(where) FUNCTION OF DIRECTIVE - DEFINED IN CONTROLLER OF DIRECTIVE.
Controller Code:
.controller('wToEatController', ['$scope', function($scope) {
$scope.submitInfoAboutTable = function() {
//validation for time selection
if($scope.idealTableTime == undefined || $scope.rightTableTime == undefined) {
return;
}
//Booking details to be updated - here users preference is updating!
var bookingDetails = {
idealTableWaitTime: $scope.idealTableTime,
rightTableWaitTime: $scope.rightTableTime
}
//Let's update booking information - the booking key will be same used while login, public/tfm.html
FirebaseDbService.updateBooking(function(isUpdated) {
console.log(isUpdated);
//I WANT TO CALL chanage(where) function of DIRECTIVE
$scope.change = "change('/tfm/home.html')";
}, bookingDetails, bookingKey);
}
}]);
Is it possible?
You have to create an attribute with which the link will be done (in this example customAttr):
<span yourDirectiveName customAttr="myFunctionLink(funcInDirective)"></span>
And into your directive controller just set the new attribute like in the following snippet( '&' two way data binding ) , and create a connection with your directive method :
scope : {customAttr : '&'},
link : function(scope,element,attrs){
scope.myDirectiveFunc = function(){
console.log("my directive function was called");}
}
scope.customAttr({funcInDirective : scope.myDirectiveFunc});
}
And in your controller :
$scope.myFunctionLink = function(funcInDirective){
$scope.callableDirectiveFunc = funcInDirective;}
Now you can call your directive function with $scope.callableDirectiveFunc();
I have the following directive,
(function(){
angular.module("pulldownmodule")
.controller("pulldownCtrl",['pullDownServices','$scope',"multiselectDefaults","templates",function(pullDownServices,$scope,multiselectDefaults,templates){
//Local variables
_this = this;
var dropdownData = {};
var currentTemplate = {};
var firstTemplate;
//Validation function
function validateInput(){
console.log(_this.dropdownid);
if (_this.dropdownid) {
getPullDownData(_this.dropdownid,_this.filter);
}
//check if the dropdown ID is present
}
$scope.$watch('pulldownCtrl.dropdownid',function(newVal){
console.log(_this.dropdownid);
if (newVal) {
validateInput();
};
});
}])
.directive('pulldown', [function(){
return {
scope: {},
bindToController:{
dropdownid:"=",
filter:"=",
templatetype:"#"
},
controller:'pulldownCtrl',
controllerAs:'pulldownCtrl',
templateUrl: 'pulldown/html/dropDownDirective.html'
};
}]);
})()
I am calling the directive 2 times as follows
<div pulldown dropdownid="currentID" templatetype="template2" filter="customFilter"></div>
<div pulldown dropdownid="currentID2" templatetype="template2" filter="customFilter2"></div>
Passing the value of dropdownid in the controller as
$scope.currentID = 1;
$scope.currentID2 = 5;
The issue here is if i call the directive only 1 time everything works fine, but if i call it multiple times then i get the _this.dropdownid in $watch as the second directives value. Not sure what I'm doing wrong.
Probably i have to create a new instance using 'new'.
Directive HTML
Following is the major part of the directives HTML,
<select id="searchData" kendo-multi-select="pulldown" k-options="ddoptions" k-rebind="ddoptions" k-on-change="getChangevalue('searchData')"></select>
i'm using the kendo multiselect
As #hgoebl point out _this = this; it is kind of global (not application level) variable though you use in a function scope.
Use var _this = this;
//after assign "_this" is not accessible here
(function(){
//after assign "_this" is accessible here
angular.module("pulldownmodule")
.controller(...function(){
_this = this; //use var _this = this;
//...others code
});
}();
angular.module("pulldownmodule",[])
.controller("pulldownCtrl",['$scope',function($scope){
//Local variables
_this = this;
// Initialize your models here
$scope.currentID = '1';
$scope.currentID2 = '3';
var dropdownData = {};
var currentTemplate = {};
var firstTemplate;
//Validation function
function validateInput(){
console.log(_this.dropdownid);
if (_this.dropdownid) {
getPullDownData(_this.dropdownid,_this.filter);
}
//check if the dropdown ID is present
}
$scope.$watch('currentID', function (newVal) {
console.log($scope.currentID);
if (newVal) {
validateInput();
};
});
$scope.$watch('currentID2', function (newVal) {
console.log($scope.currentID2);
if (newVal) {
validateInput();
};
});
}])
.directive('pulldown', [function(){
return {
scope: {
dropdownid:"=",
filter:"=",
templatetype:"#"
},
template: '<select ng-model="dropdownid"> <option ng-repeat="a in [1,2,3,4,5]" value="{{a}}"> {{a}} </option> </select>',
link: function (scope,element, attr) {
scope.$watch("dropdownid", function (newVal) {
scope.dropdownid;
});
}
};
}]);
LIVE DEMO
Consider the following spinner-click directive:
Directive Use:
<button class="btn btn-mini"
ng-class="{'btn-warning': person.active, disabled: !person.active}"
spinner-click="deleteItem($index)"
spinner-text="Please wait..."
spinner-errors="alerts">
Delete
</button>
Directive:
app.directive('spinnerClick', function() {
return {
restrict: 'A',
scope: true,
link: function(scope, element, attrs) {
var originalHTML = element.html();
var spinnerHTML = "<i class='icon-refresh icon-spin'></i> " + attrs.spinnerText;
element.click(function() {
if (element.is('.disabled')) {
return;
}
element.html(spinnerHTML).addClass('disabled');
scope.$apply(attrs.spinnerClick).then(function() {
element.html(originalHTML).removeClass('disabled');
}, function(errors) {
element.html(originalHTML).removeClass('disabled');
// This is ugly! Is there a better way?
var e = scope[attrs.spinnerErrors];
e.length = 0;
e.push.apply(e, errors);
});
});
}
};
});
Controller:
app.controller('MainCtrl', function($scope, $q, $timeout) {
$scope.alerts = ['First alert'];
$scope.people = [
{ name: 'David', active: true },
{ name: 'Layla', active: false }
];
$scope.deleteItem = function(index) {
var defer = $q.defer();
$timeout(function() {
defer.reject(["Something 'bad' happened.", "Check your logs."]);
}, 2000);
return defer.promise;
};
});
Note: spinner-click can be used with other directives (e.g. ng-class in this example).
As you can see, I set $scope.alerts in the directive using a very nasty way. Can you find a better way to do this?
UPDATE: (DEMO)
I tried to use $parse like this:
var errorsModel = $parse(attrs.spinnerErrors);
errorsModel.assign(scope, errors);
and this doesn't work.
BUT, if I have spinner-errors="wrapper.alerts" rather than spinner-errors="alerts", it does work!
Is there a way to avoid using the wrapper?
I think you can do it more simply using an isolate scope.
Instead of scope: true,, you should put:
scope:{
spinnerClick:"&",
spinnerText : "#",
spinnerErrors: "="
}
And then, in your directive use scope.spinnerClick, scope.spinnerText , scope.spinnerErrors directly.
The & is used to bind function expression defined in your attribute and pass it to your directive's scope, the # will bind the text value of the attribute and the = will set a double binding with the expression passed in the attribute.
You can fine a more precise explanation here http://docs.angularjs.org/guide/directive (look at the long version), and a much clearer explanation here http://www.egghead.io/ (look at the isolate scope videos, it only takes a few minutes and makes it look so simple).
To answer your original question regarding the ugliness of
// This is ugly! Is there a better way?
var e = scope[attrs.spinnerErrors];
e.length = 0;
e.push.apply(e, errors);
You can use angular.copy to achieve the same results
angular.copy(errors, scope[attrs.spinnerErrors]);
The reason this is so ugly in your directive is due to your use of a child scope. If you did not create a child scope, or were willing to create an isolation scope this would not be a problem. You can't use $scope.alerts because
The child scope gets its own property that hides/shadows the parent
property of the same name. Your workarounds are
define objects in the parent for your model, then reference a property of that object in the child: parentObj.someProp
use $parent.parentScopeProperty (not always possible, but easier than 1. where possible)
define a function on the parent scope, and call it from the child (not always possible)
There is a detailed explanation that can be found here.
One option is to create a setter function in the controller that you can call in the directive. The reference to the function in the child scope could then be used set the value in the parent scope. Another option is to create an isolation scope and then pass in a setter function using & binding.
You had the right idea with $parse. The problem is that you're assigning the new array of errors to the child scope, which hides (but doesn't replace) the array on the parent/controller scope.
What you have to do is get a reference to the parent array and then replace the contents (like you were doing in the original version). See here.
I question the need to put the error logic within the directive. You can simply handle the error as part of the controller. Unless you absolutely need to have the html replaced and class removed before manipulating the alerts array, your code could be rewritten as:
app.directive('spinnerClick', function() {
return {
restrict: 'A',
scope: true,
link: function(scope, element, attrs) {
var originalHTML = element.html();
var spinnerHTML = "<i class='icon-refresh icon-spin'></i> " + attrs.spinnerText;
function onClickDone(){
element.html(originalHTML).removeClass('disabled');
}
element.click(function() {
if (element.is('.disabled')) {
return;
}
element.html(spinnerHTML).addClass('disabled');
scope.$apply(attrs.spinnerClick).then(onClickDone, onClickDone);
});
}
};
});
app.controller('MainCtrl', function($scope, $q, $timeout) {
$scope.alerts = ['First alert'];
$scope.people = [
{ name: 'David', active: true },
{ name: 'Layla', active: false }
];
$scope.deleteItem = function(index) {
var defer = $q.defer();
$timeout(function() {
defer.reject(["Something 'bad' happened.", "Check your logs."]);
}, 2000);
return defer.promise.then(function(){
//Success handler
}, function(error){
$scope.alerts.length = 0;
$scope.alerts.push(error);
});
};
});
I'm new to angularjs and am writing my first directive. I've got half the way there but am struggling figuring out how to pass some variables to a directive.
My directive:
app.directive('chart', function () {
return{
restrict: 'E',
link: function (scope, elem, attrs) {
var chart = null;
var opts = {};
alert(scope[attrs.chartoptions]);
var data = scope[attrs.ngModel];
scope.$watch(attrs.ngModel, function (v) {
if (!chart) {
chart = $.plot(elem, v, opts);
elem.show();
} else {
chart.setData(v);
chart.setupGrid();
chart.draw();
}
});
}
};
});
My controller:
function AdListCtrl($scope, $http, $rootScope, $compile, $routeParams, AlertboxAPI) {
//grabing ad stats
$http.get("/ads/stats/").success(function (data) {
$scope.exports = data.ads;
if ($scope.exports > 0) {
$scope.show_export = true;
} else {
$scope.show_export = false;
}
//loop over the data
var chart_data = []
var chart_data_ticks = []
for (var i = 0; i < data.recent_ads.length; i++) {
chart_data.push([0, data.recent_ads[i].ads]);
chart_data_ticks.push(data.recent_ads[i].start);
}
//setup the chart
$scope.data = [{data: chart_data,lines: {show: true, fill: true}}];
$scope.chart_options = {xaxis: {ticks: [chart_data_ticks]}};
});
}
My Html:
<div class='row-fluid' ng-controller="AdListCtrl">
<div class='span12' style='height:400px;'>
<chart ng-model='data' style='width:400px;height:300px;display:none;' chartoptions="chart_options"></chart>
{[{ chart_options }]}
</div>
</div>
I can access the $scope.data in the directive, but I can't seem to access the $scope.chart_options data.. It's definelty being set as If I echo it, it displays on the page..
Any ideas what I'm doing wrong?
UPDATE:
For some reason, with this directive, if I move the alert(scope[attrs.chartoptions]); to inside the $watch, it first alerts as "undefined", then again as the proper value, otherwise it's always undefined. Could it be related to the jquery flot library I'm using to draw the chart?
Cheers,
Ben
One problem I see is here:
scope.$watch(attrs.ngModel, function (v) {
The docs on this method are unfortunately not that clear, but the first argument to $watch, the watchExpression, needs to be an angular expression string or a function. So in your case, I believe that you need to change it to:
scope.$watch("attrs.ngModel", function (v) {
If that doesn't work, just post a jsfiddle or jsbin.com with your example.