everyone ! I've this on my directive template...
<div class="priceSlider" ui-slider="slider.options" min="{{min}}" max="{{max}}"
step="0.01" use-decimals ng-model="sliderVal"><div>{{currency}} {{sliderVal[0]}} -
{{currency}} {{sliderVal[1]}}</div>
..and I've this on my JS for the directive
angular
.module('app.directives.categoryHead', [])
.directive('categoryHead', function() {
return {
restrict : 'E',
controller: function($scope){
$scope.sliderVal = [$scope.min , $scope.max];
$scope.slider = {
'options': {
range: true,
start: function (event, ui) { console.log('Event: Slider start - set with slider options', event); },
stop: function (event, ui) { console.log('Event: Slider stop - set with slider options', event); }
}
}
},
scope: {
language : "=",
currency : "=",
breadcrumb : "=",
min : "=",
max : "="
},
templateUrl: "templates/directives/categoryHead.html"
}
});
...and on my route template, I've this...
<category-head breadcrumb="breadCrumb" min="categoryList.minPrice"
max="categoryList.maxPrice" language="webshop.language"
currency="webshop.culture.currency"></category-head>
So, basically I've a slider...that's fires the events starts and stop - And this works really fine.
But I'd like to handle the event not inside the directive, but on the controller of the route template.
How can I "transfer" the event from the directive to the template controller ? I just need to fire a "something changed" and send a notification of the new values.
ty !
There are probably lots of different ways to do this. One option is to make your directive take event handlers that you have defined in your controller, like this:
<your-directive on-start="startHandler()" on-stop="stopHandler()></your-directive>
Then, in your directive config:
scope: {
onStart: '&',
onStop: '&'
}
Then, add something like this to your start/stop methods within the directive.
$scope.slider = {
'options': {
range: true,
start: function (event, ui) { if (onStart) onStart(event); /* other stuff */ },
stop: function (event, ui) { if (onStop) onStop(event); /* other stuff */ }
}
}
Related
I am using jquery steps in my angularjs app. I have used a custom directive to init the jquery plugin. Now I need to validate all input once the finish button is clicked on the final steps of the form. In order to do that I know there is a option which needs to be set called onFinished. Now how do I call my controller method in this section?
app.directive('step', [function() {
return {
restrict: 'EA',
scope: {
stepChanging: '='
},
compile: function(element, attr) {
element.steps({
labels: {finish: "SUBMIT"},
headerTag: "h3",
bodyTag: "section",
transitionEffect: "slideLeft",
stepsOrientation: "vertical",
onFinished: function (event, currentIndex) {
console.log("submit button has been clicked");
$scope.validator(); //problem here
}
});
return {
//pre-link
pre:function() {},
//post-link
post: function(scope, element) {
//element.on('stepChanging', scope.stepChanging);
}
}
}
};
}])
you directive is isolated scope , it cannot access controller scope , if u want to pass functions you can use & in scope object of your directive like below
scope:{
validator:'&'
}
and in your directive pass this function as below
<step validator='validator'/>
To pass events from a directive with isolate scope to parent controller, use expression & binding.
During the directive compile phase, there is no scope for a function use.
Move the function to the postLink:
app.directive('step', function() {
return {
restrict: 'EA',
scope: {
//stepChanging: '=',
stepChanging: '<',
//USE expression binding
validator: '&'
},
//compile: function(element, attr) {
link: function postLink(scope,element,attrs) {
element.steps({
labels: {finish: "SUBMIT"},
headerTag: "h3",
bodyTag: "section",
transitionEffect: "slideLeft",
stepsOrientation: "vertical",
onFinished: function (event, currentIndex) {
console.log("submit button has been clicked");
//$scope.validator(); //problem here
scope.validator();
}
);
}
/*
return {
//pre-link
pre:function() {},
//post-link
post: function(scope, element) {
//element.on('stepChanging', scope.stepChanging);
}
}*/
}
};
});
Usage:
<step step-changing="vm.changing" validator="validator()">
</step>
Moving forward, avoid using two-way = binding. Instead use one-way < binding. It is more efficient and provides a better path for migrating to Angular 2+.
Also avoid closing element directives with /> it causes problems with some browsers. Instead use a closing tag </step>.
I am using jCarousel for image thumbnail slider. but previously I was using directive for the same but now I changed my code to component. but now I am not able to use that link function and watch reload in component. because I am using first time component in agularjs.
//Previous code
directive('jblogcarousel', function() {
return {
restrict: 'A',
replace: true,
transclude: true,
scope: {
jitems: "="
},
templateUrl: '/templates/blog-carousel.html',
link: function link(scope, element, attrs) {
var container = $(element);
var carousel = container.find('.jcarousel');
carousel.jcarousel({
wrap: 'circular'
});
scope.$watch(attrs.jitems, function (value) {
carousel.jcarousel('reload');
});
container.find('.jcarousel-control-prev')
.jcarouselControl({
target: '-=1'
});
container.find('.jcarousel-control-next')
.jcarouselControl({
target: '+=1'
});
}
};
});
//Current code
.component('jCarousel', {
bindings: {
jitems: '='
},
templateUrl: '/templates/carousel.html'
})
From what I understood, in Angular 1.5 components bindings will bind the value to the controller.
So you can add a controller (with a $watch inside):
// bindings: { ... },
// templateUrl: '...',
controller: function ($scope) {
var vm = this;
console.log(vm.jitems); // vm.jitems should exist and be bound the value you passed to the component from the outside
// you should be able to watch this value like this
$scope.$watch(
function () { return vm.jitems; },
function (newValue) { console.log(newValue); }
);
}
Also, with components, you should in most situations use one way binding '<' instead of two-way binding '=', and use functions/events (binding '&') for the other direction.
I want to execute a function after the view gets loaded/ after entering to that view.
My directory is as follows:
app.directive('bonFormViewerFrame', function ($formStore) {
return {
restrict: 'E', //bind to element tag name
replace: true, //replace the entire markup with the template
templateUrl: 'ui/controls/bon-form-viewer-frame.html',
scope: {
form: '=' //specifies the item to be displayed.
},
controller: ['$scope', function ($scope) {
$scope.formContent = ($scope.form != null) ? $scope.form.Content : "";
$scope.$on("$ionicView.beforeEnter", function (event, data) {
// handle event
console.log("State Params: ", data.stateParams);
});
$scope.$on("$ionicView.enter", function (event, data) {
// handle event
console.log("State Params: ", data.stateParams);
});
$scope.$on("$ionicView.afterEnter", function (event, data) {
// handle event
console.log("State Params: ", data.stateParams);
});
}]
};
});
None of the above $ionicView events are firing. Can anyone help me out? Or what i'm doing wrong here?
None of the above $ionicView events are firing.
Well, if you will check ionic.bundle.js you can find following rows:
enteringScope.$emit('$ionicView.enter', enteringData);
enteringScope.$broadcast('$ionicParentView.enter', enteringData);
For ref:
$broadcast -- dispatches the event downwards to all child scopes,
$emit -- dispatches the event upwards through the scope hierarchy.
$ionicView uses $emit
The problem is, your directive has different scope - the child of your main controller
So you need $ionicParentView a.e.:
$scope.$on("$ionicParentView.beforeEnter", function (event, data) {
// ...
});
Demo Plunker
Results:
~+~+~ CTRL~+~+~
CTRL: on beforeEnter
~+~+~ DIR ~+~+~
CTRL: on enter
DIR: on enter
CTRL: on afterEnter
DIR: on afterEnte
However if your directive load late and ionic broadcasts these events before your directive controller regestered to events - you have nothing to do with it
I'm trying to evaluated a function on the parent controller then send it into a directive. I need the values to be watched by the digest loop and updated when a user updates them.
I've worked through some original issues, but am having trouble with getting the bindings to update.
I have a Controller with an object and a function that checks if an object has values, it returns true or false:
this.foo = {
obj1: {
name: '',
time: 'time2'
},
obj2: {
name: 'name2',
time: 'time2'
}
};
this.isPaneComplete = function(tab) {
var complete = true;
var tab2 = tab.tab;
for (var prop in tab2) {
if (tab2.hasOwnProperty(prop)) {
complete = !!tab2[prop] && complete;
}
}
return complete;
};
I have a directive called MyPane with this scope :
scope: {
completed : '&myPaneComplete'
},
This is my template:
<my-pane my-pane-complete="gigEditCtrl.isPaneComplete({tab : gigEditCtrl.foo.obj1})">
<input type="text" placeholder="2014-12-31" ng-model="gigEditCtrl.foo.obj1.name">
<input type="text" placeholder="2014-12-31" ng-model="gigEditCtrl.foo.obj1.time">
When running the following console.log I get TRUE or FAlSE in my Directive
link: function(scope, element, attrs, tabsCtrl) {
console.log(scope.completed());
},
This all works great. However, when I update the values in the input boxes the controller function isn't run again and the console.log isn't fired. Thoughts?
the solution for your issue is to use $watch in your directive and it will look something like this
testApp.directive('myDir', function(){
return{
scope:{
test: '&'
},
link: function(scope, el, attrs){
scope.$watch(scope.test, function(newValue){
console.log('from dir name = '+ newValue);
});
}
};
});
basically you will you will use $watch to watch changes in the return value of your function.
I've setup a working demo for you here http://plnkr.co/edit/Gk2tILTql8NW1QkvVRRk?p=preview
I am trying to load a 'class' directive using ng-class. but my directive is never loaded when i do that. The directive is a multipurpose directive, and I don't want to create an isolated scope on this. it will be loaded only when required, based on ng-class conditions hence not using attribute or element directive. has anyone tried doing this and succeeded?
this directive is called using <div ng-class="someClass {{tooltip: enabled}}"></div>
here enabled is a scope variable.
app.directive('tooltip', ['$timeout', '$location', '$rootScope', function (timer, $location, $rootScope) {
return {
restrict: 'C',
transclude: true,
link: function (scope, element) {
var printContent = function () {
/* uses the content of .tooltip-content if it is a complex html tooltip,
otherwise
you can use the title attribute for plaintext tooltips
*/
var tooltipContent = $(element).find('.tooltip-content').html();
if (!tooltipContent) {
tooltipContent = $(element).attr('title');
}
$(element).tooltip({
content: tooltipContent,
items: "img, a, span, button, div",
tooltipClass: "tooltip",
position: { my: "left+30 top", at: "right top", collision: "flipfit" },
show: { effect: "fadeIn", duration: "fast" },
hide: { effect: "fadeOut", duration: "fast" },
open: function (event, ui) { $rootScope.tooltipElement = event.target; }
});
};
timer(printContent, 0);
}
};
}]);
Interesting issue. It seems that you don't want to use the ng-class directive since that doesn't recompile the content after adding the class. You'll likely want to create your own dynamic-class directive that actually recompiles when the value is true:
app.directive('dynamicClass', function($compile) {
return {
scope: {
dynamicClassWhen: '=',
dynamicClass: '='
},
link: function(scope, elt, attrs) {
scope.$watch('dynamicClassWhen', function(val) {
if (val) {
console.log(val);
elt.addClass(scope.dynamicClass);
$compile(elt)(scope);
}
});
}
};
});
You may need to modify this for the ability to remove the class and depending on if the $compile is sufficient for you or if you need to further manipulate the html, but this seems to be the right track for you. I made a fiddle with this in action.
Hope this helps!