how to wrap text in angularjs and save the position - angularjs

I would like to wrap text with span tag and save the position.
I know how to do it with JS but i dont know how to do it with angularjs
Here is what i have done:
http://jsfiddle.net/ymeaL06j/1/
This function gives me the position of the text in the DIV
function getSelectionPosition() {
var range = window.getSelection().getRangeAt(0);
var preSelectionRange = range.cloneRange();
preSelectionRange.selectNodeContents(document.getElementById("code"));
preSelectionRange.setEnd(range.startContainer, range.startOffset);
var start = preSelectionRange.toString().length;
return {
start: start,
end: start + range.toString().length
}
};
I take the start and end positions and insert them as an attribute in the span tag
After this i would like to save all the marked positions and load it later, i have a function that select text and then i can wrap it (i hope that there is a better solution)
function setSelection(savedSel) {
var charIndex = 0, range = document.createRange();
range.setStart(document.getElementById("code"), 0);
range.collapse(true);
var nodeStack = [containerEl], node, foundStart = false, stop = false;
while (!stop && (node = nodeStack.pop())) {
if (node.nodeType == 3) {
var nextCharIndex = charIndex + node.length;
if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) {
range.setStart(node, savedSel.start - charIndex);
foundStart = true;
}
if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) {
range.setEnd(node, savedSel.end - charIndex);
stop = true;
}
charIndex = nextCharIndex;
} else {
var i = node.childNodes.length;
while (i--) {
nodeStack.push(node.childNodes[i]);
}
}
}
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}

You can use the same code that you illustrated above and put it inside of your angularjs controller.
Refer to my plunker code; it is a simple angularjs version of your jsfiddle code.
For example, suppose that a snippet of the index.html looks like this:
<body ng-controller="MainCtrl">
<div id="code">This is <b>some text</b> bla bla bla</div>
<br />
<input type="button" value="Mark!" ng-click="markText()" />
<input type="button" value="Remove marks!" ng-click="removeMarks()" />
</body>
Then the example angularjs controller, MainCtrl, could look like this:
app.controller('MainCtrl', function($scope) {
var getSelectionPosition = function () {
var range = window.getSelection().getRangeAt(0);
var preSelectionRange = range.cloneRange();
preSelectionRange.selectNodeContents(document.getElementById("code"));
preSelectionRange.setEnd(range.startContainer, range.startOffset);
var start = preSelectionRange.toString().length;
return {
start: start,
end: start + range.toString().length
}
}
$scope.markText = function() {
var currPosition = getSelectionPosition();
var selection = window.getSelection().getRangeAt(0);
var selectedText = selection.extractContents();
var span = document.createElement("span");
span.className = "Mark";
span.setAttribute("PosStart", currPosition.start);
span.setAttribute("PosEnd", currPosition.end);
span.appendChild(selectedText);
selection.insertNode(span);
};
$scope.removeMarks = function() {
$(".Mark").each(function () {
$(this).contents().unwrap();
});
};
});
Notice that the MainCtrl is the angularjs controller for the body. The ng-click on the buttons reference the markText and removeMarks functions in the controller's scope. The logic in the functions are exactly the same as you referenced in your question (and jsfiddle).
None of your JS code changed other than moving the functions inside of the controller. Again, check out the plunker above to see the actual code working.

Related

How to dynamically use object property as width in AngularJS (in ng-repeat)?

I cannot get the object's property to be read in ng-style(shape.radius || shape.length). I can't even get 1 to work at the moment, but would like to have an or statement included. Similar to my ng-class.
There is a button to generate shapes, and the shapes were created with a random size. Here is my code:
html:
<div ng-controller='ShapeController as sc'>
<div>
<p><input type="submit" value="Generate Random Shapes" ng-click="sc.generateShapes()"/></p>
<div ng-repeat="shape in sc.shapes">
<div class="shape" ng-class="{circle: shape.radius, square: shape.length}" ng-style="{'width': shape.length}"></div>
</div>
</div>
</div>
script:
var app = angular.module('app', []);
app.controller('ShapeController', function(){
var vm = this;
vm.shapes = [];
vm.randomShapes = [];
vm.width = 30;
function createCircle(radius) {
let circle = new Circle(radius);
vm.shapes.push(circle);
} // end createCircle
function createSquare(length) {
let square = new Square(length);
vm.shapes.push(square);
} // end createSquare
vm.generateShapes = function() {
let times = 50
for (let i = 0; i < times; i++) {
createCircle(getRandomNumber());
}
for (let i = 0; i < times; i++) {
createSquare(getRandomNumber());
}
sort(vm.shapes);
console.log(vm.shapes);
}; // end generateShapes
}); // end controller
function sort(arr) {
arr.sort(function(a,b){
return b.getArea() - a.getArea();
});
} // end sort function
function getRandomNumber() {
return Math.random() * (100-1) + 1;
}
width should be either in px(some unit like em, pt, etc) or %
ng-style="{'width': shape.length + 'px'}"

