I'm building an AngularJS app and would like some pointers on the way I'm structuring my Angular code.
For this module, I'd like to be able to click a button that will add a Div to a specific "parent div" that has JQuery UI draggable and resizable interactions attached to it.
They could add more than one Div that is draggable and resizable.
Right now, I have one directive that generates the whole Div. The event callback for when resizing is stopped is placed in the Button that has the directive attached to it.
Directive code gist:
var app = angular.module('myApp', []);
app.controller('IpadCtrl', function($scope) {
$scope.someFunc = function () {
console.log('resized');
alert('resized');
};
$scope.test = 'sup';
});
app.directive('addImageCont', function () {
return {
restrict: 'A',
link: function (scope, element, attributes) {
console.log(attributes);
element.on("click", function() {
var div = $("<div />").css({
'width': '100px',
'height': '100px',
'background-color': 'purple'
});
div.draggable();
div.resizable();
div.on('resizestop', function(event, ui) {
console.log(ui);
var cBack = scope[attributes.callback];
cBack();
});
element.parent().find('.ipad').append(div);
})
}
};
});
JSFiddle that has an example:
http://jsfiddle.net/GA2M8/3/
Is this the right way to handle callback events? I'd like to be able to have a callback for when resizing begins too.
I know there's a better way to handle this.
It has been suggested to me that I break up my directives.
One directive that just creates the Div and appends it to the Dom.
Another directive that attaches the JQuery UI draggable interaction to it.
Another directive that attaches the JQuery UI resizable interaction to it.
I'm not sure how to chain directives if that's how I should proceed.
Related
Is there a way to dynamically Render a template from angularjs directive via eventRender Fullcalendar ?
What I want to achieve is something like below:
eventRender: function(event, element, view) {
var template = '<my-directive></my-directive>';
element.find('.fc-event').append(template);
scope.$apply();
}
Following Documentation
The eventRender callback function can modify element.
For example, it can change its appearance via jQuery’s .css().
On eventRender insert the css classes with
eventRender: function (event, element) {
element.addClass(event.class)
}
Documentation on this topic is not broad in the subject.
It doesn't give any example of changing the template via Angularjs directives
and doesn't tell if there is any limitaion for template
to keep it treated by FullCalendar as event.
MyCodePen
EDITED_Code_Pen
1- add $compile to your controller
2- in eventRender make as bellow :
eventRender: function(event, element, view) {
var compiled = $compile('<div your-directive></div>')($scope);
element.find('.fc-content').replaceWith(compiled);
},
workingCodePen
I am using ui-router's autoScroll to scroll down to a div (ui-view) when page loads/route change. It currently does so. Is there any way to offset autoScroll? I need to leave 100px above the element visible for menu and am confused on how to acchomplish this.
app.directive('scrollTop', function($uiViewScroll) {
var linker = function (scope, element, attr, $elm) {
$uiViewScroll(element);
};
return {
restrict: 'A',
link: linker
}
});
HTML:
<ui-view [autoscroll="true"]/>
autoscroll directive allows you to set the scroll behavior of the browser window when a view is populated.
By default, $anchorScroll is overridden by ui-router's custom scroll service, $uiViewScroll. This custom service let's you scroll ui-view elements into view when they are populated during a state activation.
Try to decorate the default $uiViewScroll service, overriding the default behaviour.
app.config(function ($provide) {
$provide.decorator('$uiViewScroll', function ($delegate) {
return function (uiViewElement) {
// var top = uiViewElement.getBoundingClientRect().top;
// window.scrollTo(0, (top - 30));
// Or some other custom behaviour...
};
});
});
See http://corpus.hubwiz.com/2/angularjs/22290570.html
could you please tell me how to get event when user scroll to top .Actually I am using ng-repeat in my example .I want to get event when user scroll to bottom and scroll to top .I have one div in which I used ng-repeat can we get event of top when user move to top after scrolling.Actually I need to show alert when user scroll to bottom and top of div in angular .here is my code
<body ng-controller="MyController">
<div style="width:90%;height:150px;border:1px solid red;overflow:auto">
<div ng-repeat="n in name">{{n.name}}</div>
</div>
You could put directives on your scrollable div that listen to the scroll event and check for the top or bottom being reached.
So, using your HTML, your div would look like this:
<div exec-on-scroll-to-top="ctrl.handleScrollToTop"
exec-on-scroll-to-bottom="ctrl.handleScrollToBottom"
style="width:90%;height:150px;border:1px solid red;overflow:auto">
<div ng-repeat="n in name">{{n.name}}</div>
</div>
With new directives exec-on-scroll-to-top and exec-on-scroll-to-bottom added. Each specifies a function in your controller that should execute when the particular event the directive is checking for occurs.
exec-on-scroll-to-top would look like this, just checking for the scrollable div's scrollTop property to be 0:
myapp.directive('execOnScrollToTop', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var fn = scope.$eval(attrs.execOnScrollToTop);
element.on('scroll', function (e) {
if (!e.target.scrollTop) {
console.log("scrolled to top...");
scope.$apply(fn);
}
});
}
};
});
And exec-on-scroll-to-bottom would look like this (keeping in mind that an element is fully scrolled when its (scrollHeight - scrollTop) === clientHeight):
myapp.directive('execOnScrollToBottom', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var fn = scope.$eval(attrs.execOnScrollToBottom),
clientHeight = element[0].clientHeight;
element.on('scroll', function (e) {
var el = e.target;
if ((el.scrollHeight - el.scrollTop) === clientHeight) { // fully scrolled
console.log("scrolled to bottom...");
scope.$apply(fn);
}
});
}
};
});
Here's a plunk. Open the console to see messages getting logged when the scrolling reaches the top or bottom.
This is a non angular way, but you can wrap it up in a directive which also allows reuse:
Use Javascript event listener:
div.addEventListener('scroll', function(){
if(this.scrollTop===0)
//do your stuff
});
Make sure to use $apply if you make any changes to the scope variables inside this listener.
I have a simple directive along these lines:
angular.module('application').directive( 'sdTitle', sdTitleDirective );
sdTitleDirective.$inject = ['$animate'];
function sdTitleDirective($animate) {
var directive = {
template: '<div class="title-container">some content</div>',
link: link,
replace: true
};
return directive;
function link(scope, element, attrs) {
element.bind("click", function() {
$animate.leave(element);
});
}
}
And an animation similar to this:
angular.module('application').animation('.title-container', titleAnimation);
function titleAnimation() {
return {
leave: leaveAnimation
};
function leaveAnimation(element, done) {
console.log('animate leave', element);
element.hide().fadeOut(800, done);
}
}
I can't seem to get the leaveAnimation to actually fire when the directive's element is clicked. I must be missing something with how $animate works or how javascript animations are called, but I'm at a loss.
How do I correctly use the $animate service's animation methods within a directive and with javascript rather than CSS3 animations?
DOM event handlers attached by for example addEventListener() or the jqLite/jQuery methods bind and on are executed outside of the current Angular context and will not trigger the digest loop.
You will have to do it manually by using for example $apply:
$apply() is used to execute an expression in angular from outside of
the angular framework. (For example from browser DOM events,
setTimeout, XHR or third party libraries). Because we are calling into
the angular framework we need to perform proper scope life cycle of
exception handling, executing watches.
Example:
element.bind("click", function() {
scope.$apply(function() {
$animate.leave(element);
});
});
Demo: http://plnkr.co/edit/OepqBsCW25Lf2qCenbtK?p=preview
It's not apparent from your posted code if you aren't, but you need to use the ngAnimate module.
Note that in the demo I also changed:
element.hide().fadeOut(800, done);
To:
element.fadeOut(800, done);
Given the following use-case:
I use D3js to render objects which are managed by AngularJS. I would like to add interactivity to the D3 chart. When clicking on a svg element I would like to have a kind of popup menu allowing to modify the object properties. These properties are required by AngularJS but are not rendered by D3.
The D3-Angular integration is derived from http://bl.ocks.org/biovisualize/5372077 which uses a closure.
Current implementation:
As of today I am using the $modal service from angular-ui bootstrap to create the popup menu. From a functionnality point of view it works pretty well:
When clicking on a svg element, D3 dispatches an event
That event is catched by Angular which calls the $modal service
Within the modal window I modify the object properties
However I am not satisfied with the rendering. I would like the popup menu to look like a popover. It should be placed close to the svg element which was clicked.
As far as I understand, I have two options:
Continue to use the $modal service and modify its look. What approach should be taken? Using the windowClass option?
Stop using the $modal service and start hacking on the popover directive. The problem is that I do not think it is possible
to add such a directive to an svg element. The solution would be to
create a popover service close to the $modal service.
Which option should be chosen? and how to implement it?
EDIT:
Working plunker using a custom my-popover directive:
http://plnkr.co/edit/5KYvxi?p=preview
It is possible to add a directives to code generated by d3, only thing you need to ensure is that you call the $compile service on the content after it has been rendered.
For the given example, it would look something like this:
.directive('barChart', function($compile){ // inject $compile
var chart = d3.custom.barChart();
return {
restrict: 'E',
replace: true,
template: '<div class="chart"></div>',
scope:{
height: '=height',
data: '=data',
hovered: '&hovered'
},
link: function(scope, element, attrs) {
var chartEl = d3.select(element[0]);
chart.on('customHover', function(d, i){
scope.hovered({args:d});
});
scope.$watch('data', function (newVal, oldVal) {
chartEl.datum(newVal).call(chart);
$compile(element.contents())(scope); // <-- call to $compile
});
scope.$watch('height', function(d, i){
chartEl.call(chart.height(scope.height));
$compile(element.contents())(scope); // <-- call to $compile
})
}
}
And in the d3's drawing function:
bars.enter().append('rect')
.classed('bar', true)
.attr('myPopover', 'Text to show') // <-- Adding an attribute here.
.attr({x: chartW,
width: barW,
y: function(d, i) { return y1(d); },
height: function(d, i) { return chartH - y1(d); }
})
.on('mouseover', dispatch.customHover);
Demo