AngularJS animate dynamic margin - angularjs

I have an element that appears when the user clicks a button elsewhere on the screen. The element appears to come out of the top of the screen. The element by default needs to be tucked out of view above the screen, so I will have a margin-top style that is based on the height of the element (and will be a negative value). This cannot be hardcoded in css because the element height may vary. When I click the button, I want the element margin-top to change to 0 and I want a transition animation.
The sample shown on angularJS documentation is for adding a removing a class. This would work fine if I knew the values to be set and could code them in CSS, however I cannot. What is the correct way to solve this?
The code below works for displaying and hiding my element using a margin but there is no animation. How do I trigger an animation here when the margin changes?
https://docs.angularjs.org/guide/animations
Quote Total: {{salesPriceTotal + taxesTotal - tradeInsTotal | currency}}
<div class="totals" ng-style="setTopMargin()">
// totals stuff here.
</div>
$scope.setTopMargin = function() {
return {
marginTop: $scope.marginTop
}
};
$scope.$watch('showTotals', function() {
var margin = $scope.showTotals ? 10 : -160 + $scope.modelTotals.length * -200;
$scope.marginTop = margin.toString() + 'px';
});
I added the following code per a suggested solution, but this code is never hit.
myApp.animation('.totals', function () {
return {
move: function (element, done) {
element.css('opacity', 0);
jQuery(element).animate({
opacity: 1
}, done);
// optional onDone or onCancel callback
// function to handle any post-animation
// cleanup operations
return function (isCancelled) {
if (isCancelled) {
jQuery(element).stop();
}
}
},
}
});

As the documentation explains: "The same approach to animation can be used using JavaScript code (jQuery is used within to perform animations)".
So you basically needs to use animate() from jQuery to do what you want.

Related

Prevent angular-ui ui-scroll from loading in hidden div

I have an angular-ui ui-scroll directive (1.3.0) living in a div that is hidden by default with an ng-show. When it is hidden, ui-scroll just keeps loading data from the datasource even though the ui-scroll-viewport's height is set. If the div is shown, it behaves correctly. So, for now my div is 1px wide :/
I'm sure I can solve this with an ng-if to dynamically add this to the DOM. However, I wanted the div to hide/show responsively - so driven off of css.
Any suggestions on how to have ui-scroll only load 1 page from the buffer when hidden? thank you.
You can try to manage it through the datasource implementation:
$scope.datasource.get = function(index, count, success) {
// prevent data getting while ui-scroll is invisible
if (!$scope.isScrollerVisible) {
return; // or let him request for 1 pack and prevent others, any logic
}
// get required data and pass it into success callback
var result = [];
for (var i = index; i <= index + count - 1; i++) {
result.push({text: "item #" + i});
}
success(result);
}
Also you probably need to initiate scroll reloading once (adapter doc):
$scope.$watch('isScrollerVisible', function(value) {
if(value) {
$scope.datasourceAdapter.reload()
}
});

Problems related to a resizable component