passing variable value from controller to view in AngularJS

I am developing an application where i popup a window and when it gets popup the next time i will try to open it should not be opened. Next time it should open When i will close the popup then it should open.
Now for that i am using count variable and whenever it will be 1 the popup opens and whenever it is greater than or equal to 2 it shows alert. But now when i close the popup it is not resetting the value of count to 0 in view.
In JSfiddle i tried using var self = this; it works fine but when i tried it on my code it says Uncaught Exception Typeerror cannot find property 'count' defined at line self.count = 0;
How to achieve this? or any alternate solution for this?
<a ui-sref-active="active" ng-click="count=count+1; connectMachine(machine, count)" ng-init="count=0" ><span><i class="fa fa-desktop fa-5x"></i></span></a>
$scope.connectMachine = function(machine, count) {
var promise = restAPIService.connectMachineService(
$scope.thisStudentThisBatch.guacProfileId,
machine.connectionId, $stateParams.batchID).get();
promise.$promise.then(function(response) {
var json = JSON.parse(response.data);
console.log(json.id);
var dnsUrl = $location.absUrl().split('/');
dnsUrl = dnsUrl[0] + '//' + dnsUrl[2];
var apiUrl = dnsUrl + $rootScope.apiUrl + "guacamole/disconnect/"
+ json.id;
var conn_params = $http.defaults.headers.common.Authorization
+ "++" + apiUrl;
$scope.machineURL = response.headers.url + "&conn_params="
+ conn_params;
var params = "height=" + screen.availHeight + ",width="
+ screen.availWidth;
var NewWin;
var self = this;
if ($scope.count == 1) {
NewWin = window.open($scope.machineURL);
} else if ($scope.count >= 2) {
alert("Back Off Back Off");
}
function checkWindow() {
if (NewWin && NewWin.closed) {
window.clearInterval(intervalID);
self.count = 0;
}
}
var intervalID = window.setInterval(checkWindow, 500);
}, function(error) {
dialogs.error("Error", error.data.error, {
'size' : 'sm'
});
});
}
You can use a variable defined on $scope to trigger the value of button instead of using ng-init
HTML:
<div ng-app="myApp">
<ul ng-controller="TodoCtrl">
<li class="list-group-item" ng-repeat="todo in todos">{{todo.text}}
<button class="btn btn-default" ng-click="addLike($index)">value- {{count[$index]}}</button>
</li>
</ul>
</div>
JS:
var myApp = angular.module('myApp', []);
function TodoCtrl($scope) {
$scope.todos = [{
text: 'todo one'
}, {
text: 'todo two',
done: false
}];
$scope.count = [0, 0];
$scope.addLike = function(index) {
var NewWin;
$scope.count[index] ++;
if ($scope.count[index] == 1) {
NewWin = window.open('https://www.google.com');
} else if ($scope.count >= 2) {
alert("Back Off Back Off");
}
function checkWindow() {
if (NewWin && NewWin.closed) {
window.clearInterval(intervalID);
$scope.count[index] = 0;
$scope.$apply();
}
}
var intervalID = window.setInterval(checkWindow, 500);
};
};
JS Fiddle :https://jsfiddle.net/p41dLjmn/

Controller function not applying to ng-include file

