Mapbox & Angular Iframe doens't work on Firefox - angularjs

I have deployed Angular to my Codigniter project and also I have deployed MapBox Leaflet to it as part of my project, but it doesn't work on Firefox, what I have done I used the scope and angular object inside the iFrame I use to load the mapbox map view. It works seamlessly on other browsers, but it doesn't work on Firefox.
This is the error I get on my Console
WARNING: Tried to load angular more than once.
angular.min.js (line 206)
TypeError: a is null
<iframe width="100%" height="600px" src="URL/ADDRESS/TO/MAP/WHERE/I/REUSE/ANGULAR/AND/SCOPE/AGAIN" frameborder="0"></iframe>
<!DOCTYPE html>
https://api.mapbox.com/mapbox.js/v2.2.4/mapbox.js'>
https://api.mapbox.com/mapbox.js/v2.2.4/mapbox.css' rel='stylesheet' />
" type="text/javascript">
https://api.mapbox.com/mapbox.js/plugins/mapbox-directions.js/v0.4.0/mapbox.directions.css' type='text/css' />
" />
</head>
<body class="plugin-mapbox-driving-direction-mapview">
<section class="">
<div class="">
<div id='map'></div>
<div id='inputs'></div>
<div id='errors'></div>
<div id='directions'><div id='routes'></div><div id='instructions'></div></div>
<script>
var angular = window.parent.angular,
scope = window.parent.angular.element(window.frameElement).scope(),
initialization = true;
//console.log("angular", angular, "angular");
//console.log("scope", scope, "scope");
var startCoordinates = scope.__route[0],
endCoordinates = scope.__route[1];
var startLocation = {
lat: angular.isDefined(startCoordinates.lat) ? startCoordinates.lat : null,
lng: angular.isDefined(startCoordinates.lng) ? startCoordinates.lng : null,
loc: scope.pinOriginLocation
},
endLocation = {
lat: angular.isDefined(endCoordinates.lat) ? endCoordinates.lat : null,
lng: angular.isDefined(endCoordinates.lng) ? endCoordinates.lng : null,
loc: scope.pinDestinationLocation
},
origin = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [startLocation.lng, startLocation.lat]
},
properties: {
name: startLocation.loc
}
},
destination = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [endLocation.lng, endLocation.lat]
},
properties: {
name: endLocation.loc
}
};
L.mapbox.accessToken = "<?php echo $this->config->item('mapbox_api_accesstoken'); ?>";
var __journey = scope.__journey;
var map = L.mapbox.map('map', 'mapbox.streets', {zoomControl: false});
map.attributionControl.setPosition('bottomleft');
var directions = L.mapbox.directions();
var directionsLayer = L.mapbox.directions.layer(directions).addTo(map);
var directionsRoutesControl = L.mapbox.directions.routesControl('routes', directions).addTo(map);
var directionsInputControl = L.mapbox.directions.inputControl('inputs', directions).addTo(map);
var directionsErrorsControl = L.mapbox.directions.errorsControl('errors', directions).addTo(map);
var directionsInstructionsControl = L.mapbox.directions.instructionsControl('instructions', directions).addTo(map);
var wayPoints = [];
if(angular.isDefined(scope.__journey.__route) && scope.__journey.__route.length > 2) {
for(var i in scope.__journey.__route) {
if(scope.__journey.__route.length <= 20 || (wayPoints.length <= 20 && Number(i)%(Math.round(scope.__journey.__route.length / 20)) == 0))
wayPoints.push(L.latLng(scope.__journey.__route[i].latitude,scope.__journey.__route[i].longitude));
}
}
console.log("wayPoints", wayPoints, "wayPoints");
setQuery(startLocation, true, origin, endLocation, true, destination, wayPoints);
//directions.setOrigin(origin, true).setDestination(destination, true).query(undefined, false);
scope.$watch("pinOriginLocation", function(newval, oldval) {
if(angular.isDefined(newval) && newval.length >= 20 && newval !== oldval) {
startCoordinates = scope.getCordinates(newval);
startLocation = {
lat: angular.isDefined(startCoordinates.lat) ? startCoordinates.lat : null,
lng: angular.isDefined(startCoordinates.lng) ? startCoordinates.lng : null,
loc: scope.pinOriginLocation
};
origin = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [startLocation.lng, startLocation.lat]
},
properties: {
name: startLocation.loc
}
};
initialization = false;
setQuery(startLocation, undefined, origin, endLocation, undefined, destination, wayPoints);
//directions.setOrigin(origin).setDestination(destination).query(undefined, false);
}
});
scope.$watch("pinDestinationLocation", function(newval, oldval) {
if(angular.isDefined(newval) && newval.length >= 20 && newval !== oldval) {
endCoordinates = scope.getCordinates(newval);
endLocation = {
lat: angular.isDefined(endCoordinates.lat) ? endCoordinates.lat : null,
lng: angular.isDefined(endCoordinates.lng) ? endCoordinates.lng : null,
loc: scope.pinDestinationLocation
};
destination = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [endLocation.lng, endLocation.lat]
},
properties: {
name: endLocation.loc
}
};
initialization = false;
setQuery(startLocation, undefined, origin, endLocation, undefined, destination, wayPoints);
//directions.setOrigin(origin).setDestination(destination).query(undefined, false);
}
});
function suggestLocation(event, element) {
var el = $(element);
if(el.val().length >= 8 && event.keyCode == 32) {
$.ajax({
type : "POST",
url : siteVars.ajaxUrl+"webservice/lookupAddress",
datatype : "html",
data : {
"code" : siteVars.csrfHash,
"address" : el.val()
},
success: function(data) {
lookup = [];
if(!angular.equals(data, "")) {
data = JSON.parse(data);
for(var i in data) {
if(typeof data[i].location != 'undefined')
lookup.push(data[i].location);
}
displaySuggestion(el, lookup);
}
}
});
}
};
function displaySuggestion(element, lookup) {
var attr = element.attr("id") + "DataList";
var html = "";
$("#"+attr).remove();
if(lookup.length > 0) {
for(var i in lookup)
html += "<div onclick='selectSuggestion(\""+element.attr("id")+"\", \""+lookup[i]+"\")' class='autocomplete-suggestion' data-index='"+i+"'>"+lookup[i]+"</div>";
html = "<div class='autocomplete-suggestions' id='"+attr+"'>"+html+"</div>";
$(html).insertAfter(element);
}
};
function selectSuggestion(element, suggestion) {
var el = $("#"+element);
el.val(suggestion);
$("#"+el.attr("id")+"DataList").remove();
if(el.attr("id") == "mapbox-directions-origin-input")
directions.setOrigin(suggestion);
if(el.attr("id") == "mapbox-directions-destination-input")
directions.setDestination(suggestion);
if(directions.queryable()) {
initialization = false;
directions.query({proximity: map.getCenter()}, false);
}
}
function setQuery(startLocation, startLocationStatus, origin, endLocation, endLocationStatus, destination, wayPoints) {
if(!angular.isDefined(startLocation.loc) || angular.equals(startLocation.loc, "") || angular.equals(startLocation.loc, null))
directions.setOrigin(L.latLng(startLocation.lat, startLocation.lng), true);
else
directions.setOrigin(origin, true);
if(!angular.isDefined(endLocation.loc) || angular.equals(endLocation.loc, "") || angular.equals(endLocation.loc, null))
directions.setDestination(L.latLng(endLocation.lat, endLocation.lng), true);
else
directions.setDestination(destination, true);
if(angular.isDefined(wayPoints) && wayPoints.length > 0)
directions.setWaypoints(wayPoints);
//L.Routing.control({waypoints:wayPoints}).addTo(map);
console.log("wayPoints", wayPoints, "wayPoints");
return directions.query(undefined, false);
}
</script>
</div>
<?=$this->load->view("subviews/libraries_link")?>
</section>
</body>

Related

How to destroy and reinitialize angular directive -- on window resize?

