Change a scope value in directive - javascript

Whats the best way to assign a new value through a directive? A two way databinding.
I have a fiddle here where i tried. http://jsfiddle.net/user1572526/grLfD/2/ . But it dosen't work.
My directive:
myApp.directive('highlighter', function () {
return {
restrict: 'A',
replace: true,
scope: {
activeInput: '='
},
link: function (scope, element, attrs) {
element.bind('click', function () {
scope.activeInput = attrs.setInput
})
}
}
});
And my controller:
function MyCtrl($scope) {
$scope.active = {
value : true
};
}
And my view:
<h1 highlighter active-input="active.value" set-input="false">Click me to update Value in scope: {{active}}</h1>
So what i wanna do is update the scope.active with the given attribute setInput.
Any ideas what I'm doing wrong here?

With element.bind you leave the realm of Angular, so you need to tell Angular that something had happened. You do that with the scope.$apply function:
scope.$apply(function(){
scope.activeInput = attrs.setInput;
});
here is an updated jsfiddle.

Related

AngularJS Directive Template dont execute Javascript

I have a Problem with my AngularJS Directive named "showFileBrowser". I want to use Javascript in my Template but it will not be execute in my Browser. Here is my Code:
app.directive("showFileBrowser", function() {
return {
restrict: 'E',
template: '<script>$("#searchNote").fileTree({data: scope.filedata,sortable: false,selectable: false});</script>'
}
});
Someone know why I cant execute Javascript in a Directive or know how to do it?
You dont need to use the id, use 'element' directly.
app.directive("showFileBrowser", function() {
return {
restrict: 'E',
link: function($scope, element, attrs) {
$(element).fileTree({data: scope.filedata,sortable: false,selectable: false});
}
}
});
It works fine if I return a function, but i prefer return an Object.
app.directive("showFileBrowser", function() {
return function(scope, element, attr) {
$(element).fileTree({data: scope.filedata,sortable: false,selectable: false});
}
});

Is there any way to do this with a directive?

I have no problem getting the $scope function referenced in the directive attribute to run. The problem is I need to capture the element that was touched in the directive and pass it to the function in the attribute. I haven't figure out how to do that. Isolating the scope in the directives will throw an error because there's 2 directives trying to act on the same element.
With the below code I am getting $event as undefined and an error of course. Any way to achieve what I want? Or maybe there's a better way?
<li class="list-group-item row" data-touchstart="darkenBackground($event)" data-touchend="lightenBackground($event)">
..
angular.module('myNotes')
.directive('touchstart', [function () {
return {
restrict: 'A',
link: function (scope, elem, attrs) {
elem.bind('touchstart', function(e) {
e.preventDefault();
scope.$apply(attrs.touchstart);
});
}
};
}]);
angular.module('myNotes')
.controller('notesCtrl', ['$scope', 'Site',
function($scope, Site) {
Site.setTitle('My Notes');
Site.setActiveNavLink('myNotes');
$scope.darkenBackground = function($event) {
angular.element($event.currentTarget)
.css('background-color', '#eee');
};
$scope.lightenBackground = function($event) {
angular.element($event.currentTarget)
.css('background-color', '#fff');
}
}]);
Here's the issue:
scope.$apply(attrs.touchstart);
When you call scope.$apply directly on an angular expression (which is what attrs.touchstart is), it will automatically evaluate that expression against scope, so scope.$event (which is undefined) is passed to the callback.
To pass the event to the callback, you can use the second parameter (called "locals") of scope.$eval to temporarily include an $event property on the scope while evaluating the expression.
return {
restrict: 'A',
link: function (scope, elem, attrs) {
elem.bind('touchstart', function (e) {
e.preventDefault();
scope.$apply(function () {
scope.$eval(attrs.touchstart, {$event: e});
});
});
}
};
I got it working like so, but I'm not sure if it's the best/proper approach:
<li class="list-group-item row" data-touchstart="darkenBackground(event)">
...
angular.module('myNotes')
.directive('touchstart', [function () {
return {
restrict: 'A',
scope: {
touchstart: '&'
},
link: function (scope, elem, attrs) {
elem.bind('touchstart', function(e) {
e.preventDefault();
scope.$apply(scope.touchstart({event: e}));
});
}
};
}]);
angular.module('myNotes')
.controller('notesCtrl', ['$scope', 'Site',
function($scope, Site) {
Site.setTitle('My Notes');
Site.setActiveNavLink('myNotes');
$scope.darkenBackground = function(event) {
var elem = angular.element(event.currentTarget);
elem.css('background-color', '#eee');
elem.bind('touchend', function (e) {
$scope.lightenBackground(e);
})
};
$scope.lightenBackground = function(event) {
angular.element(event.currentTarget)
.css('background-color', '#fff');
};
}]);

change controller $scope from a directive

