I am trying to rotate an arrow image based on a scope variable $scope.bearing. I have seen a few examples using CSS classes, but without setting up 360 of them, and having to manage the removal of the existing one and add a new one... sounds impractical. I did find this answer that does it programmatically on a button click, but I'm still kind of at a loss as to how I can set it from my scope variable without getting into some jQuery in the controller when the value is updated, and if I'm doing that, I may as well just use JQuery and CSS.
For example:
locSvc.addUpdateListener(function(pos, dir, spd) {
console.log("handleLocationChange()");
$scope.location = pos;
$scope.bearing = dir;
$scope.speed = spd;
$('#bearing-indicator').css({
"-webkit-transform": "rotate(" + dir+"deg)",
"-moz-transform": "rotate(" + dir+"deg)",
"transform": "rotate(" + dir+"deg)"
});
});
Related
Users of my app want to be able to detach certain application components from the SPA and move them onto their second monitor while retaining their functionality.
What I don't want is to load the entire SPA in a popup window just to show this one view. I've found that I can append a template to a popup window's body and $compile it there using the scope from the main window. This mostly works, but any directive that uses the 'require' syntax will ultimately fail because where ever Angular is looking for the required directive it isn't finding it.
Is there a better way of doing what I'm trying to achieve?
Or any ideas what I can try to solve the "Controller X required by directive Y can't be found" issue?
function createWindowForPoppedOutPane(pane) {
var features = 'menubar=no';
if (pane.top) features += ',top=' + pane.top;
if (pane.left) features += ',left=' + pane.left;
if (pane.width) features += ',width=' + pane.width;
if (pane.top) features += ',height=' + pane.height;
pane.window = $window.open('', '_blank', features);
copyStyleSheetsToWindow(pane.window);
var paneScope = scope.$new(false);
paneScope.pane = pane;
var paneTemplate = $($templateCache.get('pop-out-pane-template'));
paneTemplate.append($templateCache.get(pane.template));
scope.$evalAsync(function () {
pane.window.document.title = pane.title;
angular.element(pane.window.document.body).append(paneTemplate);
$compile(paneTemplate)(paneScope);
startPoppedOutPaneWatcher();
});
}
The correct way to handle this situatin it to load the app in the new window. This would be no different than someone doing a right-click open in new window. If you are sending appropriate cache headers for your JS and CSS files the use will not need to download the files.
Attempting to build a second sub-sub site where some components can be used is going to be a maintenance nightmare.
An alternative would be to create a second top level module that pulling in only the components you need but again this is a lot of ectra work on your end and your users would need to download additional files.
using ngClick i am easily able to get the relative click coordinates from event.offsetX. however using ngTouch there seems to be no obvious way of doing this. i've inspected the event.originalEvent.changedTouches[] array thoroughly but all of the coordinates in my touch event are absolute.
the only way appears to be traversing the DOM and getting each element's offset relative to its parent...a terrible solution indeed. am i missing something? where is the easy access to the x,y coordinates of the touch relative to the listening element?
i ended up using jQuery to hack out a solution - something that i've found occasionally necessary even when trying to avoid it and go all angular. since the relative coordinates aren't provided you have to figure out the viewport offset of the target element being clicked.
perhaps ngTouch is so young that the authors hadn't thought to add this by default, but it feels like an oversight to me.
i should note that i also discovered what appears to be an ngTouch bug where, if you tap just outside the ng-click element but close enough that the touch overlaps into the element, it fires with a MouseEvent whereas if you tap well inside the element it uses a TouchEvent instead. getting the offsetX parameter is different for each of these events as shown below.
scope.click = function( event )
{
var offsetX = null;
if( event.originalEvent.changedTouches )
{
offsetX = event.originalEvent.changedTouches[0].clientX - $(event.currentTarget).offset().left;
}
else
{
offsetX = event.offsetX;
}
... do stuff with offsetX ...
}
EDIT: I've put together a JSFiddle: http://jsfiddle.net/U3pVM/18137/
I want the black to go up AND THEN the green to slide across... I do not know how to achieve this... Read on for more information:
Side note: The code is in Typescript (re: () => {} )
The following context shows a web page that has an overlaying div, and a couple of divs behind it. It also denotes the kind of animation that is occuring via a css class.
I have the following code (see references to diagram):
$scope.Resizing = true; // This prevents animations from happening (as a css class is linked up to it (see below))
$scope.MoveToPage( col, row ); // 2
$scope.$applyAsync(() => { // Update the view to remove the class and then start sliding up the overlay div
$scope.FirstPageTop = -$scope.PageMap.WindowInfo.Height; // 3
$scope.Resizing = false; // Re-enable animations // Finish
});
What it is supposed to do is:
When Resizing is true, it removes a css class that animates stuff.
It then runs the MoveToPage method that makes a position left equal a new number.. usually it would animate to the new position but in this instance i don't want it to (thus the Resizing attribute).
We can see the above expressed in my html:
<div class="column-wrap"
ng-class="{ 'column-wrap-animation' : !Resizing }"
ng-style="{ 'width': PageMap.ContainerWidth + 'px',
'left': PageMap.ColumnWrap.Left + 'px'}">
The procedure then applies the scope asynchronously (because if i use a simple apply, it throws an error) and as a callback to this method, I am then doing pulling an overlay off of the top of the page... and then setting Resizing back to false to re-apply animations.
The problem:
I need to slide off an overlay AFTER positioning the content underneath it. What is actually happening is, it slides the overlay off of the top, while the under page animates to where i've told it to go. How do i get around this css animation digestion problem...
Perhaps an even simpler question is, why might the class not be being removed when Resizing is set to false and actively i am telling it to update the scope?
Explaining my context:
You can apparently use setTimeout to deal with the $scope.$apply issue:
http://jsfiddle.net/U3pVM/18138/
$scope.Resizing = true;
$scope.MoveToPage( col, row );
setTimeout( function() {
$scope.FirstPageTop = -$scope.PageMap.WindowInfo.Height;
$scope.Resizing = false;
$scope.$apply() // This is important
}, 1);
The cursor gets moved to the beginning instead of being set to last type position when the change event is fired. I am using content editable div using angular js.
plunker
http://plnkr.co/edit/FFaWNZmgk00Etubtjgg8?p=preview
Why does the cursor position move to beginning?
I know this is an old question but, as I've been searching my whole working day to find a solution to this issue, I'd like to share this solution to people who might still looking for a working answer :
var el, el2, range, sel;
el = element[0];
range = document.createRange();
sel = window.getSelection();
if (el.childNodes.length > 0) {
el2 = el.childNodes[el.childNodes.length - 1];
range.setStartAfter(el2);
} else {
range.setStartAfter(el);
}
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
I got this code originally from akatov/angular-contenteditable directive, "moveCaretToEndOnChange" method.
and this worked for me.
I would assume Angular is rewriting the innerHTML property of the editable element, thus destroying and recreating all of the DOM tree within it, which results in the caret resetting to the start. See these answers for potential solutions:
https://stackoverflow.com/a/13950376/96100
https://stackoverflow.com/a/8240055/96100
In my case I was using the same reference of array for changing the innerHTML as was used on the DOM (two way binding). Hence the dom re-rendered and thus the cursor moved to the beginning.
The Solution was to make a deep copy of the array using JSON.parse(JSON.stringify(a)); where a is the array that stores the words that are contenteditable and then do the change
let filetexts =JSON.parse(JSON.stringify(this.filetexts));
// updating the current text
filetexts[alternativesIndex].Alternatives[0].Words[wordIndex].Word = html;
let data = { Text: JSON.stringify(filetexts) };
this.fileService.changeFileText(data, this.file.fileId).subscribe(res => {
});
I'm new to AngularJS and hoping someone can help me get my head round this please!
I'm developing a web e-reader that pulls in pages of HTML content dynamically. So far, I'm doing that with an $http AJAX call and binding it in with 'ng-bind-html-unsafe' (the HTML is our own, simply served from a directory on the same server. I have control over the HTML so I could do this differently if needs be). So each time the user presses previous/next, the controller simply fetches in the previous/next page of HTML and switches that in the model - that works great.
But next I need to augment this dynamic HTML with user additions, e.g. comments and highlights. They need to appear where the user adds them, e.g. a comment would most likely sit underneath a particular paragraph.
With JQuery I guess I would give each of the HTML elements its own ID and associate each bit of user-generated content with a particular ID. Then I could manipulate the DOM myself, for example adding each comment under its associated element.
What's the right approach with AngularJS, since the principle seems to be to avoid direct DOM manipulation altogether?
I could see how it could be done by defining the HTML content as separate elements in the model, having individual JavaScript objects for each paragraph, header, image, etc. But that would basically be defining DOM-like data in JavaScript - and that feels quite wrong and messy...
Use an "ng-include" and dynamically bind a src attribute from the controller. Adding dynamic, user generated content is as adding the binding variables to your html. Following is angular code that implements previous/next buttons that dynamically loads html templates and populates them with the user added comments. At the bottom is a link to a fully functional jsfiddle.
angular.module('app',[]).controller('controller', function($scope){
var change;
change = function(){
$scope.src = $scope.page + '.html';
};
$scope.page = 1;
$scope.items = [];
change();
$scope.submit = function(text){
$scope.items.push(text);
};
$scope.next = function () {
if($scope.page < 3){
$scope.page++;
change();
}
};
$scope.previous = function () {
if($scope.page > 1){
$scope.page--;
change();
}
};
});
http://jsfiddle.net/jwanga/rnL7c/