I've tried invoking the directive below again on $(window).resize, and adding scope.$digest to it, but the directive does not respond.
I'm attempting to load responsive width and height (which are properties of scope.viewport and scope.boundary) to the options: boundary / viewport width and height -- on resize.
What is the proper way to accomplish this?
angular.module('ngCroppie', []).directive('ngCroppie', [
function ($compile) {
return {
restrict: 'AE',
scope:{
src: '=',
viewport: '=',
boundry: '=',
type: '#',
zoom: '#',
mousezoom: '#',
update: '=',
ngModel: '='
},
link: function(scope, elem, attr) {
// defaults
if(scope.viewport == undefined){
scope.viewport = {w: null, h: null}
}
if(scope.boundry == undefined){
scope.boundry = {w: null, h: null}
}
// catches
scope.viewport.w = (scope.viewport.w != undefined) ? scope.viewport.w : 300;
scope.viewport.h = (scope.viewport.h != undefined) ? scope.viewport.h : 300;
scope.boundry.w = (scope.boundry.w != undefined) ? scope.boundry.w : 400;
scope.boundry.h = (scope.boundry.h != undefined) ? scope.boundry.h : 400;
// viewport cannot be larger than the boundaries
if(scope.viewport.w > scope.boundry.w){ scope.viewport.w = scope.boundry.w }
if(scope.viewport.h > scope.boundry.h){ scope.viewport.h = scope.boundry.h }
// convert string to Boolean
var zoom = (scope.zoom === "true"),
mouseZoom = (scope.mousezoom === "true");
// define options
var options = {
viewport: {
width: scope.viewport.w,
height: scope.viewport.h,
type: scope.type || 'square'
},
boundary: {
width: scope.boundry.w,
height: scope.boundry.h
},
showZoom: zoom,
mouseWheelZoom: mouseZoom,
}
if (scope.update != undefined){
options.update = scope.update
}
// create new croppie and settime for updates
var c = new Croppie(elem[0], options);
var intervalID = window.setInterval(function(){
c.result('canvas').then(function(img){
scope.$apply(function(){
scope.ngModel = img
})
})
}, 250);
scope.$on("$destroy",
function( event ) {
clearInterval(intervalID);
}
);
// respond to changes in src
scope.$watch('src', function(newValue, oldValue) {
if(scope.src != undefined){
c.bind(scope.src);
c.result('canvas').then(function(img){
scope.$apply(function(){
scope.ngModel = img
})
})
}
})
}
};
}
]);
Here's my updated code. Now, my problem is that it keeps making a new image (that is correctly resized). I cannot figure out how to delete the initial image which needs to be replaced:
function croppieJs (){ !function(t,e){"function"==typeof define&&define.amd?define(["exports","b"],e):"object"==typeof exports&&"string"!=typeof exports.nodeName?e(exports,require("b")):e(t.commonJsStrict={},t.b)}(this,function(t,e){function n(t){if(t in j)return t;for(var e=t[0].toUpperCase()+t.slice(1),n=X.length;n--;)if(t=X[n]+e,t in j)return t}function r(t){t=t||{};for(var e=1;e<arguments.length;e++){var n=arguments[e];if(n)for(var o in n)n.hasOwnProperty(o)&&("object"==typeof n[o]?t[o]=r({},n[o]):t[o]=n[o])}return t}function o(t,e,n){var r;return function(){var o=this,i=arguments,s=function(){r=null,n||t.apply(o,i)},a=n&&!r;clearTimeout(r),r=setTimeout(s,e),a&&t.apply(o,i)}}function i(t){if("createEvent"in document){var e=document.createEvent("HTMLEvents");e.initEvent("change",!1,!0),t.dispatchEvent(e)}else t.fireEvent("onchange")}function s(t,e,n){if("string"==typeof e){var r=e;e={},e[r]=n}for(var o in e)t.style[o]=e[o]}function a(t){var e=t.points,n=document.createElement("div"),r=document.createElement("img"),o=e[2]-e[0],i=e[3]-e[1];return n.classList.add("croppie-result"),n.appendChild(r),s(r,{left:-1*e[0]+"px",top:-1*e[1]+"px"}),r.src=t.url,s(n,{width:o+"px",height:i+"px"}),n}function u(t,e){var n=e.points,r=n[0],o=n[1],i=n[2]-n[0],s=n[3]-n[1],a=e.circle,u=document.createElement("canvas"),l=u.getContext("2d"),c=i,p=s;return e.outputWidth&&e.outputHeight&&(c=e.outputWidth,p=e.outputHeight),u.width=c,u.height=p,a&&(l.save(),l.beginPath(),l.arc(c/2,p/2,c/2,0,2*Math.PI,!0),l.closePath(),l.clip()),l.drawImage(t,r,o,i,s,0,0,c,p),u.toDataURL()}function l(t,e){var n,r=e||new Image;return n=new Promise(function(t,e){r.setAttribute("crossOrigin","anonymous"),r.onload=function(){setTimeout(function(){t(r)},1)}}),r.src=t,n}function c(){var t,e,n,r,o=this,i=["croppie-container"],a=o.options.viewport.type?"cr-vp-"+o.options.viewport.type:null;o.data={},o.elements={},t=o.elements.boundary=document.createElement("div"),n=o.elements.viewport=document.createElement("div"),e=o.elements.img=document.createElement("img"),r=o.elements.overlay=document.createElement("div"),t.classList.add("cr-boundary"),s(t,{width:o.options.boundary.width+o.options.metric,height:o.options.boundary.height+o.options.metric}),n.classList.add("cr-viewport"),a&&n.classList.add(a),s(n,{width:o.options.viewport.width+o.options.metric,height:o.options.viewport.height+o.options.metric}),e.classList.add("cr-image"),r.classList.add("cr-overlay"),o.element.appendChild(t),t.appendChild(e),t.appendChild(n),t.appendChild(r),o.element.classList.add(i),o.options.customClass&&o.element.classList.add(o.options.customClass),g.call(this),o.options.showZoom&&m.call(o)}function p(t){this.options.showZoom&&(this.elements.zoomer.value=E(t))}function m(){function t(){h.call(s),r=new I(s.elements.img),o=s.elements.viewport.getBoundingClientRect(),i=F.parse(s.elements.img)}function e(){d.call(s,{value:parseFloat(u.value),origin:r||new I(s.elements.img),viewportRect:o||s.elements.viewport.getBoundingClientRect(),transform:i||F.parse(s.elements.img)})}function n(n){var r=n.deltaY/-2e3,o=s._currentZoom+r;n.preventDefault(),t(),p.call(s,o),e()}var r,o,i,s=this,a=s.elements.zoomerWrap=document.createElement("div"),u=s.elements.zoomer=document.createElement("input");a.classList.add("cr-slider-wrap"),u.type="range",u.classList.add("cr-slider"),u.step="0.01",u.value=1,s.element.appendChild(a),a.appendChild(u),s._currentZoom=1,s.elements.zoomer.addEventListener("mousedown",t),s.elements.zoomer.addEventListener("touchstart",t),s.elements.zoomer.addEventListener("input",e),s.elements.zoomer.addEventListener("change",e),s.options.mouseWheelZoom&&(s.elements.boundary.addEventListener("mousewheel",n),s.elements.boundary.addEventListener("DOMMouseScroll",n))}function d(t){var e=this,n=t.transform,r=t.viewportRect,o=t.origin;e._currentZoom=t.value,n.scale=e._currentZoom;var i=f.call(e,r),a=i.translate,u=i.origin;n.x>=a.maxX&&(o.x=u.minX,n.x=a.maxX),n.x<=a.minX&&(o.x=u.maxX,n.x=a.minX),n.y>=a.maxY&&(o.y=u.minY,n.y=a.maxY),n.y<=a.minY&&(o.y=u.maxY,n.y=a.minY);var l={};l[B]=n.toString(),l[M]=o.toString(),s(e.elements.img,l),O.call(e),y.call(e)}function f(t){var e=this,n=e._currentZoom,r=t.width,o=t.height,i=e.options.boundary.width/2,s=e.options.boundary.height/2,a=e._originalImageWidth,u=e._originalImageHeight,l=a*n,c=u*n,p=r/2,m=o/2,d=-1*(p/n-i),f=d-(l*(1/n)-r*(1/n)),h=-1*(m/n-s),g=h-(c*(1/n)-o*(1/n)),v=1/n*p,y=l*(1/n)-v,w=1/n*m,_=c*(1/n)-w;return{translate:{maxX:d,minX:f,maxY:h,minY:g},origin:{maxX:y,minX:v,maxY:_,minY:w}}}function h(){var t=this,e=t._currentZoom,n=t.elements.img.getBoundingClientRect(),r=t.elements.viewport.getBoundingClientRect(),o=F.parse(t.elements.img.style[B]),i=new I(t.elements.img),a=r.top-n.top+r.height/2,u=r.left-n.left+r.width/2,l={},c={};l.y=a/e,l.x=u/e,c.y=(l.y-i.y)*(1-e),c.x=(l.x-i.x)*(1-e),o.x-=c.x,o.y-=c.y;var p={};p[M]=l.x+"px "+l.y+"px",p[B]=o.toString(),s(t.elements.img,p)}function g(){function t(t){t.preventDefault(),c||(c=!0,r=t.pageX,o=t.pageY,transform=F.parse(l.elements.img),window.addEventListener("mousemove",e),window.addEventListener("touchmove",e),window.addEventListener("mouseup",n),window.addEventListener("touchend",n),document.body.style[Z]="none",u=l.elements.viewport.getBoundingClientRect())}function e(t){t.preventDefault();var e=t.pageX||t.touches[0].pageX,n=t.pageY||t.touches[0].pageY,c=e-r,m=n-o,d=l.elements.img.getBoundingClientRect(),f=transform.y+m,h=transform.x+c,g={};if("touchmove"==t.type&&t.touches.length>1){var y=t.touches[0],w=t.touches[1],_=Math.sqrt((y.pageX-w.pageX)*(y.pageX-w.pageX)+(y.pageY-w.pageY)*(y.pageY-w.pageY));a||(a=_/l._currentZoom);var x=_/a;return p.call(l,x),void i(l.elements.zoomer)}u.top>d.top+m&&u.bottom<d.bottom+m&&(transform.y=f),u.left>d.left+c&&u.right<d.right+c&&(transform.x=h),g[B]=transform.toString(),s(l.elements.img,g),v.call(l),o=n,r=e}function n(){c=!1,window.removeEventListener("mousemove",e),window.removeEventListener("touchmove",e),window.removeEventListener("mouseup",n),window.removeEventListener("touchend",n),document.body.style[Z]="",h.call(l),y.call(l),a=0}var r,o,a,u,l=this,c=!1;l.elements.overlay.addEventListener("mousedown",t),l.elements.overlay.addEventListener("touchstart",t)}function v(){var t=this,e=t.elements.boundary.getBoundingClientRect(),n=t.elements.img.getBoundingClientRect();s(t.elements.overlay,{width:n.width+"px",height:n.height+"px",top:n.top-e.top+"px",left:n.left-e.left+"px"})}function y(){var t=this;w.call(t)&&t.options.update.call(t,t.get())}function w(){return this.elements.img.offsetHeight>0&&this.elements.img.offsetWidth>0}function _(){var t,e,n,r,o,a=this,u=0,l=1.5,c=1,m={},d=a.elements.img,f=a.elements.zoomer,h=new F(0,0,c),g=new I,y=w.call(a);y&&!a.data.bound&&(a.data.bound=!0,m[B]=h.toString(),m[M]=g.toString(),s(d,m),t=d.getBoundingClientRect(),e=a.elements.viewport.getBoundingClientRect(),n=a.elements.boundary.getBoundingClientRect(),a._originalImageWidth=t.width,a._originalImageHeight=t.height,a.options.showZoom&&(r=e.width/t.width,o=e.height/t.height,u=Math.max(r,o),u>=l&&(l=u+1),f.min=E(u),f.max=E(l),c=Math.max(n.width/t.width,n.height/t.height),p.call(a,c),i(f)),a._currentZoom=h.scale=c,m[B]=h.toString(),s(d,m),a.data.points.length?x.call(a,a.data.points):b.call(a),v.call(a))}function x(t){if(4!=t.length)throw"Croppie - Invalid number of points supplied: "+t;var e=this,n=t[2]-t[0],r=e.elements.viewport.getBoundingClientRect(),o=e.elements.boundary.getBoundingClientRect(),i={left:r.left-o.left,top:r.top-o.top},a=r.width/n,u=t[1],l=t[0],c=-1*t[1]+i.top,m=-1*t[0]+i.left,d={};d[M]=l+"px "+u+"px",d[B]=new F(m,c,a).toString(),s(e.elements.img,d),p.call(e,a),e._currentZoom=a}function b(){var t=this,e=t.elements.img.getBoundingClientRect(),n=t.elements.viewport.getBoundingClientRect(),r=t.elements.boundary.getBoundingClientRect(),o=n.left-r.left,i=n.top-r.top,a=o-(e.width-n.width)/2,u=i-(e.height-n.height)/2,l=new F(a,u,t._currentZoom);s(t.elements.img,B,l.toString())}function C(t,e){var n,r=this,o=[];if("string"==typeof t)n=t,t={};else if(Array.isArray(t))o=t.slice();else{if("undefined"==typeof t&&r.data.url)return _.call(r),y.call(r),null;n=t.url,o=t.points||[]}r.data.bound=!1,r.data.url=n||r.data.url,r.data.points=(o||r.data.points).map(function(t){return parseFloat(t)});var i=l(n,r.elements.img);return i.then(function(){_.call(r),y.call(r),e&&e()}),i}function E(t){return parseFloat(t).toFixed(2)}function L(){var t=this,e=t.elements.img.getBoundingClientRect(),n=t.elements.viewport.getBoundingClientRect(),r=n.left-e.left,o=n.top-e.top,i=r+n.width,s=o+n.height,a=t._currentZoom;return(a===1/0||isNaN(a))&&(a=1),r=Math.max(0,r/a),o=Math.max(0,o/a),i=Math.max(0,i/a),s=Math.max(0,s/a),{points:[E(r),E(o),E(i),E(s)],zoom:a}}function S(t){var e,n,r=this,o=L.call(r),i=t||{type:"canvas",size:"viewport"},s="string"==typeof i?i:i.type,c=i.size||"viewport";return"viewport"===c&&(e=r.elements.viewport.getBoundingClientRect(),o.outputWidth=e.width,o.outputHeight=e.height),o.circle="circle"===r.options.viewport.type,o.url=r.data.url,n=new Promise(function(t,e){"canvas"===s?l(o.url).then(function(e){t(u(e,o))}):t(a(o))})}function A(){console.warn("Croppie.refresh() is deprecated. Please use Croppie.bind() without any arguments instead. refresh() will be removed in a later release."),_.call(this)}function R(){var t=this;t.element.removeChild(t.elements.boundary),t.options.showZoom&&t.element.removeChild(t.elements.zoomerWrap),delete t.elements}function Y(t,e){this.element=t,this.options=r({},Y.defaults,e),c.call(this)}"function"!=typeof Promise&&function(){"use strict";function t(t){return"function"==typeof t||"object"==typeof t&&null!==t}function e(t){return"function"==typeof t}function n(t){return"object"==typeof t&&null!==t}function r(t){W=t}function o(t){k=t}function i(){return function(){process.nextTick(c)}}function s(){return function(){T(c)}}function a(){var t=0,e=new Q(c),n=document.createTextNode("");return e.observe(n,{characterData:!0}),function(){n.data=t=++t%2}}function u(){var t=new MessageChannel;return t.port1.onmessage=c,function(){t.port2.postMessage(0)}}function l(){return function(){setTimeout(c,1)}}function c(){for(var t=0;q>t;t+=2){var e=G[t],n=G[t+1];e(n),G[t]=void 0,G[t+1]=void 0}q=0}function p(){try{var t=require,e=t("vertx");return T=e.runOnLoop||e.runOnContext,s()}catch(n){return l()}}function m(){}function d(){return new TypeError("You cannot resolve a promise with itself")}function f(){return new TypeError("A promises callback cannot return that same promise.")}function h(t){try{return t.then}catch(e){return et.error=e,et}}function g(t,e,n,r){try{t.call(e,n,r)}catch(o){return o}}function v(t,e,n){k(function(t){var r=!1,o=g(n,e,function(n){r||(r=!0,e!==n?_(t,n):b(t,n))},function(e){r||(r=!0,C(t,e))},"Settle: "+(t._label||" unknown promise"));!r&&o&&(r=!0,C(t,o))},t)}function y(t,e){e._state===$?b(t,e._result):e._state===tt?C(t,e._result):E(e,void 0,function(e){_(t,e)},function(e){C(t,e)})}function w(t,n){if(n.constructor===t.constructor)y(t,n);else{var r=h(n);r===et?C(t,et.error):void 0===r?b(t,n):e(r)?v(t,n,r):b(t,n)}}function _(e,n){e===n?C(e,d()):t(n)?w(e,n):b(e,n)}function x(t){t._onerror&&t._onerror(t._result),L(t)}function b(t,e){t._state===V&&(t._result=e,t._state=$,0!==t._subscribers.length&&k(L,t))}function C(t,e){t._state===V&&(t._state=tt,t._result=e,k(x,t))}function E(t,e,n,r){var o=t._subscribers,i=o.length;t._onerror=null,o[i]=e,o[i+$]=n,o[i+tt]=r,0===i&&t._state&&k(L,t)}function L(t){var e=t._subscribers,n=t._state;if(0!==e.length){for(var r,o,i=t._result,s=0;s<e.length;s+=3)r=e[s],o=e[s+n],r?R(n,r,o,i):o(i);t._subscribers.length=0}}function S(){this.error=null}function A(t,e){try{return t(e)}catch(n){return nt.error=n,nt}}function R(t,n,r,o){var i,s,a,u,l=e(r);if(l){if(i=A(r,o),i===nt?(u=!0,s=i.error,i=null):a=!0,n===i)return void C(n,f())}else i=o,a=!0;n._state!==V||(l&&a?_(n,i):u?C(n,s):t===$?b(n,i):t===tt&&C(n,i))}function Y(t,e){try{e(function(e){_(t,e)},function(e){C(t,e)})}catch(n){C(t,n)}}function M(t,e){var n=this;n._instanceConstructor=t,n.promise=new t(m),n._validateInput(e)?(n._input=e,n.length=e.length,n._remaining=e.length,n._init(),0===n.length?b(n.promise,n._result):(n.length=n.length||0,n._enumerate(),0===n._remaining&&b(n.promise,n._result))):C(n.promise,n._validationError())}function B(t){return new rt(this,t).promise}function Z(t){function e(t){_(o,t)}function n(t){C(o,t)}var r=this,o=new r(m);if(!H(t))return C(o,new TypeError("You must pass an array to race.")),o;for(var i=t.length,s=0;o._state===V&&i>s;s++)E(r.resolve(t[s]),void 0,e,n);return o}function X(t){var e=this;if(t&&"object"==typeof t&&t.constructor===e)return t;var n=new e(m);return _(n,t),n}function j(t){var e=this,n=new e(m);return C(n,t),n}function P(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function z(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")}function F(t){this._id=ut++,this._state=void 0,this._result=void 0,this._subscribers=[],m!==t&&(e(t)||P(),this instanceof F||z(),Y(this,t))}function I(){var t;if("undefined"!=typeof global)t=global;else if("undefined"!=typeof self)t=self;else try{t=Function("return this")()}catch(e){throw new Error("polyfill failed because global object is unavailable in this environment")}var n=t.Promise;(!n||"[object Promise]"!==Object.prototype.toString.call(n.resolve())||n.cast)&&(t.Promise=lt)}var O;O=Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)};var T,W,D,H=O,q=0,k=({}.toString,function(t,e){G[q]=t,G[q+1]=e,q+=2,2===q&&(W?W(c):D())}),N="undefined"!=typeof window?window:void 0,U=N||{},Q=U.MutationObserver||U.WebKitMutationObserver,J="undefined"!=typeof process&&"[object process]"==={}.toString.call(process),K="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel,G=new Array(1e3);D=J?i():Q?a():K?u():void 0===N&&"function"==typeof require?p():l();var V=void 0,$=1,tt=2,et=new S,nt=new S;M.prototype._validateInput=function(t){return H(t)},M.prototype._validationError=function(){return new Error("Array Methods must be provided an Array")},M.prototype._init=function(){this._result=new Array(this.length)};var rt=M;M.prototype._enumerate=function(){for(var t=this,e=t.length,n=t.promise,r=t._input,o=0;n._state===V&&e>o;o++)t._eachEntry(r[o],o)},M.prototype._eachEntry=function(t,e){var r=this,o=r._instanceConstructor;n(t)?t.constructor===o&&t._state!==V?(t._onerror=null,r._settledAt(t._state,e,t._result)):r._willSettleAt(o.resolve(t),e):(r._remaining--,r._result[e]=t)},M.prototype._settledAt=function(t,e,n){var r=this,o=r.promise;o._state===V&&(r._remaining--,t===tt?C(o,n):r._result[e]=n),0===r._remaining&&b(o,r._result)},M.prototype._willSettleAt=function(t,e){var n=this;E(t,void 0,function(t){n._settledAt($,e,t)},function(t){n._settledAt(tt,e,t)})};var ot=B,it=Z,st=X,at=j,ut=0,lt=F;F.all=ot,F.race=it,F.resolve=st,F.reject=at,F._setScheduler=r,F._setAsap=o,F._asap=k,F.prototype={constructor:F,then:function(t,e){var n=this,r=n._state;if(r===$&&!t||r===tt&&!e)return this;var o=new this.constructor(m),i=n._result;if(r){var s=arguments[r-1];k(function(){R(r,o,s,i)})}else E(n,o,t,e);return o},"catch":function(t){return this.then(null,t)}};var ct=I,pt={Promise:lt,polyfill:ct};"function"==typeof define&&define.amd?define(function(){return pt}):"undefined"!=typeof module&&module.exports?module.exports=pt:"undefined"!=typeof this&&(this.ES6Promise=pt),ct()}.call(this);var M,B,Z,X=["Webkit","Moz","ms"],j=document.createElement("div").style;B=n("transform"),M=n("transformOrigin"),Z=n("userSelect");var P="translate3d",z=", 0px",F=function(t,e,n){this.x=parseFloat(t),this.y=parseFloat(e),this.scale=parseFloat(n)};F.parse=function(t){return t.style?F.parse(t.style[B]):t.indexOf("matrix")>-1||t.indexOf("none")>-1?F.fromMatrix(t):F.fromString(t)},F.fromMatrix=function(t){var e=t.substring(7).split(",");return e.length&&"none"!==t||(e=[1,0,0,1,0,0]),new F(parseInt(e[4],10),parseInt(e[5],10),parseFloat(e[0]))},F.fromString=function(t){var e=t.split(") "),n=e[0].substring(P.length+1).split(","),r=e.length>1?e[1].substring(6):1,o=n.length>1?n[0]:0,i=n.length>1?n[1]:0;return new F(o,i,r)},F.prototype.toString=function(){return P+"("+this.x+"px, "+this.y+"px"+z+") scale("+this.scale+")"};var I=function(t){if(!t||!t.style[M])return this.x=0,void(this.y=0);var e=t.style[M].split(" ");this.x=parseFloat(e[0]),this.y=parseFloat(e[1])};I.prototype.toString=function(){return this.x+"px "+this.y+"px"};var O=o(v,500);if(this.jQuery){var T=this.jQuery;T.fn.croppie=function(t){var e=typeof t;if("string"===e){var n=Array.prototype.slice.call(arguments,1),r=T(this).data("croppie");return"get"===t?r.get():"result"===t?r.result.apply(r,n):this.each(function(){var e=T(this).data("croppie");if(e){var r=e[t];if(!T.isFunction(r))throw"Croppie "+t+" method not found";r.apply(e,n),"destroy"===t&&T(this).removeData("croppie")}})}return this.each(function(){var e=new Y(this,t);T(this).data("croppie",e)})}}Y.defaults={viewport:{width:100,height:100,type:"square"},boundary:{width:300,height:300},customClass:"",showZoom:!0,mouseWheelZoom:!0,update:function(){}},r(Y.prototype,{bind:function(t,e){return C.call(this,t,e)},get:function(){return L.call(this)},result:function(t){return S.call(this,t)},refresh:function(){return A.call(this)},destroy:function(){return R.call(this)}}),t.Croppie=window.Croppie=Y})};
croppieJs();
/*************************
* acrCroppie
* Allen Royston
* Version: 1.0.0
* Updated 4/12/2016
*************************/
angular.module('ngCroppie', []).directive('ngCroppie', [
function ($compile, $window) {
return {
restrict: 'AE',
scope:{
src: '=',
viewport: '=',
boundry: '=',
type: '#',
zoom: '#',
mousezoom: '#',
responsive: '#',
update: '=',
metric: '#',
ngModel: '='
},
link: function(scope, elem, attr) {
// defaults
if(scope.viewport == undefined){
scope.viewport = {w: null, h: null}
}
if(scope.boundry == undefined){
scope.boundry = {w: null, h: null}
}
// catches
scope.viewport.w = (scope.viewport.w != undefined) ? scope.viewport.w : 300;
scope.viewport.h = (scope.viewport.h != undefined) ? scope.viewport.h : 300;
scope.boundry.w = (scope.boundry.w != undefined) ? scope.boundry.w : 400;
scope.boundry.h = (scope.boundry.h != undefined) ? scope.boundry.h : 400;
// viewport cannot be larger than the boundaries
if(scope.viewport.w > scope.boundry.w){ scope.viewport.w = scope.boundry.w }
if(scope.viewport.h > scope.boundry.h){ scope.viewport.h = scope.boundry.h }
// convert string to Boolean
var zoom = (scope.zoom === "true"),
mouseZoom = (scope.mousezoom === "true");
// define options
var options = {
viewport: {
width: scope.viewport.w,
height: scope.viewport.h,
type: scope.type || 'square'
},
boundary: {
width: scope.boundry.w,
height: scope.boundry.h
},
metric: scope.metric,
showZoom: zoom,
mouseWheelZoom: mouseZoom,
}
function init(){
if (scope.update != undefined){
options.update = scope.update
}
console.log('scope: ', scope)
// create new croppie and settime for updates
var c = new Croppie(elem[0], options);
var intervalID = window.setInterval(function(){
c.result('canvas').then(function(img){
scope.$apply(function(){
scope.ngModel = img
})
})
}, 250);
console.log('ccc : ', c)
scope.$on("$destroy",
function( event ) {
clearInterval(intervalID);
$(window).off('resize', function(){
responsive()});
}
);
// respond to changes in src
scope.$watch('src', function(newValue, oldValue) {
if(scope.src != undefined){
c.bind(scope.src);
c.result('canvas').then(function(img){
scope.$apply(function(){
scope.ngModel = img
})
})
}
})
};
if(scope.responsive === 'true'){
function responsive(){
console.log('responsive')
// defaults
if(scope.viewport == undefined){
scope.viewport = {w: null, h: null}
}
if(scope.boundry == undefined){
scope.boundry = {w: null, h: null}
}
// convert string to Boolean
var zoom = (scope.zoom === "true"),
mouseZoom = (scope.mousezoom === "true");
scope.width = $('.modal-md form').width()
scope.height = scope.width * .264;
console.log('width : ', scope.width);
// define options
options = {
viewport: {
width: scope.width,
height: scope.height,
type: scope.type || 'square'
},
boundary: {
width: scope.width,
height: scope.height
},
metric: scope.metric,
showZoom: zoom,
mouseWheelZoom: mouseZoom,
}
init();
};
responsive();
$(window).on('resize', function(){
scope.$digest(function(){
delete scope.$$watchers[0];
scope.$$watchersCount = scope.$$watchers.length;
});
responsive();
clearInterval();
console.log('ccc : ', c)
});
}
else{
init();
}
}
};
}
]);
I think you just need to can put all the link part of your directive in a function along with the variables you need to reinitiate and put them in a function and invoke that function on window.resize event, let me show you
angular.module('ngCroppie',[]).directive('ngCroppie','$document','$log', [
function ($compile) {
return {
restrict: 'AE',
scope:{
src: '=',
viewport: '=',
boundry: '=',
type: '#',
zoom: '#',
mousezoom: '#',
update: '=',
ngModel: '='
},
link: function(scope, elem, attr) {
function init()={
// defaults
if(scope.viewport == undefined){
scope.viewport = {w: null, h: null}
}
if(scope.boundry == undefined){
scope.boundry = {w: null, h: null}
}
// catches
scope.viewport.w = (scope.viewport.w != undefined) ? scope.viewport.w : 300;
scope.viewport.h = (scope.viewport.h != undefined) ? scope.viewport.h : 300;
scope.boundry.w = (scope.boundry.w != undefined) ? scope.boundry.w : 400;
scope.boundry.h = (scope.boundry.h != undefined) ? scope.boundry.h : 400;
// viewport cannot be larger than the boundaries
if(scope.viewport.w > scope.boundry.w){ scope.viewport.w = scope.boundry.w }
if(scope.viewport.h > scope.boundry.h){ scope.viewport.h = scope.boundry.h }
// convert string to Boolean
var zoom = (scope.zoom === "true"),
mouseZoom = (scope.mousezoom === "true");
// define options
var options = {
viewport: {
width: scope.viewport.w,
height: scope.viewport.h,
type: scope.type || 'square'
},
boundary: {
width: scope.boundry.w,
height: scope.boundry.h
},
showZoom: zoom,
mouseWheelZoom: mouseZoom,
}
if (scope.update != undefined){
options.update = scope.update
}
// create new croppie and settime for updates
var c = new Croppie(elem[0], options);
}
init();
//window resizing event
$document.bind('click', function(event) {
$log.info(event);
init()://intiating the variables again
});
var intervalID = window.setInterval(function(){
c.result('canvas').then(function(img){
scope.$apply(function(){
scope.ngModel = img
})
})
}, 250);
scope.$on("$destroy",
function( event ) {
clearInterval(intervalID);
}
);
// respond to changes in src
scope.$watch('src', function(newValue, oldValue) {
if(scope.src != undefined){
c.bind(scope.src);
c.result('canvas').then(function(img){
scope.$apply(function(){
scope.ngModel = img
})
})
}
})
}
};
}
]);
Try it out!
PS: unbind the resize event when you destroy the scope of directive, that can be done pretty much easily i guess
Did you try listening to the resize event in the link function?
link: function(scope, element, attrs) {
scope.doSomething = function() {
//example of accessing element's attribute
var width = $(element).width();
};
$window.addEventListener("resize", scope.doSomenthing);
}

Occasional AngularJS error when directive is compiling

I have an AngularJS directive that renders into a custom social share widget. Out of about 10,000 page views per day, around 1 or 2 of those times, Angular errors out after starting to compile the directive. This leaves the raw HTML partial in the DOM, visible to the user.
I only came to know of this error because it was reported by several users. I can't reproduce it, but I have devised some informative logging which shows that it is occurring.
Each time this has occurred:
Browser is always Chrome
OS is Mac or Windows
Angular starts the compile phase, but fails before starting post link
Angular reports an error during the compile phase, but the 'exception' object passed to the '$exceptionHandler' service is always null.
No other JavaScript errors are reported
This error is occurring for some of the same IPs across multiple days.
Has anyone out there had a similar issue?
Edit
Here's my code...
JavaScript:
(function () {
angular.module('common', []);
angular.module('common')
.filter('encodeURIComponent', function () {
return window.encodeURIComponent;
});
function configure($provide) {
// Pass all Angular errors to Loggly
$provide.decorator("$exceptionHandler", function ($delegate) {
return function exceptionHandlerDecorator(exception, cause) {
$delegate(exception, cause);
_LTracker.push({
'error': 'angularError',
'app': 'shareCounts',
'err': exception,
'element': cause
});
};
});
}
angular.module('common')
.config(['$provide', configure]);
function configure($provide) {
// Defines available share options as well as behaviors of the share popup windows
function shareLinksConfig() {
return {
'facebook': {
width: 670,
height: 200,
urlBase: 'https://www.facebook.com/sharer/sharer.php?',
shareParamPre: 'u=',
msgParamPre: '',
mediaParamPre: '',
addParams: ''
},
'twitter': {
width: 550,
height: 420,
urlBase: 'https://twitter.com/intent/tweet?',
shareParamPre: 'url=',
msgParamPre: '&text=',
mediaParamPre: ''
},
'googlePlus': {
width: 600,
height: 600,
urlBase: 'https://plus.google.com/share?',
shareParamPre: 'url=',
msgParamPre: '',
mediaParamPre: '',
addParams: ''
},
'linkedIn': {
width: 600,
height: 400,
urlBase: 'http://www.linkedin.com/shareArticle?',
shareParamPre: 'url=',
msgParamPre: '',
mediaParamPre: '',
addParams: '&mini=true'
},
'pinterest': {
width: 750,
height: 320,
urlBase: 'https://www.pinterest.com/pin/create/button/?',
shareParamPre: 'url=',
msgParamPre: '&description=',
mediaParamPre: '&media=',
addParams: ''
},
'email': {
width: 0,
height: 0,
urlBase: '',
shareParamPre: '',
msgParamPre: '',
mediaParamPre: '',
addParams: ''
}
};
}
$provide.factory('shareLinksConfig', shareLinksConfig);
}
angular.module('common')
.config(['$provide', configure]);
function ShareLinksController($scope, shareLinksService) {
sendToLoggly.push("A \"ShareLinksController\" started constructing...");
sendToLoggly.push("...and the $scope is typeof...");
sendToLoggly.push(typeof $scope);
var vm = this;
vm.share = function ($event, shareVia) {
if (shareVia !== 'email') {
$event.preventDefault();
// console.log($scope.mediaUrl);
shareLinksService.openPopUp(shareVia, $scope.shareUrl, $scope.shareMsg, $scope.mediaUrl);
}
// Tell Google Analytics share link was clicked
shareLinksService.pushGAEvent($scope.analyticsLocation, shareVia, $scope.shareUrl);
};
$scope.shareLinksShown = true; // Initialized to true, but then this gets set to false in the directive's link function if slideIn is true
vm.toggle = function ($event) {
$event.preventDefault();
$scope.shareLinksShown = !$scope.shareLinksShown;
};
sendToLoggly.push("...and controller finished constructing.");
}
angular.module('common')
.controller('ShareLinksController', ["$scope", "shareLinksService",
ShareLinksController]);
function fuShareLinks($http, shareLinksConfig, testRenderingService) {
function compile() {
sendToLoggly.push("A \"fuShareLinks\" directive started compiling...");
testRenderingService.testShareCounts();
return function postLink(scope) {
sendToLoggly.push("A \"fuShareLinks\" directive started postLinking...");
function Settings(shareVia, slideInDir, slideToggleLabel, colorized, showCounts) {
var self = this,
prop,
splitArray;
/* --------
ShareVia
--------
Comma separated list of ways to share
Accepted options are: 'facebook, twitter, googlePlus, linkedIn, pinterest, email' */
// Copy the properties from the config and initialize to false
self.shareVia = {};
for (prop in shareLinksConfig) {
if (shareLinksConfig.hasOwnProperty(prop)) {
self.shareVia[prop] = false;
}
}
if (typeof shareVia === 'string') {
splitArray = shareVia.split(',');
} else {
splitArray = [];
}
// Check each value of splitArray, if it is in possible share options,
// set that option to true.
angular.forEach(splitArray, function (value) {
// Clean up 'value' a bit by removing spaces
value = value.trim();
if (value.length > 0) {
if (self.shareVia.hasOwnProperty(value)) {
self.shareVia[value] = true;
}
}
});
/* --------
Slide In
--------
The slide-in functionality is activated by passing a value to 'slideInDir'.
Accepted values are 'left' or 'down' (case insensitive)
The 'slideToggleLabel' can be any string, if empty, it defaults to 'Share'. */
self.slideIn = {
direction: '',
label: 'Share'
};
if (typeof slideInDir === 'string') {
slideInDir = slideInDir.toUpperCase();
}
switch (slideInDir) {
case 'LEFT':
self.slideIn.direction = 'left';
break;
case 'DOWN':
self.slideIn.direction = 'down';
break;
}
if (typeof slideToggleLabel === 'string') {
self.slideIn.label = slideToggleLabel;
}
/* ---------
Colorized
---------
'true', 'yes', or 'colorized' (case insensitive) -- results in true
defaults to false */
self.colorized = false;
if (typeof colorized === 'string') {
colorized = colorized.toUpperCase();
}
switch (colorized) {
case 'TRUE':
self.colorized = true;
break;
case 'YES':
self.colorized = true;
break;
case 'COLORIZED':
self.colorized = true;
break;
}
/* -----------
Show Counts
-----------
'true', 'yes', or 'show' (case insensitive) -- results in true
defaults to false */
self.showCounts = false;
if (typeof showCounts === 'string') {
showCounts = showCounts.toUpperCase();
}
switch (showCounts) {
case 'TRUE':
self.showCounts = true;
break;
case 'YES':
self.showCounts = true;
break;
case 'SHOW':
self.showCounts = true;
break;
}
}
scope.settings = new Settings(
scope.shareVia,
scope.slideInDir,
scope.slideToggleLabel,
scope.colorized,
scope.showCounts
);
// Initally hide the share links, if they are set to toggle
if (scope.settings.slideIn.direction !== '') {
scope.shareLinksShown = false;
}
function ShareCounts(shareVia) {
var self = this;
angular.forEach(shareVia, function (value, name) {
self[name] = 0;
});
$http.get(
'/local/social-share-counts/?url=' +
encodeURIComponent(scope.shareUrl)
).success(function (data) {
/* Check for share counts in the returned data.
Must use consistent naming for the social networks
from shareLinksConfig properties all the way to the
JSON data containting the counts.
Expected JSON format:
{
"twitter": {
"count": 42,
"updated": "2015-03-25T15:13:48.355422"
},
"facebook": {
"count": 120,
"updated": "2015-03-25T15:13:47.470778"
}
}
*/
angular.forEach(shareVia, function (value, name) {
if (data[name] && data[name]["count"]) {
self[name] = data[name]["count"];
}
});
}).error(function (data, status) {
sendToLoggly.push("HTTP Response " + status);
});
}
// If showing share counts, get the counts from the specified networks
if (scope.settings.showCounts) {
scope.shareCounts = new ShareCounts(scope.settings.shareVia);
}
sendToLoggly.push("...and directive finished postLinking.");
};
sendToLoggly.push("...and directive finished compiling.");
}
return {
restrict: 'E',
scope: {
shareVia: '#',
shareUrl: '#',
shareMsg: '#',
mediaUrl: '#',
analyticsLocation: '#',
slideInDir: '#',
slideToggleLabel: '#',
colorized: '#',
showCounts: '#'
},
controller: 'ShareLinksController',
controllerAs: 'shrLnksCtrl',
templateUrl: '/angular-partials/common.share-links.html',
compile: compile
};
}
angular.module('common')
.directive('fuShareLinks', ['$http', 'shareLinksConfig', 'testRenderingService', fuShareLinks])
.factory('testRenderingService', function () {
var timerId = null;
function evalShareRender() {
var renderError = (-1 < $('em.ng-binding')
.text()
.indexOf('{{'));
if (renderError) {
console.error('RENDER ERROR');
_LTracker.push({
'error': 'rendering',
'app': 'shareCounts',
'statusMsgs': sendToLoggly,
'userAgent': navigator.userAgent
});
}
}
return {
testShareCounts: function () {
if (!timerId) {
timerId = window.setTimeout(evalShareRender, 5000);
}
}
};
});
function shareLinksService(shareLinksConfig) {
function openPopUp(shareVia, shareUrl, shareMsg, mediaUrl) {
var width,
height,
urlBase,
shareParamPre,
msgParamPre,
mediaParamPre,
addParams,
popUpUrl;
width = shareLinksConfig[shareVia].width;
height = shareLinksConfig[shareVia].height;
urlBase = shareLinksConfig[shareVia].urlBase;
shareParamPre = shareLinksConfig[shareVia].shareParamPre;
msgParamPre = shareLinksConfig[shareVia].msgParamPre;
mediaParamPre = shareLinksConfig[shareVia].mediaParamPre;
addParams = shareLinksConfig[shareVia].addParams;
popUpUrl = encodeURI(urlBase);
popUpUrl += encodeURI(shareParamPre);
popUpUrl += encodeURIComponent(shareUrl);
if (msgParamPre && shareMsg) {
popUpUrl += encodeURI(msgParamPre);
popUpUrl += encodeURIComponent(shareMsg);
}
if (mediaParamPre && mediaUrl) {
popUpUrl += encodeURI(mediaParamPre);
popUpUrl += encodeURIComponent(mediaUrl);
}
popUpUrl += encodeURI(addParams);
// Open the social share window
window.open(popUpUrl, '_blank', 'width=' + width + ',height=' + height);
}
function pushGAEvent(analyticsLocation, shareVia, shareUrl) {
function capitalize(firstLetter) {
return firstLetter.toUpperCase();
}
var gaEventAction = shareVia;
gaEventAction = gaEventAction.replace(/^[a-z]/, capitalize);
gaEventAction += ' - Clicked';
_gaq.push([
'_trackEvent',
analyticsLocation + ' - SocialShare',
gaEventAction,
shareUrl
]);
}
return {
openPopUp: openPopUp,
pushGAEvent: pushGAEvent
};
}
angular.module('common')
.factory('shareLinksService', ['shareLinksConfig', shareLinksService]);
}());
HTML:
<div class="share-links-wrapper" ng-class="{ 'right': settings.slideIn.direction === 'left', 'center': settings.slideIn.direction === 'down' }" ng-cloak>
<a href="#" class="toggle" ng-show="settings.slideIn.direction != ''" ng-click="shrLnksCtrl.toggle($event)">
<i class="fuicon-share"></i>{{ settings.slideIn.label }}
</a>
<div class="share-links" ng-class="{ 'share-links-colorized': settings.colorized }" ng-show="shareLinksShown">
<ul>
<li ng-show="settings.shareVia.facebook">
<a href="#" ng-click="shrLnksCtrl.share($event, 'facebook')"
class="fuicon-hex-facebook">
</a>
<em ng-show="settings.showCounts && shareCounts.facebook > 0">
{{ shareCounts.facebook }}
</em>
</li>
<li ng-show="settings.shareVia.twitter">
<a href="#" ng-click="shrLnksCtrl.share($event, 'twitter')"
class="fuicon-hex-twitter">
</a>
<em ng-show="settings.showCounts && shareCounts.twitter > 0">
{{ shareCounts.twitter }}
</em>
</li>
<li ng-show="settings.shareVia.googlePlus">
<a href="#" ng-click="shrLnksCtrl.share($event, 'googlePlus')"
class="fuicon-hex-googleplus">
</a>
<em ng-show="settings.showCounts && shareCounts.googlePlus > 0">
{{ shareCounts.googlePlus }}
</em>
</li>
<li ng-show="settings.shareVia.linkedIn">
<a href="#" ng-click="shrLnksCtrl.share($event, 'linkedIn')"
class="fuicon-hex-linkedin">
</a>
<em ng-show="settings.showCounts && shareCounts.linkedIn > 0">
{{ shareCounts.linkedIn }}
</em>
</li>
<li ng-show="settings.shareVia.pinterest && mediaUrl">
<a href="#" ng-click="shrLnksCtrl.share($event, 'pinterest')"
class="fuicon-hex-pinterest">
</a>
<em ng-show="settings.showCounts && shareCounts.pinterest > 0">
{{ shareCounts.pinterest }}
</em>
</li>
<li ng-show="settings.shareVia.email">
<a href="mailto:?subject={{ shareMsg | encodeURIComponent }}
&body={{ shareUrl | encodeURIComponent }}"
ng-click="shrLnksCtrl.share($event, 'email')"
class="fuicon-hex-email">
</a>
</li>
</ul>
</div>
</div>
Not had such an issue, but as its so infrequent could you make it reload the page?
Also, do you know about ng-cloak? it can be useful to hide the raw stuff :)
Could it be a race condition?

How can I fix this angular memory leak?

Here's my js
var app = angular.module('SearchAndResultsApp', ['angularUtils.directives.dirPagination']);
app.controller('SearchAndResultsController', function($location ,$scope, $http){
$scope.fields = [
{ id: 'accountNumber', name: 'Account Number', clicked: false},
{ id: 'pointOfInterestName', name: 'Point Of Interest Name', clicked: true},
{ id: 'address1', name: 'Address 1', clicked: false},
{ id: 'address2', name: 'Address 2', clicked: false},
{ id: 'city', name: 'City', clicked: false},
{ id: 'isostateCode', name: 'ISO State Code', clicked: false},
{ id: 'postalCode', name: 'Postal Code', clicked: false},
{ id: 'phone', name: 'Phone', clicked: false},
{ id: 'fax', name: 'Fax', clicked: false},
{ id: 'website', name: 'Website', clicked: false},
{ id: 'email', name: 'Email', clicked: false},
{ id: 'isocountryCode', name: 'ISO Country Code', clicked: false},
{ id: 'latitude', name: 'Latitude', clicked: false},
{ id: 'longitude', name: 'Longitude', clicked: false}
];
$scope.getSearchState = function() {
var fields = window.location.hash.substring(1).split("&");
if (fields.length > 0) {
return decodeURIComponent(fields[0]);
} else {
return '';
}
};
$scope.getPageState = function() {
var fields = window.location.hash.substring(1).split("&");
if (fields.length > 1) {
return parseInt(fields[1]);
} else {
return 1;
}
};
$scope.noResults = false;
$scope.pointsOfInterest = null;
$scope.loadingQuery = false;
$scope.orderDirection = 'asc';
$scope.glyphDirection = 'glyphicon-triangle-top';
$scope.orderedBy = 'pointOfInterestName';
$scope.previousClicked = $scope.fields[1];
$scope.lowerRange = 0;
$scope.upperRange = 0;
$scope.totalRange = 0;
$scope.currentPage = $scope.getPageState();
$scope.searchString = $scope.getSearchState();
$scope.offset = 'col-sm-offset-4';
$scope.loadingOffset = 'col-sm-offset-1';
$scope.getResultsState = function() {
return ((($scope.pointsOfInterest == null)||($scope.pointsOfInterest[0] == null)) && ($scope.searchString != ''))
};
$scope.downloadCSV = function(selection) {
if (selection == 'searched') {
window.location = 'pointsOfInterest/' + encodeURIComponent($scope.searchString) + '/orderedList/' + $scope.orderedBy + '/' + $scope.orderDirection + '/csv';
} else if (selection == 'all') {
if (confirm("This may take some time.")){
window.location = 'allPointsOfInterest/csv';
}
}
};
$scope.updateRanges = function() {
$scope.searchString = $scope.searchString = $scope.searchString.replace(/\//g,'').replace(/\\/g,'');
window.location.hash = encodeURIComponent($scope.searchString) + '&' + encodeURIComponent($scope.currentPage);
if ($scope.pointsOfInterest[0] != null) {
$scope.lowerRange = ($scope.currentPage - 1) * 20 + 1;
$scope.totalRange = $scope.pointsOfInterest.length;
$scope.upperRange = $scope.currentPage * 20;
} else {
$scope.lowerRange = 0;
$scope.totalRange = 0;
$scope.upperRange = 0;
}
if ($scope.upperRange > $scope.totalRange) {
$scope.upperRange = $scope.totalRange;
}
};
$scope.updateGlyphs = function(field) {
$scope.searchString = $scope.searchString = $scope.searchString.replace(/\//g,'').replace(/\\/g,'');
$scope.orderedBy = field.id;
if ($scope.previousClicked != null)
$scope.previousClicked.clicked = false;
field.clicked = true;
if ($scope.previousClicked == field) {
if ($scope.orderDirection == 'asc') {
$scope.orderDirection = 'desc';
$scope.glyphDirection = 'glyphicon-triangle-bottom';
} else {
$scope.orderDirection = 'asc';
$scope.glyphDirection = 'glyphicon-triangle-top';
}
} else {
$scope.orderDirection = 'asc';
$scope.glyphDirection = 'glyphicon-triangle-top';
}
$scope.updatePointsOfInterest();
$scope.previousClicked = field;
};
$scope.updatePointsOfInterest = function() {
if ($scope.searchString.length != 0) {
$scope.loadingOffset = '';
$scope.currentPage = $scope.getPageState();
$scope.searchString = $scope.searchString.replace(/\//g,'').replace(/\\/g,'');
window.location.hash = encodeURIComponent($scope.searchString) + '&' + encodeURIComponent($scope.currentPage);
if ($scope.searchString == '') return;
$scope.loadingQuery = true;
$http.get('pointsOfInterest/' + encodeURIComponent($scope.searchString) + '/orderedList/' + $scope.orderedBy + '/' + $scope.orderDirection)
.success(function(data) {
$scope.pointsOfInterest = data;
$scope.loadingQuery = false;
$scope.loadingOffset = 'col-sm-offset-1';
$scope.updateRanges();
$scope.noResults = $scope.getResultsState();
if ($scope.pointsOfInterest.length > 0) {
$scope.offset = '';
} else {
$scope.offset = 'col-sm-offset-4';
}
})
.error(function(data) {
window.location = 'pointsOfInterest/' + encodeURIComponent($scope.searchString) + '/orderedList/' + $scope.orderedBy + '/' + $scope.orderDirection;
});
}
};
window.onhashchange = function() {
$scope.updatePointsOfInterest();
}
$scope.updatePointsOfInterest();
$scope.gotoUpdatePage = function (accountNumber) {
window.location.href = 'pointOfInterestEditor/' + accountNumber;
};
$scope.onTextClick = function ($event) {
$event.target.select();
};
});
app.directive('ngDelay', ['$timeout', function ($timeout) {
return {
restrict: 'A',
scope: true,
compile: function (element, attributes) {
var expression = attributes['ngChange'];
if (!expression)
return;
var ngModel = attributes['ngModel'];
if (ngModel) attributes['ngModel'] = '$parent.' + ngModel;
attributes['ngChange'] = '$$delay.execute()';
return {
post: function (scope, element, attributes) {
scope.$$delay = {
expression: expression,
delay: scope.$eval(attributes['ngDelay']),
execute: function () {
var state = scope.$$delay;
state.then = Date.now();
$timeout(function () {
if (Date.now() - state.then >= state.delay)
scope.$parent.$eval(expression);
}, state.delay);
}
};
}
}
}
};
}]);
I get a memory leak from adding in the $location to the controller. I would like to use $location instead of window.location. I'm not sure why the memory leak is caused. Anyone know why and how to fix it?
Edit: It appears to be making infinite get requests. Still not sure how to fix it.
First of all $location service has a hash() method that u can use instead of window.location.hash().
Second: you call $scope.updatePointsOfInterest() method on your controller load that do a http get request. If it fails it changes the url. When the url is changed your controller code is reloaded, hence $scope.updatePointsOfInterest() method is executing again and again.
I'm not sure why $http.error() method is triggered. It could be anything. Check out your XHR request in Network tab in dev tools (Chrome).
If your $http.error() method should fail (this means that there's an error and it's properly handled) than to fix the "infinite looping" issue you can do the following:
1) either doesn't call $scope.updatePointsOfInterest() on controller loading
2) or doesn't change the url in your $http.error() method
If you describe what you're trying to achieve with this updatePointsOfInterest() method I will be able to help you write it properly.
Regards,
Aleksey

Exporting Rally grid information to CSV

I am trying to export rally grid data to csv. Here's my code:
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
items:[{ xtype: 'container', itemId: 'print_button_box', padding: 5},{xtype: 'container', itemId: 'grid_box'}],
count: 0,
globalStore: null,
launch: function() {
this.globalStore = null;
this.count = 0;
this._addPrintButton();
list = [];
//Write app code here
this._get_stories_of_feature();
},
_addPrintButton: function() {
var me = this;
this.down('#print_button_box').add( {
xtype: 'rallybutton',
itemId: 'print_button',
text: 'CSV',
disabled: false,
handler: function() {
console.log('globalStore ',me.globalStore);
me._onClickExport();
}
});
},
_onClickExport: function () { //using this function to export to csv
if (document.getElementById('grid_box')) {
//Ext.getBody().mask('Exporting Tasks...');
console.log('inside export');
setTimeout(function () {
var template = '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-' +
'microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head>' +
'<!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>' +
'{worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet>' +
'</x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head><body><table>{table}' +
'</table></body></html>';
var base64 = function (s) {
return window.btoa(unescape(encodeURIComponent(s)));
};
var format = function (s, c) {
return s.replace(/{(\w+)}/g, function (m, p) {
return c[p];
});
};
var table = document.getElementById('grid_box');
console.log("Exporting table ",table);
var excel_data = '<tr>';
Ext.Array.each(table.innerHTML.match(/<span .*?x-column-header-text.*?>.*?<\/span>/gm), function (column_header_span) {
excel_data += (column_header_span.replace(/span/g, 'td'));
});
excel_data += '</tr>';
Ext.Array.each(table.innerHTML.match(/<tr class="x-grid-row.*?<\/tr>/gm), function (line) {
excel_data += line.replace(/[^\011\012\015\040-\177]/g, '>>');
});
console.log("Excel data ",excel_data);
var ctx = {worksheet: name || 'Worksheet', table: excel_data};
window.location.href = 'data:application/vnd.ms-excel;base64,' + base64(format(template, ctx));
Ext.getBody().unmask();
}, 500);
}else{
console.log("grid_box does not exist");
}
},
_createStore: function(){
var me = this;
//var f = [{property: 'UserStories', operator: '!=', value: null}];
Ext.create('Rally.data.WsapiDataStore',{
autoLoad: true,
model: "PortfolioItem/Feature",
limit: 5000,
fetch: ['FormattedID','Name','UserStories','c_DIteration','c_DPSI'],
listeners:{
load: function(store,data,success){
console.log("Store ",store);
var data_length = data.length;
console.log('Data length is '+data.length);
Ext.Array.each(data,function(item){
if(item.get('UserStories')==null){
store.remove(item);
}else{
var data = {
id: item.get("FormattedID"),
name: item.get("Name"),
UserStories: item.get("UserStories")._type,
DIteration: item.get("c_DIteration"),
DPSI: item.get("c_DPSI")
};
//list.push(data);
}
me._get_stories_of_feature(item.get("ObjectID"),item.get("Name"),data_length);
});
},
scope: this
}
});
},
_showGrid: function(store){
var me = this;
if(!this.grid){
this.grid = Ext.create('Rally.ui.grid.Grid',{
store: store,
columnCfgs:[
{text: 'Feature_ID', dataIndex: 'ide'},
{text: 'Feature Name', dataIndex: 'name'},
{text: 'Feature DIteration', dataInfex: 'fDIteration'},
{text: 'Story Name', dataIndex: 'UserStories'},
{text: 'Story ID', dataIndex: 'FormattedID'},
{text: 'Story DIteration', dataIndex: 'DIteration'},
{text: 'Story DPSI', dataIndex: 'DPSI'},
{text: 'Unscheduled', dataIndex: 'Unscheduled'}
]
});
me.globalStore = this.grid;
this.down('#grid_box').add(this.grid);
}
},
_get_stories_of_feature: function(){
var me = this;
Ext.create('Rally.data.WsapiDataStore',{
autoLoad: true,
model: "hierarchicalrequirement",
limit: 5000,
fetch: ['Name','ObjectID','FormattedID','Feature','c_DIteration','c_DPSI','DragAndDropRank'],
sorters:[{
property: 'DragAndDropRank', direction: 'ASC'
}],
filters:{
property: 'Feature', operator: '!=', value:null
},
listeners:{
load: function(store,data,success){
var data_length = data.length;
if(data_length>0){
console.log("Total stories ",data_length);
for(var i=0;i<data_length;i++){
var flag;
if(data[i].data.Feature.c_DIteration==null || data[i].data.Feature.c_DIteration.toString().indexOf("*")!=-1)
flag = "YES";
else flag = "NO";
var element = {
ide: data[i].data.Feature.FormattedID,
name: data[i].data.Feature.Name,
fDIteration: data[i].data.Feature.c_DIteration,
UserStories: data[i].data.Name,
FormattedID: data[i].data.FormattedID,
DIteration: data[i].data.c_DIteration,
DPSI: data[i].data.c_DPSI,
Unscheduled: flag
};
list.push(element);
console.log("me count is ",me.count);
console.log("Found ",data[i].data);
me.count++;
}
}
if(me.count==data_length){
console.log("Building store");
//once all the stories and feature data is computed
var myStore = Ext.create("Rally.data.custom.Store",{
data: list,
pageSize: 100
});
me._showGrid(myStore);
}
}
}
});
},
exportGrid: function(grid) {
if (Ext.isIE) {
this._ieToExcel(grid);
} else {
var data = this._getCSV(grid);
var a = document.createElement('a');
a.href = 'data:attachment/csv,' + data;
a.target ='_blank';
a.download = 'myFile.csv,' + encodeURIComponent(data); ;
a.innerHTML = "Click me to download the file.";
window.location = a;
}
},
_escapeForCSV: function(string) {
if (string.match(/,/)) {
if (!string.match(/"/)) {
string = '"' + string + '"';
} else {
string = string.replace(/,/g, ''); // comma's and quotes-- sorry, just loose the commas
}
}
return string;
},
_getFieldText: function(fieldData) {
var text;
if (fieldData == null || fieldData == undefined) {
text = '';
} else if (fieldData._refObjectName && !fieldData.getMonth) {
text = fieldData._refObjectName;
} else if (fieldData instanceof Date) {
text = Ext.Date.format(fieldData, this.dateFormat);
} else if (!fieldData.match) { // not a string or object we recognize...bank it out
text = '';
} else {
text = fieldData;
}
return text;
},
_getFieldTextAndEscape: function(fieldData) {
var string = this._getFieldText(fieldData);
return this._escapeForCSV(string);
},
_getCSV: function (grid) {
var cols = grid.columns;
var store = grid.store;
var data = '';
var that = this;
Ext.Array.each(cols, function(col, index) {
if (col.hidden != true) {
data += that._getFieldTextAndEscape(col.text) + ',';
}
});
data += "\n";
store.each(function(record) {
var entry = record.getData();
Ext.Array.each(cols, function(col, index) {
if (col.hidden != true) {
var fieldName = col.dataIndex;
var text = entry[fieldName];
data += that._getFieldTextAndEscape(text) + ',';
}
});
data += "\n";
});
return data;
},
_ieGetGridData : function(grid, sheet) {
var that = this;
var resourceItems = grid.store.data.items;
var cols = grid.columns;
Ext.Array.each(cols, function(col, colIndex) {
if (col.hidden != true) {
console.log('header: ', col.text);
sheet.cells(1,colIndex + 1).value = col.text;
}
});
var rowIndex = 2;
grid.store.each(function(record) {
var entry = record.getData();
Ext.Array.each(cols, function(col, colIndex) {
if (col.hidden != true) {
var fieldName = col.dataIndex;
var text = entry[fieldName];
var value = that._getFieldText(text);
sheet.cells(rowIndex, colIndex+1).value = value;
}
});
rowIndex++;
});
},
_ieToExcel: function (grid) {
if (window.ActiveXObject){
var xlApp, xlBook;
try {
xlApp = new ActiveXObject("Excel.Application");
xlBook = xlApp.Workbooks.Add();
} catch (e) {
Ext.Msg.alert('Error', 'For the export to work in IE, you have to enable a security setting called "Initialize and script ActiveX control not marked as safe" from Internet Options -> Security -> Custom level..."');
return;
}
xlBook.worksheets("Sheet1").activate;
var XlSheet = xlBook.activeSheet;
xlApp.visible = true;
this._ieGetGridData(grid, XlSheet);
XlSheet.columns.autofit;
}
}
});
I am using the "_onClickExport" function that I read about here to export my grid data to CSV but when I execute that function, it can't find ElementId "grid_box", although I have defined it.
I was able to export with your code after making these changes:
line 31, change to
if (this.down('#grid_box')){
line 52, change to
var table = that.getComponent('grid_box');
where that is defined on the top of _onClickExport
var that = this;
line 57, and a similar line below it, replace table.innerHTML.match with table.getEl().dom.outerHTML.match
Ext.Array.each(table.getEl().dom.outerHTML.match

Loading grid only after "all" the data stores have loaded

I have this code that loads a data store for all stories that contain each of the names from an array.
var prefix_set = [list of names]
for(var i=0;i<prefix_set.length;i++)
_get_stories_of_feature(prefix_set[i]) //this function creates a data store
I am storing all the data into a global array and creating a data store which is passed to a grid in the end. The problem is that my grid loads up with incomplete data (the grid loads up even before all the data has been loaded into the global array).
Here's my code:
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
items:[{ xtype: 'container', itemId: 'print_button_box', padding: 5},{xtype: 'container', itemId: 'grid_box'}],
count: 0,
globalStore: null,
totalDataLength: 0,
gridCounter: 0,
launch: function() {
var me = this;
this.globalStore = null;
this.gridCounter = 0;
this.totalDataLength=0;
this.count = 0;
this._addPrintButton();
list = [];
//Write app code here
var prefix_set = ["Epic:","Arch:","Refa:","Innov:","Spike:","Producer:","Dependency:","Consumer:"];
var i;
for(i=0;i<prefix_set.length;i++){
this._get_stories_of_feature(prefix_set[i],prefix_set.length,i);
}
},
_addPrintButton: function() {
var me = this;
this.down('#print_button_box').add( {
xtype: 'rallybutton',
itemId: 'print_button',
text: 'CSV',
disabled: false,
handler: function() {
console.log('globalStore ',me.globalStore);
me.exportGrid(me.globalStore);
}
});
},
_onClickExport: function () {
if (document.getElementById('grid_box')) {
//Ext.getBody().mask('Exporting Tasks...');
console.log('inside export');
setTimeout(function () {
var template = '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-' +
'microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head>' +
'<!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>' +
'{worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet>' +
'</x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head><body><table>{table}' +
'</table></body></html>';
var base64 = function (s) {
return window.btoa(unescape(encodeURIComponent(s)));
};
var format = function (s, c) {
return s.replace(/{(\w+)}/g, function (m, p) {
return c[p];
});
};
var table = document.getElementById('grid_box');
console.log("Exporting table ",table);
var excel_data = '<tr>';
Ext.Array.each(table.innerHTML.match(/<span .*?x-column-header-text.*?>.*?<\/span>/gm), function (column_header_span) {
excel_data += (column_header_span.replace(/span/g, 'td'));
});
excel_data += '</tr>';
Ext.Array.each(table.innerHTML.match(/<tr class="x-grid-row.*?<\/tr>/gm), function (line) {
excel_data += line.replace(/[^\011\012\015\040-\177]/g, '>>');
});
console.log("Excel data ",excel_data);
var ctx = {worksheet: name || 'Worksheet', table: excel_data};
window.location.href = 'data:application/vnd.ms-excel;base64,' + base64(format(template, ctx));
Ext.getBody().unmask();
}, 500);
}else{
console.log("grid_box does not exist");
}
},
tableToExcel: function(){
var me = this;
console.log("Global store ",me.globalStore);
var uri = 'data:application/vnd.ms-excel;base64,'
, template = '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>{worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head><body><table>{table}</table></body></html>'
, base64 = function(s) { return window.btoa(unescape(encodeURIComponent(s))); }
, format = function(s, c) { return s.replace(/{(\w+)}/g, function(m, p) { return c[p]; }); };
return function(table, name) {
if (!table.nodeType) table = document.getElementById(table);
var ctx = {worksheet: name || 'Worksheet', table: table.innerHTML};
window.location.href = uri + base64(format(template, ctx));
};
},
_createStore: function(){
var me = this;
//var f = [{property: 'UserStories', operator: '!=', value: null}];
Ext.create('Rally.data.WsapiDataStore',{
autoLoad: true,
model: "PortfolioItem/Feature",
limit: 10000,
fetch: ['FormattedID','Name','UserStories','c_DIteration','c_DPSI'],
listeners:{
load: function(store,data,success){
console.log("Store ",store);
var data_length = data.length;
console.log('Data length is '+data.length);
Ext.Array.each(data,function(item){
if(item.get('UserStories')==null){
store.remove(item);
}else{
var data = {
id: item.get("FormattedID"),
name: item.get("Name"),
UserStories: item.get("UserStories")._type,
DIteration: item.get("c_DIteration"),
DPSI: item.get("c_DPSI")
};
//list.push(data);
}
me._get_stories_of_feature(item.get("ObjectID"),item.get("Name"),data_length);
});
},
scope: this
}
});
},
_showGrid: function(store){
var me = this;
if(!this.grid){
this.grid = Ext.create('Rally.ui.grid.Grid',{
store: store,
columnCfgs:[
{text: 'Feature', dataIndex: 'name'},
{text: 'FormattedID', dataIndex: 'FormattedID'},
{text: 'Name', dataIndex: 'UserStories'},
{text: 'Release', dataIndex: 'Release'},
{text: 'Iteration', dataIndex: 'Iteration'},
{text: 'DIteration', dataIndex: 'DIteration'},
{text: 'DPSI', dataIndex: 'DPSI'},
{text: 'Schedule State', dataIndex: 'ScheduleState'},
{text: 'Task Remaining Total', dataIndex: 'TaskRemainingTotal'},
{text: 'Owner', dataIndex: 'Owner'},,
{text: 'Project', dataIndex: 'Project'},
{text: 'Unscheduled', dataIndex: 'Unscheduled'}
]
});
me.globalStore = this.grid;
this.down('#grid_box').add(this.grid);
}
},
_get_stories_of_feature: function(name,length,k){
var me = this;
Ext.create('Rally.data.WsapiDataStore',{
autoLoad: true,
model: "HierarchicalRequirement",
limit: 10000,
fetch: ['Name','ObjectID','FormattedID','Parent','Feature','TaskRemainingTotal','c_DIteration','c_DPSI','DragAndDropRank','Release','Iteration','Project','Owner','ScheduleState'],
sorters:[{
property: 'DragAndDropRank', direction: 'ASC'
,},{property: 'Project', direction: 'ASC'}],
filters:[{
property: 'Name', operator: 'contains', value: name
},{
property: 'ScheduleState', operator: '!=', value: 'Accepted'
}],
pageSize: 5000,
listeners:{
load: function(store,data,success){
var data_length = data.length;
if(data_length>0){
console.log("Total stories ",data_length);
for(var i=0;i<data_length;i++){
console.log("Data length for ",k," is ",data_length);
console.log("Data i ",data[i]);
me.totalDataLength++;
var flag;
var iteration=""; var release="";
var fid = "",fname="";
var owner="";
if(data[i].data.Feature!=null && (data[i].data.Feature.c_DIteration==null || data[i].data.Feature.c_DIteration.toString().indexOf("*")!=-1))
flag = "YES";
else flag = "NO";
if(data[i].data.Iteration!=null){
iteration = data[i].data.Iteration._refObjectName;
}
if(data[i].data.Release!=null){
release = data[i].data.Release._refObjectName;
}
if(data[i].data.Owner!=null){
owner = data[i].data.Owner._refObjectName;
}
if(data[i].data.Feature!=null){
fid = data[i].data.Feature.FormattedID;
fname = data[i].data.Feature.Name;
}
var element = {
name: fid+":"+fname,
UserStories: data[i].data.Name,
Project: data[i].data.Project._refObjectName,
Owner: owner,
FormattedID: data[i].data.FormattedID,
Iteration: iteration,
TaskRemainingTotal: parseInt(data[i].data.TaskRemainingTotal),
Release: release,
ScheduleState: data[i].data.ScheduleState,
DIteration: data[i].data.c_DIteration,
DPSI: data[i].data.c_DPSI,
Unscheduled: flag
};
list.push(element);
console.log("me count is ",me.count);
console.log("Found ",data[i].data);
me.count++;
}
}
console.log("Total Data Length is ",me.totalDataLength);
console.log("k is ",k," and length is ",length);
if(k==length-1){
console.log("Building store");
//once all the stories and feature data is computed
var myStore = Ext.create("Rally.data.custom.Store",{
data: list,
sortable: true,
pageSize: me.totalDataLength,
});
me._showGrid(myStore);
}
}
}
});
me.gridCounter++;
},
exportGrid: function(grid) {
if (Ext.isIE) {
this._ieToExcel(grid);
} else {
var data = this._getCSV(grid);
window.location = 'data:text/csv;charset=utf8,' + encodeURIComponent(data);
}
},
_escapeForCSV: function(string) {
if (string.match(/,/)) {
if (!string.match(/"/)) {
string = '"' + string + '"';
} else {
string = string.replace(/,/g, ''); // comma's and quotes-- sorry, just loose the commas
}
}
return string;
},
_getFieldText: function(fieldData) {
var text;
if (fieldData == null || fieldData == undefined) {
text = '';
} else if (fieldData._refObjectName && !fieldData.getMonth) {
text = fieldData._refObjectName;
} else if (fieldData instanceof Date) {
text = Ext.Date.format(fieldData, this.dateFormat);
} else if (!fieldData.match) { // not a string or object we recognize...bank it out
text = '';
} else {
text = fieldData;
}
return text;
},
_getFieldTextAndEscape: function(fieldData) {
var string = this._getFieldText(fieldData);
return this._escapeForCSV(string);
},
_getCSV: function (grid) {
var cols = grid.columns;
var store = grid.store;
var data = '';
console.log("Grid.Store is ",grid.store);
var that = this;
Ext.Array.each(cols, function(col, index) {
if (col.hidden != true) {
data += that._getFieldTextAndEscape(col.text) + ',';
}
});
data += "\n";
store.each(function(record) {
var entry = record.getData();
Ext.Array.each(cols, function(col, index) {
if (col.hidden != true) {
var fieldName = col.dataIndex;
var text = entry[fieldName];
data += that._getFieldTextAndEscape(text) + ',';
}
});
data += "\n";
});
return data;
},
_ieGetGridData : function(grid, sheet) {
var that = this;
var resourceItems = grid.store.data.items;
var cols = grid.columns;
Ext.Array.each(cols, function(col, colIndex) {
if (col.hidden != true) {
console.log('header: ', col.text);
sheet.cells(1,colIndex + 1).value = col.text;
}
});
var rowIndex = 2;
grid.store.each(function(record) {
var entry = record.getData();
Ext.Array.each(cols, function(col, colIndex) {
if (col.hidden != true) {
var fieldName = col.dataIndex;
var text = entry[fieldName];
var value = that._getFieldText(text);
sheet.cells(rowIndex, colIndex+1).value = value;
}
});
rowIndex++;
});
},
_ieToExcel: function (grid) {
if (window.ActiveXObject){
var xlApp, xlBook;
try {
xlApp = new ActiveXObject("Excel.Application");
xlBook = xlApp.Workbooks.Add();
} catch (e) {
Ext.Msg.alert('Error', 'For the export to work in IE, you have to enable a security setting called "Initialize and script ActiveX control not marked as safe" from Internet Options -> Security -> Custom level..."');
return;
}
xlBook.worksheets("Sheet1").activate;
var XlSheet = xlBook.activeSheet;
xlApp.visible = true;
this._ieGetGridData(grid, XlSheet);
XlSheet.columns.autofit;
}
}
});
Please take a look at the code in this post, where Mark uses promises, and builds three separate Rally.data.wsapi.Stores and only after all the data is there _makeGrid(resultArray) is called.

Resources