Angular 1.5 directive loop issue repeating the first element - angularjs

referring to this plunker:
https://plnkr.co/edit/kBoDTXmm2XbxXjpDA4M9?p=preview
I am trying to create a directive that takes an array of json objects, syntax highlights them and its that syntax highlighted element in the page.
I have had mixed results with different watching methods, but what I cant figure out in this one is why the same id:1 is showing all the way down the list, why not id:1, id:2, id:2, id:3 etc.
angular.module("ngjsonview",[]).directive('ngjsoncollection',['$sce', function(){
'use strict';
return {
transclude: false
,restrict:'E'
,scope:{ d:'=',o:"=" }
,templateUrl: "./template.htm"
,controller:function($scope,$sce){
var fnPretty = function(objData){
if (typeof objData != 'string') { var strOut = JSON.stringify(objData, undefined, 2); }
strOut = strOut.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>');
return strOut.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
var cls = 'number';
if (/^"/.test(match)) {
if (/:$/.test(match)) { cls = 'key';}
else { cls = 'string'; }
} else if (/true|false/.test(match)) { cls = 'boolean';}
else if (/null/.test(match)) {cls = 'null'; }
return '<span class="' + cls + '">' + match + '</span>';
});
};
$scope.html=$sce.trustAsHtml(fnPretty($scope.d));
$scope.$watchCollection('d',function(arrData){
$scope.arrHTML=[];
for (var i = 0, len = arrData.length; i < len; i++) {
$scope.arrHTML.unshift($sce.trustAsHtml(fnPretty(arrData[i])));
}
});
}
};
}]);

moving the timeout into a function helped with the specific problem I had
$scope.arrData=[];
function addIt(x) {
$timeout(function(){
$scope.arrData.push({id:x});
}, 100);
}
for(var i=0; i < 100; i++){
addIt(i)
}

Related

Resizable handles duplicate problem in undo redo functionality

" I am using resizable handles directive.I am creating undo / redo functionality then i am push html into array, When ever i am click on the undo button resizable Handles becoming duplicate."
I am pushing inside ng-repeat html into array where i am using resizable directive.
When ever i am going to append html(array for undo/redo) resizable handles become duplicate.
app.directive('rotateimage', function($compile) {
return {
restrict: 'A',
link: function postLink(scope, elem, attrs) {
elem.resizable({
handles: "ne, nw, se, sw,n,e,s,w",
aspectRatio: true
});
elem.on('resizestop', function(evt, ui) {
scope.id = [];
scope.style = [];
scope.html = [];
var id = elem.parent(".div01").attr('id');
scope.id.push(id);
scope.style.push($("#" + id).attr('style'));
$("#" + id).find(".ui-resizable-handle").remove();
scope.html.push($("#" + id).html().trim());
scope.historyApp.add(scope.style, scope.id, scope.html);
});
elem.on('resizestart', function(evt, ui) {
console.log("loll");
scope.id = [];
scope.style = [];
scope.html = [];
var id = elem.parent(".div01").attr('id');
scope.id.push(id);
scope.style.push($("#" + id).attr('style'));
$("#" + id).find(".ui-resizable-handle").remove();
scope.html.push($("#" + id).html().trim());
scope.historyApp.add(scope.style, scope.id, scope.html);
});
}
};
});
$scope.historyApp = {
stackStyle: [],
stackId: [],
html: [],
dataorignlbgcrop: [],
databbgval: [],
counter: -1,
add: function(style, id, html) {
++this.counter;
this.stackStyle[this.counter] = style;
this.stackId[this.counter] = id;
this.html[this.counter] = html;
this.doSomethingWith(style, id, html);
$scope.countBoxVal = true;
// delete anything forward of the counter
this.stackStyle.splice(this.counter + 1);
$scope.countBoxVal = true;
//alert(this.counter);
// alert($scope.countBoxVal);
},
undo: function() {
--this.counter;
// this.doSomethingWith(this.stackStyle[this.counter],this.stackId[this.counter]);
this.doSomethingWith(this.stackStyle[this.counter], this.stackId[this.counter], this.html[this.counter]);
--this.counter;
$scope.countBoxVal = false;
},
redo: function() {
++this.counter;
++this.counter;
this.doSomethingWith(this.stackStyle[this.counter], this.stackId[this.counter], this.html[this.counter]);
},
doSomethingWith: function(style, id, html) {
if (this.counter <= 0) {
this.counter = 0;
$scope.couterStackEnter = "enter";
//$scope.couterStackExit="undefined";
$('#undo').addClass('disabled');
$('#redo').removeClass('disabled');
} else {
$('#undo').removeClass('disabled');
}
var mathPro = Math.abs(this.counter);
var kp = mathPro + 1;
if (Math.abs(this.counter) >= this.stackStyle.length) {
$('#redo').addClass('disabled');
$scope.couterStackExit = "exit";
} else {
$('#redo').removeClass('disabled');
}
angular.forEach(html, function(val, key) {
console.log($scope.historyApp);
var myEl = angular.element(document.querySelector('#' + id[key]));
var ids = myEl.find(".forReverseUndoRedo").attr("id");
var myEls = angular.element(document.querySelector('#' + ids));
setTimeout(function() {
myEl.attr("style", style);
// myEls.clone().html("");
myEl.html("");
myEl.find(".ui-resizable-handle").remove();
myEl.append($compile(val)($scope));
$scope.$digest();
}, 100);
});
}
};

