I'm having an issue working with AngularJS and CSS3 animations.
I currently have a ng-repeat of items that are styled depending on their index. I have the appropriate CSS3 animation properties in place, but I can't seem to get the items to animate when the $index of each item changes (through orderBy).
Here's my ng-repeat list:
<li ng-repeat="video in videos | orderObjectBy:'order'" ng-style="{left: (($index * 160) + ($index * 15) + 'px')}">
{{ video.title }}
</li>
My CSS3:
ul li {
-webkit-transition: all 3s ease !important;
-moz-transition: all 3s ease !important;
transition: all 3s ease !important;
}
My orderObjectBy filter:
.filter('orderObjectBy', function(){
return function(input, attribute) {
if (!angular.isObject(input)) return input;
var array = [];
for(var objectKey in input) {
array.push(input[objectKey]);
}
array.sort(function(a, b){
a = parseInt(a[attribute]);
b = parseInt(b[attribute]);
if (!a && b) return 1;
else if (a && !b) return -1;
else if (a === b) return 0;
else return (a > b) ? 1 : (b > a ? -1 : 0)
});
return array;
}
});
And here's a JSFiddle: http://jsfiddle.net/jakemulley/Y8L5L/
As you can see in the JSFiddle, when you click on a video, it "jumps" to the front, because its index is changed. This is what I want to animate - instead of it jumping.
Any ideas?
Thanks guys!
You must configure your animation CSS code before, as animations guide says, and you're using ngRepeat, so, you should read this: https://docs.angularjs.org/api/ng/directive/ngRepeat#usage_animations.
See a Plunker: http://plnkr.co/edit/VXg7x0PXDyNw5YEbbDLI. On my Plunker I am using a ngRepeat too, so you should see the example after read.
Related
I have a ng-repeat on my html view. The array it's repeating is populated each time a user selects a checkbox. It works like a filter way.
However, I am having issues with performance. It seems to create the DOM elements again and thus doing this there is a 1-1.5 second 'freeze' delay before the results are populated on the user interface again.
The array it's repeating isnt that big - probably around 50-60 entries. Each entry in the array has two objects which do have a lot of properties. Will this affect the performance? From what I've read it seems to be because it's creating the DOM elements again.
I have tried using track by $index, which speeds it up drastically but this causes problems on the div boxes I'm displaying. Text is on the wrong results, gets mixed up etc. I have also tried using track by ($index + item). No luck - same problem. I've also tried using track by item.id - but this has the same effect of not using track by - slow.
Is there anything I can do to optimize this? Or do I just bite the bullet?
Here is my code below:
<div ng-if="$ctrl.hasDataProcessed() && $ctrl.resultsAvailable()">
<div class="acca-builder-content">
<div class="acca-builder-header" style="border: 1px solid #1393ED;">{{"RESULTS" | translate}} ({{$ctrl.accaBuilderResultsCount}})</div>
<ul class="tips-list-group-matches">
<li ng-repeat="result in $ctrl.accaBuilderResults | orderBy: $ctrl.getSort" ng-class="{'match-has-link': $ctrl.canViewMatch(result.match)}" class="tip-list-group-match">
<tf-competition-header ng-if="result.match.CompMasterID" competition="result.match"></tf-competition-header>
<match-header match="result.match" tracking-screen="Tips"></match-header>
</li>
</ul>
</div>
</div>
</div>
And within the controller:
var buildAccaResultsFromFilter = function () {
var results = [];
var tips = ctrl.tips;
for (var i = 0; i < tips.length; i++) {
var tip = tips[i];
if(valueInFilter("COMPETITIONS", tip.match.CompID) &&
valueInFilter("DATES", tip.match.MatchDateConverted) &&
valueInFilter("SHOW", tip.tip.TipType)) {
results.push(tip);
}
}
if(results.length > 0) {
ctrl.accaBuilderResults = results;
ctrl.accaBuilderResultsCount = results.length;
ctrl.resultsFound = true;
} else {
clearAccaBuilderResults();
ctrl.resultsFound = false;
}
};
// Function called when a checkbox is clicked
ctrl.onCheckboxChange = function (option, item) {
item.checkState = !item.checkState;
if(item.checkState) {
addToFilter(option.optionKey, item.textKey);
}
else {
removeFromFilter(option.optionKey, item.textKey);
}
if(option.onChange) {
option.onChange(item.checkState, item.checkId);
}
if(ctrl.canBuildAccaResults()) {
buildAccaResultsFromFilter();
} else {
clearAccaBuilderResults();
}
};
// Checks if a value is present within the filter by it's key
var valueInFilter = function (filterKey, value) {
return ctrl.filter[filterKey].includes(value);
};
ctrl.resultsAvailable = function () {
return ctrl.accaBuilderResults && ctrl.accaBuilderResults.length > 0;
};
ctrl.hasDataProcessed = function () {
return ctrl.tips && ctrl.competitions;
};
Without seeing some code it is tough to optimize.
You can try and eliminate any ng-show and ng-hide and use ng-if only. It will remove a lot of watchers if you have those present.
Additionally you can use the syntax below in your directives if you don’t need two-way binding. This will also remove a watcher. The more watchers you can remove in ng-repeat, the better.
{{:: expression }}
Hi Guys My requirement is to make an element draggable across the screen and set the position of the element to the place where users stops dragging. So far I am able to make a element draggable across the screen but once released it is going back to its old position (position where it was earlier) I am using ngDraggable directive of angular. Sorry I am new to Ionic and angular. Any help will be highly appreciated.
My code goes as follows
<div ng-drag="true" id="draggableAxis" ng-style="{{draggedStyle}}" ng-drag-success="onDragComplete($data,$event)">
<img src="img/axis.png" >
<img ng-src="img/other.png" style="width:75px">
</div>
In my controller:
$scope.draggedStyle = {top: '96px',
right: '90px'};
var onDraggableEvent = function(evt, data) {
if(evt.name=='draggable:start'){
console.log('draggable-start');
console.log(data.x);
console.log(data.y);
}else{
console.log('draggable-end');
console.log(data);
console.log(data.element.centerX +' '+data.element.centerY);
console.log(evt);
$scope.setPosition(data);
}
}
$scope.onDragComplete=function(data,evt){
console.log("drag success, data:", data);
console.log(evt);
}// this fn doesnot gets triggered
$scope.$on('draggable:start', onDraggableEvent);
$scope.$on('draggable:end', onDraggableEvent);
$scope.setPosition=function(data){
$scope.draggedStyle = {
top: data.x+'px',
right: data.y+'px'
};
}
SCREEN SHOT OF MOBILE VIEW
I updated the fix to https://plnkr.co/edit/fVaIUVvAd7jLETkwVepm?p=preview
One of the problems was
ng-style="{{draggedStyle}}"
which should be
ng-style="draggedStyle"
Also, I switched the setPosition method to flip the x and y and used left because x indicates position from the left and not right.
$scope.setPosition = function(data) {
$scope.draggedStyle = {
top: data.y + 'px',
left: data.x + 'px'
};
}
Hope that helps
For my Angular js grid work, I am using ui-grid(v3.1.1) to implement the grid table. When I change the pagination size, I am having trouble to scroll down using mousewheel. When I hover over the mouse on scrollbar, then scroll up and down both are working fine. Strangely, the scroll up using mousewheel is working fine too. Its just the scroll down that doesn't seem to work. I fail to understand the reason for it.
I have disabled the scrolling when not required using:
.ui-grid-render-container-body .ui-grid-viewport {
overflow: auto !important;
}
Other than this I have not changed any of the default settings. Why is it that the mousewheel scroll down won't work when hovered over ui-grid? Please help
I commented out event.preventDefault() on line 2859 of v4.0.2 (in gridUtil.on.mousewheel function).
Can't say what other impact this may have, but worked for my usage.
Experienced this mousewheel/trackpad drag issue in v4.0.2 & v4.0.3 in a grid with pagination & filtering enabled and with CSS modifications to enable cell word-wrap.
.ui-grid-viewport .ui-grid-cell-contents {
word-wrap: break-word;
white-space: normal !important;
overflow:visible;
}
.ui-grid-cell {
display : table-cell;
height: auto !important;
overflow:visible;
position: static;
}
.ui-grid-row {
height: auto !important;
display : table-row;
position: static;
}
.ui-grid-row div[role=row] {
display: flex ;
align-content: stretch;
}
Tried Paul's fix and it worked but requires change to core code. Also, our requirements called for showing the entire table on the page based on the data rather than a fixed height table with scroll. So we couldn't use.
Instead, I worked around the issue by implementing a dynamic grid height solution.
Added ng-style to the grid as follows:
<div ui-grid="gridOptions" style="float:left" ui-grid-selection ui-grid-pagination ng-style="gridHeight()" class="myGrid"></div>
Disabled grid scrolling:
$scope.gridOptions.enableHorizontalScrollbar = 0;
$scope.gridOptions.enableVerticalScrollbar = 0;
Wrote function gridHeight as follows (uses jQuery):
$scope.gridHeight = function() {
var outerHeight = 50; //buffer for things like pagination
$('.ui-grid-row').each(function() {
outerHeight += $(this).outerHeight();
});
$('.ui-grid-header').each(function() {
outerHeight += $(this).outerHeight();
});
return { height: outerHeight+"px"};
};
This satisfied our requirements.
Another way to solve this - if the grid has no scroll at least - is to redefine the prototype function atTop & atBottom on uigrid's ScrollEvent. This will not need changes to uigrid's code, one just has to inject ScrollEvent in the controller or app.run().
The grid swallows any scroll event if its possible to scroll, but does not handle it if there are no scrollbars present.
The original code also fails an isNaN check on this.y.percentage - i think it should be as it is in the comment below.
ScrollEvent.prototype.atTop = function(scrollTop) {
return true;
//return (this.y && (this.y.percentage === 0 || isNaN(this.y.percentage) || this.verticalScrollLength < 0) && scrollTop === 0);
};
ScrollEvent.prototype.atBottom = function(scrollTop) {
return true;
//return (this.y && (this.y.percentage === 1 || isNaN(this.y.percentage) || this.verticalScrollLength === 0) && scrollTop > 0);
};
I have an app with <ons-sliding-menu> and a page with <ons-toolbar> and a horizontal <ons-carousel> covering the remaining space.
For the <ons-sliding-menu> the parameter swipe-target-width="50px" is set.
Is there a way to tell the <ons-carousel> to ignore events originating from the most left 50px and let these go to the menu?
Currently there is no option to make the carousel ignore events on one side, but perhaps you can make a trick. You can put a div at the same level than the carousel and let it take the clicks instead of the carousel in the area you need:
<div class="cover"></div>
<ons-carousel>
...
</ons-carousel>
You can change these values to fit your case:
.cover {
position: absolute;
left: 0;
height: 100%;
width: 200px;
z-index: 1;
}
Check it out here: http://codepen.io/frankdiox/pen/YqKOJE
Hope it helps!
After some experimentation, I came to the solution to inject the necessary functionality directly in the drag event handlers of the OnsCarouselElement. For this purpose I have introduced the attribute swipe-ignore-left for the <ons-carousel>. The other sites could easily be added when needed.In order to inject the functionality, load this JS-Code after loading onsenui.js:
(function () {
'use strict';
/****************************************************************
Checks the current event against the attribute swipe-ignore-left.
****************************************************************/
window.OnsCarouselElement.prototype._ignoreDrag = function (event) {
var attr = this.getAttribute('swipe-ignore-left');
if (attr === undefined) return false;
var left = parseInt(attr, 10);
if (left === undefined || left < 1) return false;
var startX = event.gesture.center.clientX - event.gesture.deltaX;
return startX < left;
};
/****************************************************************
Save the original drag-event-handlers
****************************************************************/
var originalCarouselOnDrag = window.OnsCarouselElement.prototype._onDrag;
var originalCarouselOnDragEnd = window.OnsCarouselElement.prototype._onDragEnd;
/****************************************************************
Override: OnsCarouselElement.prototype._onDrag
****************************************************************/
window.OnsCarouselElement.prototype._onDrag = function (event) {
if (this._ignoreDrag(event)) return;
originalCarouselOnDrag.apply(this, arguments);
};
/****************************************************************
Override: OnsCarouselElement.prototype._onDragEnd
****************************************************************/
window.OnsCarouselElement.prototype._onDragEnd = function (event) {
if (this._ignoreDrag(event)) return;
originalCarouselOnDragEnd.apply(this, arguments);
};
})();
To preserve for example the left 20 pixel for the <ons-sliding-menu>, this HTML is to provide:
<ons-sliding-menu ... side="left" swipeable swipe-target-width="20px" />
...
<ons-carousel ... swipeable swipe-ignore-left="20px" />
I am trying to dynamically load the optionset of select tag on ng-click event. clicking on expand arrow fires ng-click event. Since I fetch the data at that point and bind to the select control, angularjs omits the expand event somehow, select does not expand. User has to click on the arrow one more time to see the results of the dynamically loaded options.
How can I ensure to expand the select correctly in one click while loading the options?
I tried to find a way to dynamically expand the select when the load is complete, but afaik there is no cross browser way of achieving that.
Here is the code, onclick retrieve method is being called to fetch and bind the data to the select. But click does not expand the control...
<div style="position: relative;">
<input type="text" class="inputTextFieldCell"
style="width: 215px; position: relative; top: 0; left: 0; z-index: 2; padding: 0; margin: 0;"
ng-model="subscriptionResource.Value" />
<select class="optionsCellInput selectTextFieldCell"
style="width:240px;position: absolute; top: 0; left: 0; padding: 0; margin: 0;"
ng-click="resources.retrieve(subscriptionResource, $event)"
ng-model="item" ng-options="resource as resource.Serial for resource in subscriptionResource.resourceList"
ng-change="selectChange(subscriptionResource, item)" >
</select>
</div>
Javascript:
$scope.resources = {
retrieve: function (resource, event) {
if (resource.resourceList == null) {
var reqDataRetrieveResources = { "request": null };
reqDataRetrieveResources = window.createRequest(window.definitions.messages.RetrieveResourceRequest, reqDataRetrieveResources);
reqDataRetrieveResources.request["ResourceType"] = 1;
reqDataRetrieveResources.request["SearchCount"] = 5;
$http.post(window.definitions.url, reqDataRetrieveResources).success(function (result) {
if ((result.d !== null) && (result.d.Resources !== null)) {
resource.resourceList = result.d.Resources;
return true;
}
});
return true;
}
}
}
Try to use ng-mousedown instead of ng-click. If it don't works, try to call $scope.$apply after bind your data.