I am trying to use the "minisCtrlOverall" controller in my ng-include file, but none of the functionality in my controller works for the included file unless I put ng-controller in the actual overall.html file. What I am trying to do is access the "ring-fill" class that is in my overall.html file from my controller. I want to add the class "active" too all "ring-fills" but its not working. I'm guessing this isn't working because the included files comes after the controller runs? Anyone know how I can fix this?
Controller:
angular.module('ciscoImaDashboardAdmin',[])
.controller('minisCtrlOverall', function ($scope, $rootScope, dummyData) {
$scope.overallOn = true;
$scope.switchView = function(element) {
var value = element.target.attributes['value'].value;
if(value == "overall") {
$scope.overallOn = true;
$scope.swimlaneOn = false;
}
else if(value == "swimlane") {
$scope.swimlaneOn = true;
$scope.overallOn = false;
}
};
var totalRings = 9;
var maxScore = 10;
var data_overall = {
average_score: 6
}
var ringToHighlight = Math.floor((data_overall.average_score/maxScore)*totalRings); //round down
var i = 0;
var rings = [];
var ringClass = 'path.ring-fill:not(.ring-border)';
$(ringClass).each(function(){
rings.push($(this)[0]);
});
while( i < ringToHighlight) {
fillPath(i);
i = i + 1;
}
function fillPath(i) {
if(i < ringToHighlight) {
var selectedRing = $(rings[i]);
selectedRing.attr("class", "ring-fill active");
}
}
});
HTML:
<div class="row mini" ng-show="overallOn" ng-controller="minisCtrlOverall">
<div class="col-sm-3">
<div ng-include="'svgs/overall.html'"></div>
</div>
</div>

rendering html from a controller in angular

Hopefully someone can point my in the right direction.
I am building a web app and part of it requires a user to click a button as fast as they can to obtain a score. The design dictates that I will need to show this score in double digits i.e 9 would be 09 so for styling I need to wrap span tags around each digit.
I have got everything working as required, I'm just having an issue with outputting the score that is wrapped in span tags as rendered html in my view.
I've put together a fiddle for the section that is causing me problems. Any advice, help, best practices etc is much appreciated.
What I've tried:
I've included a few of the things I've tried. Basically they involve using $sce and trying to ng-bind-html in the view. Attempt 3 seems the most logical to me but the $scope.count isn't being updated. I'm guessing I need to add a $watch or $apply function to keep it binded? but I'm not too sure how to implement it or even if this is good practice. Also, because I'm outputting html is it better practice to do this in a directive?
Fiddle http://jsfiddle.net/funkycamel/gvxpnvqp/4/
HTML
<section ng-app="myApp">
<div ng-controller="MyController">
<button ng-click="add(1)">add</button>
<!-- First attempt -->
<p class="first-attempt">{{ pad(count) }}</p>
<!-- Second attempt -->
<!-- in order for this attempt to work I have to call the pad2 function which
returns trustedHtml -->
{{ pad2(count) }}
<p class="second-attempt" ng-bind-html="trustedHtml"></p>
<!-- Third attempt -->
<p class="third-attempt" ng-bind-html="moreTrustedHtml"></p>
</div>
Javascript
var app = angular.module('myApp', []);
app.controller('MyController', ['$scope', '$sce', function ($scope, $sce) {
// Set initial count to 0
$scope.count = 0;
// function to add to $scope.count
$scope.add = function (amount) {
$scope.count += amount;
};
// Attempt 1
// make sure number displays as a double digit if
// under 10. convert to string to add span tags
$scope.pad = function (number) {
var input = (number < 10 ? '0' : '') + number;
var n = input.toString();
var j = n.split('');
var newText = '';
var trustedHtml = '';
for (var i = 0; i < n.length; i++) {
newText += '<span>' + n[i] + '</span>';
}
return newText;
};
// Attempt 2 - trying to sanitise output
// same as above just returning trusted html
$scope.pad2 = function (number) {
var input = (number < 10 ? '0' : '') + number;
var n = input.toString();
var j = n.split('');
var newText = '';
var trustedHtml = '';
for (var i = 0; i < n.length; i++) {
newText += '<span>' + n[i] + '</span>';
}
// return sanitised text, hopefully
$scope.trustedHtml = $sce.trustAsHtml(newText);
return $scope.trustedHtml;
};
// Attempt 3
// Trying to sanitise the count variable
$scope.moreTrustedHtml = $sce.trustAsHtml($scope.pad($scope.count));
}]);
These currently output
<span>0</span><span>0</span>
<span>0</span><span>0</span>
00
00
Again any advice/help is greatly appreciated.
Far simpler solution:
HTML
<p>{{ count < 10 ? '0' + count : count}}</p>
Controller:
app.controller('MyController', ['$scope', function ($scope) {
$scope.count = 0;
$scope.add = function (amount) {
$scope.count += amount;
};
}]);
DEMO
If you prefer you can do the padding in the controller instead, just use another variable
app.controller('MyController', ['$scope', function ($scope) {
var count = 0;
$scope.countText = '0';
$scope.add = function (amount) {
count += amount;
$scope.countText = count < 10 ? '0' + count : count;
};
}]);