For-loop return array value

I'm trying to insert url for menu through mustache template. But just the first value is being returned for the array.
Or is this the return method wrong
var main_menu_link = ["main_dashboard.html", "#", "online_dashboard.html","index.html","#","#","#"];
var url = "";
var i;
var url_link="";
for(i = 0; i < main_menu_link.length; i++) {
url += main_menu_link[i];
return '' + text + '';
}
CodePen working here
The return statement has to be after the loop:
var main_menu_link = ["main_dashboard.html", "#", "online_dashboard.html","index.html","#","#","#"];
var url = "";
var i;
var url_link="";
for(i = 0; i < main_menu_link.length; i++) {
url += '' + text + '';
}
return url;
Correct template as below in-case it can be of use to someone else
var link_details = { "link_details" :[
{ main_menu: "Dashboard", main_menu_link: "dashboard.html" },
{ main_menu: "Analytics", main_menu_link: "#" },
{ main_menu: "System", main_menu_link: "system.html" }
]};
var template = "<ul>{{#link_details}}<li>{{main_menu}}</li>{{/link_details}}‌​</ul>";
var html = Mustache.to_html(template, link_details);
document.write(html)

getting the inner html of a contenteditable div in angularjs

I am trying to get innerHTML of a contenteditable div via function defined in controller of angularjs but it returns undefined every time.. what are the alternatives or how can I handle this issue?
$scope.genrate_HTML=function()
{
var read_string=document.getElementsByClassName("MainPage");
//console.log(read_string);
var p_tag= '\n<p id="test"> \n'+read_string.innerHTML+'\n </p>';
//document.getElementById("createdHTML").value = p_tag ;
//$compile( document.getElementById('createdHTML') )($scope);
}
the contenteditble div's classs name is "MainPage"
VisualEditor.controller("GenrateHTML",function($scope){
$scope.savefile=function()
{
$scope.genratedHTML_text=document.getElementById("createdHTML").value;
var text_file_blob= new Blob([$scope.genratedHTML_text],{type:'text/html'});
$scope.file_name_to_save=document.getElementById("file_name").value ;
var downloadLink=document.createElement("a");
downloadLink.download=$scope.file_name_to_save;
downloadLink.innerHTML="Download File";
if(window.URL!=null)
{
downloadLink.href=window.URL.createObjectURL(text_file_blob);
}
else
{
downloadLink.href = window.URL.createObjectURL(text_file_blob);
downloadLink.onclick = destroyClickedElement;
downloadLink.style.display = "none";
document.body.appendChild(downloadLink);
}
downloadLink.click();
}
function destroyClickedElement(event)
{
document.body.removeChild(event.target);
}
$scope.toggleModal = function(){
$scope.showModal = !$scope.showModal;
};
///add details
$scope.details=[];
$scope.addDetails=function(){
$scope.details.push({
Title:$scope.Details_Title,
meta_chars:$scope.Details_metaChars,
version:$scope.Details_version,
Auth_name:$scope.Details_AuthName,
copyRights:$scope.Details_copyRights
});
document.getElementById("createdHTML").innerHTML = $scope.details;
};
$scope.$watch('details', function (value) {
console.log(value);
}, true);
/////////////////////
$scope.genrate_HTML=function()
{
var read_string=document.getElementsByClassName("MainPage");
//console.log(read_string);
var p_tag = '';
for (var i = 0; i < read_string.length; i++) {
p_tag += '\n<p id="test_"' + i + '> \n' + read_string[i].innerHTML + '\n </p>';
document.getElementById("createdHTML").value = p_tag;
}
//$compile( document.getElementById('createdHTML') )($scope);
}
});
getElementsByClassName returns an Array, so, your read_string variable is an Array type. you should iterate through the elements of read_string with for loop.
NOTE: Please check the p element's id here aswell. Because id must be unique!
$scope.genrate_HTML = function() {
var read_string = document.getElementsByClassName("MainPage");
var p_tag = '';
for (var i = 0; i < read_string.length; i++) {
p_tag += '\n<p id="test_"'+i+'> \n'+read_string[i].innerHTML+'\n </p>';
}
/* Other code here... */
}
UPDATE: Don't use the code below! If read_string returns with no elements than your code will crash!
But if it's a 1 element Array then you can take the value like:
$scope.genrate_HTML = function() {
var read_string = document.getElementsByClassName("MainPage");
var p_tag= '\n<p id="test"> \n'+read_string[0].innerHTML+'\n </p>';
/* Other code here... */
}
I hope that helps. If it doesn't then paste the full code of the Controller.