I am writing a component that allows the width of it's child element to change if you click on its right border and drag it.
I have a few problems however. First off, it's awkward to drag the div element, because if the mouse enters the other element to the right while dragging, the dragging state is lost and bugs out.
Also, I currently show the resize cursor when the point is within 5 pixels of the right border, which works fine when inside the resizable div. However, if you approach the border from the right (mouse inside other div), you cannot select it, even though you're within 5 pixels.
Another problem is that when I drag the mouse and resize the div, the mouse selects the text it drags over.
Lastly, because the element has to rerender each time it's width is changed, I've noticed that the performance is not always smooth.
Any advice on how to mitigate these problems?
Resizable = React.createClass({
propTypes: {
id : React.PropTypes.string,
class : React.PropTypes.string,
width : React.PropTypes.number,
onResize : React.PropTypes.func,
onAction : React.PropTypes.func,
},
getInitialState: function() {
return {
showResizeCursor : false,
canResize : false,
};
},
getDefaultProps: function() {
return {
};
},
_handleMouseMove: function(event) {
var node = React.findDOMNode(this);
var offsets = node.getBoundingClientRect();
var divLeft = offsets.left;
var divRight = offsets.right;
var mouseX = event.clientX;
var maxWidth = this.props.maxWidth || this.props.width;
var minWidth = this.props.minWidth || this.props.width;
var newWidth = mouseX - divLeft + 200;
var isWithinBounds = newWidth <= maxWidth && newWidth >= minWidth;
if (this.state.canResize && isWithinBounds) {
this.props.onResize(newWidth);
}
var difference = Math.abs(divRight - mouseX);
if (difference < 4) {
return this.setState({ showResizeCursor: true });
}
if (this.state.showResizeCursor) {
this.setState({ showResizeCursor: false });
}
},
_handleMouseUp: function() {
this.setState({ canResize: false });
},
_handleMouseDown: function() {
if (this.state.showResizeCursor) {
this.setState({ canResize: true });
}
},
render: function() {
var style = {
width : this.state.width,
};
if (this.state.showResizeCursor) { style.cursor = 'col-resize'; }
return (
<div id={this.props.id}
style ={style}
className ={this.props.class}
onMouseDown ={this._handleMouseDown}
onMouseUp ={this._handleMouseUp}
onMouseMove ={this._handleMouseMove}
onMouseLeave={this._handleMouseUp}
>
{this.props.children}
</div>
);
}
});
Example usage:
render: function() {
...
return (
<Wrapper>
<Resizable
id = {'list-view'}
width = {this.state.listViewWidth}
maxWidth = {this.state.listViewMaxWidth}
minWidth = {this.state.listViewMinWidth}
onResize = {this._handleListViewResize}
>
{first_column_that_should_be_resizable}
</Resizable>
{second_column_not_resizeable}
There are many different concerns here...
First off, it's awkward to drag the div element, because if the mouse enters the other element to the right while dragging, the dragging state is lost and bugs out.
This is a very common issue when you start coding your first drag&drop alike behavior. You should not listen the mousedown, mousemove and mouseup events on the same element, you should only listen the mousedown event and in that handler start listening the other two but on the body of the document. This way, you have a global handler and you will not have problems with the mouse getting over other elements.
Also, I currently show the resize cursor when the point is within 5 pixels of the right border, which works fine when inside the resizable div. However, if you approach the border from the right (mouse inside other div), you cannot select it, even though you're within 5 pixels.
I would suggest you to use only CSS for this. Is what it is for :)
Another problem is that when I drag the mouse and resize the div, the mouse selects the text it drags over.
Yep, just CSS. Once your mousedown handler is executed add a special CSS class to your element and put something like this in your CSS.
.disable-select {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
Lastly, because the element has to rerender each time it's width is changed, I've noticed that the performance is not always smooth.
I don't think React is your best option in here. I would just add this behavior using jQuery and the lifecycle methods like componentDidMount. This way, you can resize the div using plain jQuery (on every mouse move) and then just apply the final state (that is, the final size) to your component on the mouseup handler.

Changing list-item css class using ng-class when mousing over Leaflet Map Markers

I've got a doozy of an ng-class problem.
So I have an app with a map/markers on the right and a list-item menu on the left with info about the markers (like Yelp or Foursquare).
With some of my beginner hacking, I got the hover events to sort of work:
But it's odd, the list-item (pink background on hover) only works when I mouseout of the marker. I'm trying to set it up so that when you mouse over the marker, the appropriate list-item's background changes and when you mouseout, it goes back to white. Most of the other ng-class examples/questions I read through seem to work quite differently (they're going for a different functionality).
Ok, to the code:
HTML
<div class="col-md-6" id="leftCol">
<div class="list-group" ng-controller="ShowsCtrl">
<div class="nav nav-stacked list-group-item" id="sidebar" ng-repeat="(key, show) in shows.features" ng-mouseover="menuMouse(show)" ng-mouseout="menuMouseout(show)" ng-class="{hover: $index == hoveritem}">
The key part there is the ng-class="{hover: $index == hoveritem}"
Now I'll show you where hoveritem comes from
Controller
$scope.hoveritem = {};
function pointMouseover(leafletEvent) {
var layer = leafletEvent.target;
//console.log(layer.feature.properties.id);
layer.setIcon(mouseoverMarker);
$scope.hoveritem = layer.feature.properties.id;
console.log($scope.hoveritem);
}
function pointMouseout(leafletEvent) {
var layer = leafletEvent.target;
layer.setIcon(defaultMarker);
}
$scope.menuMouse = function(show){
var layer = layers[show.properties.id];
//console.log(layer);
layer.setIcon(mouseoverMarker);
}
$scope.menuMouseout = function(show){
var layer = layers[show.properties.id];
layer.setIcon(defaultMarker);
}
// Get the countries geojson data from a JSON
$http.get('/json/shows.geojson').success(function (data, status) {
angular.extend($scope, {
geojson: {
data: data,
onEachFeature: function (feature, layer) {
layer.bindPopup(feature.properties.artist);
layer.setIcon(defaultMarker);
layer.on({
mouseover: pointMouseover,
mouseout: pointMouseout
});
layers[feature.properties.id] = layer;
//console.log(layers);
}
}
});
});
}]);
So mousing over a marker
(layer.on({
mouseover: pointMouseover,
mouseout: pointMouseout
});)
fires the appropriate functions which then changes the icon colors.
I connected the layer.feature.properties.id; to $scope.hoveritem so that my HTML can then use that as the index c. When you mouseover a marker, it then feeds the marker id through to $scope.hoveritem which then it turn goes into the $index part of the HTML, thus changing it's CSS class.
But something is awry. It only changes to the correct list item on mouseout instead of mouseover. Furthermore, I can't figure out to get it to return to the default white state. None of the list items should look active if the mouse is not on a marker.
Any ideas or hints on this would be very appreciated.
The reason for the delay in the mouseover effects was because of the angular $apply digest cycle. Angular basically wasn't aware of the changes to hoveritem. Wrapping it with $scope.$apply did the trick:
$scope.$apply(function () {
$scope.hoveritem = layer.feature.properties.id;
})