AngularJS : Two-way binding between a textarea and ng-repeat-ed inputs

I was going to ask this as a question, but I figured out a solution. So at this point, I'm looking for a critique of my solution.
I've got a static textarea, and an input with an ng-repeat directive.
As the user types a sentence into the textarea, a input is rendered for each word in the sentence.
Then if the user updates the text in any input, the corresponding word in the textarea sentence is updated (really the whole sentence is recreated).
Demo: http://plnkr.co/edit/bSjtOK?p=preview
Questions
Keeping in mind that I'm only 2 weeks into my AngularJS learning:
Did I write this in the "angular" way?
Is there something I could have done better?
Am I violating any no-nos?
Abbreviated Code
HTML
<textarea ng-model="sentence" ng-change="parseSentence()" style="width: 100%; height: 15em;"></textarea>
<input type="text" ng-repeat="w in words" ng-model="w.word" ng-change="buildSentance(w)" />
JavaScript
function WordCtrl($scope, debounce) {
$scope.words = [];
$scope.sentence = 'Hello there how are you today?';
// this is called when the textarea is changed
// it splits up the textarea's text and updates $scope.words
$scope.parseSentence = function() {
var words = $scope.sentence.split(/\s+/g);
var wordObjects = [];
for (var i=0;i<words.length;i++) {
wordObjects.push({word: words[i]});
}
if ((words.length == 1) && (words[0] === '')) {
$scope.words = [];
} else {
$scope.words = wordObjects;
}
};
$scope.parseSentenceDebounced = debounce($scope.parseSentence, 1000, false);
$scope.buildSentance = function(w) {
var words = [];
for (var i=0;i<$scope.words.length;i++) {
var word = $scope.words[i].word;
if (word.replace(/\s+/g,'') !== '') {
words.push(word);
}
}
$scope.sentence = words.join(' ');
// if the user puts a space in the input
// call parseSentence() to update $scope.words
if (w.word.indexOf(' ') > -1) {
$scope.parseSentenceDebounced();
}
}
$scope.parseSentence();
}
Interesting issue you are having. I put your code on my page and the first thing I noticed is that you cannot pass debounce in the controller method.
Next Problem I noticed is that you have an ng-change that changes the values on another box with ng-change. I changed the event to Keypress to stop the digest in a digest.
Here it is working in JSFiddle enter link description here
The code:
HTML
<body ng-app="portal">
<div ng-controller="WordCtrl">
<textarea ng-model="sentence" ng-keypress="parseSentence()" style="width: 100%; height: 15em;"></textarea>
<input type="text" ng-repeat="w in words" ng-model="w.word" ng-keypress="buildSentance(w)" />
</div>
</body>
Javascript
angular.module("portal",[]).controller("WordCtrl",function($scope) {
$scope.words = [];
$scope.sentence = 'Hello there how are you today?';
$scope.parseSentence = function () {
var words = $scope.sentence.split(/\s+/g);
var wordObjects = [];
for (var i = 0; i < words.length; i++) {
wordObjects.push({ word: words[i] });
}
if ((words.length == 1) && (words[0] === ''))
{
$scope.words = [];
}
else
{
$scope.words = angular.copy(wordObjects);
}
}
$scope.buildSentance = function (w) {
var words = [];
for (var i = 0; i < $scope.words.length; i++) {
var word = $scope.words[i].word;
if (word.replace(/\s+/g, '') !== '') {
words.push(word);
}
}
$scope.sentence = words.join(' ');
// if the user puts a space in the input
// call parseSentence() to update $scope.words
if (w.word.indexOf(' ') > -1) {
$scope.parseSentenceDebounced();
}
}
$scope.parseSentence();
});
Hope this solves your issue.

Resources