I have a controller:
function myController($scope) {
$scope.clicked = false;
}
and a directive:
function myDirective() {
return {
restrict: 'E',
link: function(scope, elem, attrs) {
elem.bind('click', function() {
// need to update controller $scope.clicked value
});
},
template: '<div>click me</div>';
replace: true;
}
}
and I´m using it like this:
<div ng-controller="myController">
<my-directive></my-directive>
</div>
How can I change the controller value of $scope.clicked ?
thanks!
As you don't use isolated scope in your directive, you can use scope.$parent.clicked to access the parent scope property.
link: function(scope, elem, attrs) {
elem.bind('click', function() {
scope.$parent.clicked = ...
});
},
I would not recommend using scope.$parent to update or access the parent scope values, you can two way bind the controller variable that needs to be updated into your directive, so your directive becomes:
function myDirective() {
return {
restrict: 'E',
scope: {
clicked: '='
},
link: function(scope, elem, attrs) {
elem.bind('click', function() {
// need to update controller $scope.clicked value
$scope.clicked = !$scope.clicked;
});
},
template: '<div>click me</div>';
replace: true;
}
}
now pass this clicked from parent:
<div ng-controller="myController as parentVm">
<my-directive clicked="parentVm.clicked"></my-directive>
</div>
function myController() {
var parentVm = this;
parentVm.clicked = false;
}
I would recommend reading up on using controllerAs syntax for your controller as that would really solidify the concept of using two way binding here.
I like to use $scope.$emit for such purposes. It allows to send data from directive to the controller.
You should create custom listener in your controller:
$scope.$on('cliked-from-directive', function(event, data){
console.log(data)
})
As you can see, now you have full access to your controller scope and you can do whatever you want. And in your directive just to use scope.$emit
link: function(scope, elem, attrs) {
elem.bind('click', function() {
scope.$emit('cliked-from-directive', {a:10})
});
Here I've created jsfiddle for you

Custom directive - two way binding not working

I am trying to write a simple custom directive in Angular that turns a tag into a toggle button (similar to a checkbox). The code I have written so far updates the internal variable (isolated scope) but the two way binding doesn't seem to work. When I click the button, the button toggles (the css class is appearing and disappearing) but myVariable is not updating.
Any help much appreciated!
Usage
<button toggle-button="myVariable">My Button</button>
Directive code
( function() {
var directive = function () {
return {
restrict: 'A',
scope: {
toggleButton: '=checked'
},
link: function( $scope, element, attrs ) {
$scope.$watch('checked', function(newVal, oldVal) {
newVal ? element.addClass ('on') : element.removeClass('on');
});
element.bind('click', function() {
$scope.checked = !$scope.checked;
$scope.$apply();
});
}
};
};
angular.module('myApp')
.directive('toggleButton', directive );
}());
just replace
scope: {
toggleButton: '=checked'
}
to
scope: {
checked: '=toggleButton'
}
Your directive scope is looking for an attribute that doesn't exist.
Try changing:
scope: {
toggleButton: '=checked'
},
To
scope: {
toggleButton: '='
},
The difference is that =checked would look for the attribute checked whereas = will use the same attribute as the property name in the scope object
Will also need to change the $watch but you could get rid of it and use ng-class
As charlietfl said, you don't need that checked variable. You are making changes to it instead of the external variable.
Here is a fixed version:
angular.module('components', [])
.directive('toggleButton', function () {
return {
restrict: 'A',
scope:{
toggleButton:'='
},
link: function($scope, $element, $attrs) {
$scope.$watch('toggleButton', function(newVal) {
newVal ? $element.addClass ('on') : $element.removeClass('on');
});
$element.bind('click', function() {
$scope.toggleButton = !$scope.toggleButton;
$scope.$apply();
});
}
}
})
angular.module('HelloApp', ['components'])
http://jsfiddle.net/b3b3qkug/1/

Directive two way binding

I'm relative new to AngularJS and trying to create a directive for add some buttons. I'm trying to modify the controller scope from inside the directive but I can't get it to work. Here is an example of my app
app.controller('invoiceManagementController', ['$scope', function ($scope) {
$scope.gridViewOptions = {
isFilterShown: false,
isCompact: false
};
}]);
app.directive('buttons', function () {
return {
restrict: 'A',
template: '<button type="button" data-button="search" title="Filter"><i class="glyphicon glyphicon-search"></i></button>',
scope: {
gridViewOptions: '='
},
transclude: true,
link: function (scope, element, attr, ctrl, transclude) {
element.find("button[data-button='search']").bind('click', function (evt) {
// Set the property to the opposite value
scope.gridViewOptions.isFilterShown = !scope.gridViewOptions.isFilterShown
transclude(scope.$parent, function (clone, scope) {
element.append(clone);
});
});
}
};
});
My HTML like following
{{ gridViewOptions.isFilterShown }}
<div data-buttons="buttons" data-grid-view-options="gridViewOptions"></div>
The scope inside the directive does change but is like isolated, I did try paying with the scope property and transclude but I'm probably missing something, would appreciate some light here
When you modify scope inside of your directive's link function, you are modifying your directive's isolated scope (because that is what you have set up). To modify the parent scope, you can put the scope assignment inside of your transclude function:
transclude(scope.$parent, function (clone, scope) {
// Set the property to the opposite value
scope.gridViewOptions.isFilterShown = !scope.gridViewOptions.isFilterShown
element.append(clone);
});
Ok finally found a solution for this after some more research today. Not sure if the best solution, but this works so good for now.
app.controller('invoiceManagementController', ['$scope', function ($scope) {
$scope.gridViewOptions = {
isFilterShown: false,
isCompact: false
};
}]);
app.directive('buttons', function () {
return {
restrict: 'A',
template: '<button type="button" data-button="search" data-ng-class="gridViewOptions.isFilterShown ? \'active\' : ''" title="Filter"><i class="glyphicon glyphicon-search"></i></button>',
scope: {
gridViewOptions: '='
},
link: function (scope, element, attr, ctrl, transclude) {
element.find("button[data-button='search']").bind('click', function (evt) {
scope.$apply(function () {
// Set the property to the opposite value
scope.gridViewOptions.isFilterShown = !scope.gridViewOptions.isFilterShown;
});
});
}
};
});

Categories

Resources