Create read more link in AngularJS

I want to create a link with read more text. if there are more than 3 lines in a paragraph this link should be visible and clicking on this it show display all the lines.
I wanted to do the same thing, so I created a directive. Have a look here: https://gist.github.com/doukasd/0744566c5494ebc8643f
Usage is pretty simple:
<p data-dd-collapse-text="100">{{veryLongText}}</p>
Where 100 is the character limit you want to specify.
UPDATE: dd-text-collapse
For read more , you can use angular limitTo filter for limiting your paragraph to character's length instead of limiting paragraph to line numbers .
You can use something like this :
in html
<p> {{myString | limitTo:numLimit}} </p>
<button type='button' ng-click="readMore()">read more</button>
in controller
$scope.numLimit=200;
$scope.readMore=function(){
$scope.numLimit=10000;
};
Based on some of the information here I've put together a nice show more/less implementation
Directive Definition
showMore.js
.directive('showMore',
function(){
return {
templateUrl: 'showMore.html',
restrict: 'A',
transclude: true,
scope:{
'showMoreHeight': '#'
},
controller: ['$scope', '$element', '$interval', function($scope, $element, $interval) {
$scope.expanded = false;
$interval(function(){
renderStyles();
}, 300);
$scope.expandable = false;
function renderStyles(){
if($element.height() >= $scope.showMoreHeight && $scope.expanded === false){
$scope.expandable = true;
}
}
$scope.showLessStyle = {
'max-height': $scope.showMoreHeight + 'px',
'overflow': 'hidden'
};
}]
};
});
showMore.html
<span>
<div ng-style='showLessStyle' ng-hide='expanded' ng-transclude>
</div>
<div ng-show='expanded' ng-transclude>
</div>
<a style='float:right;' ng-hide='expanded || !expandable' ng-click='expanded = !expanded' localize>show more</a>
<a style='float:right;' ng-show='expanded && expandable' ng-click='expanded = !expanded'>show less</a>
</span>
The usage is fairly simple just transclude what ever you want to show more/less of
USEAGE:
<div show-more show-more-height='150'>
<div class='showMoreContent'></div>
</div>
I hope this is somewhat helpful!
There can be some workaround.
basic idea is like this
1. at first, use javascript to calculate the original height
2. if higher than 3 lines , set the overflow to hidden and show a button. The button is used to toggle state
3. if not, do nothing
About the button.
If the button is out of text area, it's no problem for you I think.
If you need the button inline within the element, there are 2 possibilitis.
if you want it position fixed at right-bottom,
create a gradient background for the button, fade-out effect.
It looks nice and very simple.
here is jsfiddle: http://jsfiddle.net/sunderls/HYHZ6/ .
Everytime toggling the state, you actually change the class of the element.
if you want it right after the end of text? it's a little tricky, since even if text height is 3 lines, you cannot be sure
whether it remains 3 lines height , after appending a button next to
it.
I think one way is to use Range API, to calculate the paragraph
dimensions, (I've used the api in my collamark.com, it's powerful
but different browsers have different behaviors). Actually you can
get the dimensions of every lines dynamically. And by some
backwards loops, you can get the appropriate substring of the text,
to show in collapsed mode, which is just right for 3 lines height
with a button. (the api doc is here:
https://developer.mozilla.org/en-US/docs/Web/API/range)
So now every time you toggle the state, you actually is change the
text. not the class.
Here begins the Code when do it in Angular
template
first, this kind of feature is a standalone module which you can reuse everywhere, so in your template, we create a new directive autoFolded, sth like this:
<auto-folded>
<p>Some text here, maybe long maybe short</p>
</auto-folded>
directive.coffee
then we handle all the logic in directive definition( sorry for writing in coffee though)
directive('autoFolded',[
'$window'
($window) ->
return {
restrict: 'E'
transclude: true
template: '<div class="auto-folded"><div ng-transclude></div><a href ng-click="toggleFoldedState()" class="auto-folded--more"></a></div>'
link: (scope, element, attrs)->
$$window = $ $window
content = $(element).find('.auto-folded')
toggleFoldedState = ->
if content.hasClass 'auto-folded--folded'
content.removeClass('auto-folded--folded').addClass('auto-folded--unfolded')
else if content.hasClass 'auto-folded--unfolded'
content.removeClass('auto-folded--unfolded').addClass('auto-folded--folded')
return
scope.toggleFoldedState = toggleFoldedState
init = ()->
contentHeight = content.outerHeight()
if contentHeight > 48
content.addClass 'auto-folded--folded'
content.show()
$$window.on 'ngcontentloaded',init
}
])
here is the explaination
for this directive contains text it doesn't know, so it's a translucent direcitve. Like a modal popup, it contains the Text and a toggle button.
restrict: 'E'
transclude: true
template: '<div class="auto-folded"><div ng-transclude></div><a href ng-click="toggleFoldedState()" class="auto-folded--more"></a></div>'
when clicking the button, it actually do the toggling. If unfolded, then fold it; If foled, then unfolded. We accomplish this by toggling the classNames, the cold is straightforward
toggleFoldedState = ->
content.css 'color','red'
if content.hasClass 'auto-folded--folded'
content.removeClass('auto-folded--folded').addClass('auto-folded--unfolded')
else if content.hasClass 'auto-folded--unfolded'
content.removeClass('auto-folded--unfolded').addClass('auto-folded--folded')
return
and we use ng-click="toggleFoldedState()" to bind this action to the toggling button
We have to do some initial work to fold the text if it's tall enough at page loaded. However, link function of directive is to create the actual Dom, before dom rendering.So in link, we cannot know the height, that's why we register the init() to ngcontentloaded event:
init = ()->
contentHeight = content.outerHeight()
if contentHeight > 48
content.addClass 'auto-folded--folded'
content.show()
$$window.on 'ngcontentloaded',init
here I use 48px as 3-line-height, you can define your own, or calculate dynamically from the dom, like content.css('lineHeight').
since this is done after dom rendering, so the text is already displayed before init(). There will be a ugly slideUp effect. That's why we first hide the dom using css(as following), and content.show() in init
So we are done with directive, the folded/unfoled state are controlled by className. here we go.
css.sass
(sorry I wrote it in sass)
.auto-folded
display: none //only display after init()
position: relative
.auto-folded--more //the button is placed at right-bottom, and default to hidden
display: none
position: absolute
right: 0
bottom: 0
&.auto-folded--folded //when folded, set maxHeight, and overflow to hidden
max-height: 48px
overflow: hidden
.auto-folded--more // toggling button is displayed,
display: block
&:before // and it's text is "more"
content: "more"
&.auto-folded--unfolded //when unfoled, s
.auto-folded--more // toggling button is displayed
display: block
&:before // and it's text is "hide"
content: "hide"
So the text of toggling button and visibility of it , are all controlled by the class of its parent.
for the parent
1. if text is not 3-line height, it will only have 'auto-folded' as css class, so the button is hidden
if text is over 3-line height, in init() process, it's classNames will be 'auto-folded auto-folded--folded'.
then the button is shown. clicking it will toggle parent's classNames between 'auto-folded--folded' and 'auto-folded--unfolded'

Leaflet + Sencha NO touch + Sencha architect integration: mixed up tiles

I am trying to integrate Sencha 4.1 (ExtJS) with the Leaflet mapping library while using Sencha Architect.
When the page loads, the tiles are mixed up and appear offset. I need to drag the page up to be able to see the tiles.
The full project is available here: https://github.com/breizo/SenchaLeaflet.
Here is an excerpt of the custom component created (see full code here: https://github.com/breizo/SenchaLeaflet/blob/master/ux/LeafletMap.js).
constructor: function () {
this.callParent(arguments);
this.on({
resize: 'doResize',
scope: this
});
var ll = window.L;
if (!ll) {
this.setHtml('Leaflet library is required');
}
}
onRender: function() {
this.callParent(arguments);
var renderTo = arguments[0].dom.id;
debugger;
var me = this,
ll = window.L,
element = me.mapContainer,
mapOptions = me.getMapOptions(),
map,
tileLayer;
if (ll) {
// if no center property is given -> use default position
if (!mapOptions.hasOwnProperty('center') || !(mapOptions.center instanceof ll.LatLng)) {
mapOptions.center = new ll.LatLng(47.36865, 8.539183); // default: Zuerich
}
me.setTileLayer(new ll.TileLayer(me.getTileLayerUrl(), me.getTileLayerOptions()));
tileLayer = me.getTileLayer();
mapOptions.layers = [tileLayer];
me.setMap(new ll.Map(renderTo, mapOptions));
map = me.getMap();
// track map events
map.on('zoomend', me.onZoomEnd, me);
map.on('movestart', me.onMoveStart, me);
map.on('moveend', me.onMoveEnd, me);
me.fireEvent('maprender', me, map, tileLayer);
}
},
When debugging it appears that when onRender is called, the parent container of the map is not properly sized yet, in particular its height is only enough to contain the attrib text, about 16 pix. WHen the doResize is called, the container is properly sized, but it doesn't change the end result: the tiles are mixed up and offset.
I tried various changes to the container, but nothing worked...
1) Problem with mixed layers is caused by CSS. Your leaflet.css has wrong path in html, so it's not attached in the document. To fix mixing issue set correct path to css file, or attach it from CDN:
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.4/leaflet.css" />
2) Wrong map offset is caused by extjs generated div:
<div class="x-llmap x-fit-item x-llmap-default" ...></div>
It pushes map container to the bottom and wrong offset calculations are made. You can also fix this using inline style or CSS:
.leaflet-map-pane {
top: 0;
}

Resources