How to check if expression will have a value after evaluating

Let's say I have a following template:
"foo['x'] = '{{ myVar }}';"
Is there an angular way of checking if evaluating this against my current scope will give myVar some value ? I've got an array of such small templates and I only want to include them in the document when values are truthy. I was hoping either $interpolate, $parse or $eval might come in handy here. I know for sure that $interpolate is useless. What about the other two ? Maybe it's at least possible to get the name of the assigned value/expression ?
EDIT
I wasn't specific enough. What I was trying to achieve, was checking in advance if for example template '{{ myVar }}' evaluated against the current scope will return an empty string or value of the scope variable (if it exists). The case was really specific - when traversing an array of short templates I wanted to know if a template will return as an empty string or not, and only include it in my final html if it doesn't.
I'm not sure what are you trying to achieve, but to if you want to check if myVar is truthy in current scope, you can:
{{myVar ? "aw yiss" : "nope"}}
Evaluates to "aw yiss" if myVar is truthy and "nope" otherwise.
I ended up with a modified $interpolate provider but maybe someone knows a shorter solution :
app.provider('customInterpolateProvider', [
function $InterpolateProvider() {
var startSymbol = '{{';
var endSymbol = '}}';
this.startSymbol = function(value){
if (value) {
startSymbol = value;
return this;
} else {
return startSymbol;
}
};
this.endSymbol = function(value){
if (value) {
endSymbol = value;
return this;
} else {
return endSymbol;
}
};
this.$get = ['$parse', '$sce', function($parse, $sce) {
var startSymbolLength = startSymbol.length,
endSymbolLength = endSymbol.length;
function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
allOrNothing = !!allOrNothing;
var startIndex,
endIndex,
index = 0,
expressions = [],
parseFns = [],
textLength = text.length,
exp;
var getValue = function (value) {
return trustedContext ?
$sce.getTrusted(trustedContext, value) :
$sce.valueOf(value);
};
var stringify = function (value) {
if (value == null) {
return '';
}
switch (typeof value) {
case 'string':
break;
case 'number':
value = '' + value;
break;
default:
value = angular.toJson(value);
}
return value;
};
var parseStringifyInterceptor = function(value) {
try {
return stringify(getValue(value));
} catch(err) {
console.err(err.toString());
}
};
while(index < textLength) {
if ( ((startIndex = text.indexOf(startSymbol, index)) !== -1) &&
((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) !== -1) ) {
exp = text.substring(startIndex + startSymbolLength, endIndex);
expressions.push(exp);
parseFns.push($parse(exp, parseStringifyInterceptor));
index = endIndex + endSymbolLength;
} else {
break;
}
}
if (!expressions.length && !text.contains(startSymbol) && !text.contains(endSymbol)) {
expressions.push(text);
}
if (!mustHaveExpression) {
var compute = function(values) {
for(var i = 0, ii = expressions.length; i < ii; i++) {
if (allOrNothing && angular.isUndefined(values[i])) {
return;
}
expressions[i] = values[i];
}
return expressions.join('');
};
return angular.extend(function interpolationFn(context) {
var i = 0;
var ii = expressions.length;
var values = new Array(ii);
try {
if (ii && !parseFns.length) {
return expressions[0];
} else {
for (; i < ii; i++) {
values[i] = parseFns[i](context);
}
return compute(values);
}
} catch(err) {
console.err(err.toString());
}
}, {
exp: text,
expressions: expressions,
$$watchDelegate: function (scope, listener, objectEquality) {
var lastValue;
return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
var currValue = compute(values);
if (angular.isFunction(listener)) {
listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
}
lastValue = currValue;
}, objectEquality);
}
});
}
}
return $interpolate;
}];
}
]);
Lines below were added because in some cases I have a predefined text in my short template and I always want to render it :
if (!expressions.length && !text.contains(startSymbol) && !text.contains(endSymbol)) {
expressions.push(text);
}
if (ii && !parseFns.length) {
return expressions[0];
} else {

How to construct view content programmatically

I'm trying to construct a sentence in an AngularJS view. For example, with variables {overdue: 5, name: "Kasper"}, I would like to have "{{overdue}} days overdue. Employee: {{name}}".
I tried using a function:
function renderLine() {
var results = new Array();
if (overdue) {
result.push("{{overdue}} days overdue");
}
if (overdue) {
result.push("{{points}} points");
}
/* combine into a string */
var result = "";
for (var i = 0; i < results.length; i+=1) {
if (result.length != 0) {
result += ", ";
}
result += results[i];
}
if (result.length > 0) {
result += ". ";
}
/* add name */
result += "Name: {{name}}";
return result,
}
More specifically, my question is: how can I use angular directives like {{variable}} in strings that are constructed programmatically and have angular process the directives? I don't want to construct the strings without using directives because the strings are translated into different languages, where the placing of variables within sentences might change.
I ended up creating an angular directive. The ui-if and ngRepeat directives were good starting points for a DOM-manipulating directive. There is the code for the somewhat modified directive:
angular.module("hk").directive("myDirective",
[ "$interpolate", "$log",
function($interpolate, $log) {
return {
transclude: 'element',
replace: false,
restrict: 'A',
terminal: true,
compile: function(tElement, tAttrs, linker) {
return function(scope, elem, attr) {
var lastElement;
var lastScope;
var expression = attr.myDirective;
scope.$watch(expression, function (item) {
if (lastElement) {
lastElement.remove();
lastElement = null;
}
if (lastScope) {
lastScope.$destroy();
lastScope = null;
}
lastScope = scope.$new();
lastScope.item = item;
linker(lastScope, function (clone) {
lastElement = clone;
var results = [];
if (item.isactive) {
results.push("++{{item.createdtime | age}} active");
if (item.status == 'started') {
results.push("++{{item.startedtime | age}} started: {{item.startedby_displayname}}");
}
}
if (item.islate) {
results.push("++{{item.latetime | age}} past due");
}
var result = "";
for (var i = 0; i < results.length; i+=1) {
if (result.length != 0) {
result += ", ";
}
result += results[i];
}
if (result.length > 0) {
result += ". ";
}
if (!item.startedby_displayname) {
if (item.assignedto_displayname) {
result += "++Assigned to {{item.assignedto_displayname}}.";
}
}
var interpolated = $interpolate(result)(lastScope);
elem.after(interpolated);
});
});
};
}
};
}]);
I think you could use $scope.$eval for your purposes. See this fiddle
In you could create a message like this:
$scope.$eval('"Hello "+name');
And then let the string to be evaluated change per language.
{de: '"Hallo " + name', it: '"Buon giorno "+ name', fr: '"Salut " +name'}
Or something along those lines (of course you'll want to have those translations checked).
You could also create a directive and use $compile to keep the exact strings you have now working.

Resources