How to access multiple services in angularjs controller - angularjs

In an aspnetboilerplate .cshtml page, we have
div ng-controller="app.views.automate as vm"
, this angular controller access the automateService from asp.net mvc controller.
inside that div , we are loading partial view from another .cshtml file, here it should have a different service(excelService) from the Asp.net mvc controller. How to achieve it.
Angular.js
(function () {
var controllerId = 'app.views.automate';
angular.module('app').controller(controllerId, [
'$scope', 'abp.services.app.automate', function ($scope, automateService) {
var vm = this;
//Automate logic...
//Model
vm.currentStep = 1;
vm.steps = [
{
step: 1,
name: "First step",
template: "/App/Main/views/automate/step1.cshtml",
controller: 'app.views.excel as vm'
},
{
step: 2,
name: "Second step",
template: "/App/Main/views/automate/step2.cshtml"
},
{
step: 3,
name: "Third step",
template: "/App/Main/views/automate/step3.cshtml",
controller: 'app.views.capacity as vm'
}
];
//Functions
vm.gotoStep = function (newStep) {
vm.currentStep = newStep;
//$scope.ParseExcel();
}
vm.getStepTemplate = function () {
for (var i = 0; i < vm.steps.length; i++) {
if (vm.currentStep == vm.steps[i].step) {
return vm.steps[i].template;
}
}
}
vm.save = function () {
//todo: save data...
//vm.location['abq'].center
//alert(vm.location.abq.center);
// vm.enterCapacity(vm.location);
}
vm.enterCapacity = function () {
// alert(vm.capacity.locations[0].center);
automateService.calculateCapacity(vm.capacity).then(function () {
abp.notify.info(app.localize('SavedSuccessfully'));
});
}
$scope.SelectedFileForUpload = null; //initially make it null
$scope.BindData = null;
$scope.showLoader = false;
$scope.IsVisible = false;
$scope.UploadFiles = function (files) {
$scope.$apply(function () {
$scope.Message = '';
$scope.SelectedFileForUpload = files[0];
});
}
vm.capacity = {
locations: [
{ city: "abq", center: 0, housing: 0 },
{ city: "han", center: 0, housing: 0 },
{ city: "udh", center: 0, housing: 0 },
{ city: "dhn", center: 0, housing: 0 },
{ city: "spp", center: 0, housing: 0 },
{ city: "rt", center: 0, housing: 0 },
{ city: "has", center: 0, housing: 0 },
{ city: "jed", center: 0, housing: 0 },
{ city: "ry", center: 0, housing: 0 },
{ city: "yan", center: 0, housing: 0 },
{ city: "tan", center: 0, housing: 0 },
{ city: "itq", center: 0, housing: 0 },
{ city: "abq", center: 0, housing: 0 }
]
};
}
]);
})();
Angular.html
<div class="row clearfix" ng-controller="app.views.automate as vm">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="card">
<div class="header">
<h2>
Automate
</h2>
</div>
<div class="body">
<div id="wizard-step-container">
<ul class="nav nav-pills nav-justified">
<li ng-repeat="step in vm.steps" ng-class="{'active':step.step == vm.currentStep}"><a ng-click="vm.gotoStep(step.step)" href="">{{step.step}}. {{step.name}}</a></li>
</ul>
</div>
<div id="wizard-content-container">
<ng-include src="vm.getStepTemplate()"></ng-include>
</div>
<div id="wizard-navigation-container">
<div class="pull-right">
<span class="btn-group">
<button ng-disabled="vm.currentStep <= 1" class="btn btn-default" name="previous" type="button" ng-click="vm.gotoStep(vm.currentStep - 1)"><i class="fa fa-arrow-left"></i> Previous step</button>
<button ng-disabled="vm.currentStep >= vm.steps.length" class="btn btn-primary" name="next" type="button" ng-click="vm.gotoStep(vm.currentStep + 1)">Next step <i class="fa fa-arrow-right"></i></button>
</span>
<button ng-disabled="vm.currentStep != vm.steps.length" class="btn btn-success" name="next" type="button" ng-click="vm.save()"> <i class="fa fa-floppy-o"></i> Save</button>
</div>
</div>
</div>
</div>
</div>
</div>
Step1.html
<div class="step1">
<div class="loading-icon" id="loading" ng-show="showLoader">
</div>
<div class="form-inline">
<input type="file" class="form-control" name="file" accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel" onchange="angular.element(this).scope().UploadFiles(this.files)" />
<input type="button" value="Preview" class="btn btn-primary" ng-disabled="!SelectedFileForUpload" ng-click="ParseExcel()" />
<input type="button" value="Insert" style="margin-left: 15px;" class="btn btn-success" ng-show="IsVisible" ng-click="InsertData()" />
</div>
<br />
</div>
Excel.js
(function () {
angular.module('app').controller('app.views.excel', [
'$scope', 'abp.services.app.excel',
function ($scope, ExcelService) {
var vm = this;
$scope.ParseExcel = function () {
var formData = new FormData();
var file = $scope.SelectedFileForUpload;
formData.append('file', file);
$scope.showLoader = true; //show spinner
var response = Excelservice.SendExcelData(formData); //calling service
response.then(function (d) {
var res = d.data;
$scope.BindData = res;
$scope.IsVisible = true; //showing the table after databinding
$scope.showLoader = false; //after success hide spinner
}, function (err) {
console.log(err.data);
console.log("error in parse excel");
});
}
$scope.InsertData = function () {
var response = Excelservice.InsertToDB();
response.then(function (d) {
var res = d.data;
if (res.toString().length > 0) {
$scope.Message = res + " Records Inserted";
$scope.IsVisible = false; //used to disable the insert button and table after submitting data
angular.forEach(
angular.element("input[type='file']"),
function (inputElem) {
angular.element(inputElem).val(null); //used to clear the file upload after submitting data
});
$scope.SelectedFileForUpload = null; //used to disable the preview button after inserting data
}
}, function (err) {
console.log(err.data);
console.log("error in insertdata");
});
}
}
]);
angular.module('app').service("Excelservice", function ($http) {
this.SendExcelData = function (data) {
var request = $http({
method: "post",
withCredentials: true,
url: '/Home/ReadExcel',
data: data,
headers: {
'Content-Type': undefined
},
transformRequest: angular.identity
});
return request;
}
this.InsertToDB = function () {
var request = $http({
method: "get",
url: '/Home/InsertExcelData',
data: {},
datatype: 'json'
});
return request;
}
});
//used to check the extension of file while uploading
function checkfile(sender) {
var validExts = new Array(".xlsx", ".xls");
var fileExt = sender.value;
fileExt = fileExt.substring(fileExt.lastIndexOf('.'));
if (validExts.indexOf(fileExt) < 0) {
alert("Invalid file selected, valid files are of " +
validExts.toString() + " types.");
return false;
}
else return true;
}
})();
Error Details

(function () {
var controllerId = 'app.views.automate';
angular.module('app').controller(controllerId, [
'$scope','Excelservice', 'abp.services.app.automate', function ($scope, Excelservice, automateService) {
var vm = this;
// ...
}]);
})();
https://github.com/aspnetboilerplate/aspnetboilerplate/issues/3789#issuecomment-416031257

Related

Controlling ng-repeat iterations

HTML :
<div ng-repeat="data in $ctrl.list">
<div ng-init="$ctrl.applyAction(data)">
<h4>{{data.name}}</h4>
<ul ng-if="data.steps">
<li ng-repeat="step in data.steps">{{step.name}}</li>
</ul>
</div>
</div>
Controller :
$onInit() {
this.list = [{
name: "First Obj"
}, {
name: "Second Obj"
}, {
name: "Third Obj"
}, {
name: "Fourth Obj"
}];
}
applyAction(data) {
this.someHttpService.getResponse(data).then(function(success) {
data.reqForSecondServiceCall = success.data;
this.secondServiceCall(data);
}, function(error) {
// console.log(error);
});
}
secondServiceCall(data) {
this.someHttpService.getSecondServiceResponse(data).then(function(success) {
data.status = success.data;
}, function(error) {
// console.log(error);
});
}
Currently ng-repeat will be iterating through the list object irrespective of the service calls made on each object (asynchronous).
And the desired functionality is to render the current object only when the applyActions method is completed on previous object.
One solution is to queue the calls in an event queue and then invoke the events one by one when previous call is completed
angular.module('myApp',[]).controller('myCtrl', function($scope, $http, $timeout){
$scope.title = 'welcome';
$scope.finishedEvent = '';
$scope.eventQueue = [];
$scope.list = [{
name: "First Obj"
}, {
name: "Second Obj"
}, {
name: "Third Obj"
}, {
name: "Fourth Obj"
}];
$scope.applyAction = function(data, index) {
//declare the event
var event = function(){
var testApi = "https://jsonplaceholder.typicode.com/posts";
$http.get(testApi).then(function(response) {
data.steps = response.data.slice(0,2);
$scope.finishedEvent = data.name;
}, function(error) {
console.log(error);
});
};
if(index == 0){
event();
}else{
$scope.eventQueue.push(event);
}
};
$scope.$watch('finishedEvent', function(){
if($scope.eventQueue.length > 0){
$timeout(function(){
console.log($scope.finishedEvent + '- loaded')
var event = $scope.eventQueue[0];
$scope.eventQueue.splice(0, 1); //remove current event from queue
event();
}, 1000);
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp" ng-controller="myCtrl">
<h1>{{title}}</h1>
<div ng-repeat="data in list">
<div ng-init="applyAction(data, $index)">
<h4>{{data.name}}</h4>
<ul ng-if="data.steps">
<li ng-repeat="step in data.steps">{{step.title}}</li>
</ul>
</div>
</div>
</body>
NOTE 1: I used a dummie api just to have live data
NOTE 2: Remove the $timeout, only added it to make the example clear
Here's a plunker with the example

How to Create playlist in angularjs audio app?

I had created a angular audio app. i want to display play list in it . Using directive to trigger play pause and other operations. Please help me to create play list here.
my main html is
<!DOCTYPE html>
<html ng-app="app">
<head>
<title> AUDIO</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css" type="text/css">
<link rel="stylesheet" href="css/ng-cool-audio.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.18/angular.min.js"></script>
<script src="js/ng-cool-audio.js"></script>
<script src="js/app.js"></script>
<style>
body {
background-color: #f5f5f5;
font-family: Helvetica Neue, Helvetica, arial, sans-serif;
}
.main {
width: 800px;
margin: 100px auto;
}
</style>
</head>
<body ng-controller="MainCtrl">
<div class="main">
<ng-cool-audio source="source2"></ng-cool-audio>
</div>
</body>
</html>
my html template is
<div class="ng-cool-audio-container">
<audio>
Your browser seems to be upgraded! :)
</audio>
<div class="ng-cool-audio-preview">
<div class="ncv-audio-left">
<div class="ncv-img-container">
<img ng-src="{{audio.cover}}" alt="Photo">
</div>
</div>
<div class="ncv-audio-right">
<div class="ncv-audio-right-top">
<div class="ncv-header">
<div class="ncv-header-title">{{audio.author}}</div>
<div class="ncv-header-subtitle">{{audio.name}}</div>
<div class="ncv-audio-sound">
<i class="fa fa-volume-up" ng-show="!isMuted" ng-click="volumeOff()"></i>
<i class="fa fa-volume-off" ng-show="isMuted" ng-click="volumeOn()"></i>
<input type="range" max="10" min="0" value="5">
</div>
</div>
</div>
<div class="ncv-audio-right-bottom">
<div class="ncv-audio-controls">
<i class="fa fa-backward" ng-click="playBackward()"></i>
<i class="fa fa-play" ng-click="play()"></i>
<i class="fa fa-pause" ng-click="pause()"></i>
<i class="fa fa-forward" ng-click="playForward()"></i>
</div>
<div class="ncv-progress">
<progress class="ncv-progress-bar" max="100" value="{{progressValue}}"></progress>
</div>
<div class="ncv-time">
<span class="ncv-current-time">{{currentTime}}</span>
</div>
</div>
</div>
</div>
<div class="col-lg-12">
<h4> PLAY LIST </h4>
<div class="col-lg-3" ng-repeat="song in source2">
{{song.audio.name}}
</div>
</div>
</div>
app.js is
(function() {
'use strict';
angular
.module('app', [
'ngCoolAudio'
])
.controller('MainCtrl', function($scope) {
$scope.source2 = {
audio: [{
author: 'Dr. SPB',
cover: 'images/about.jpg',
src: 'audio/AADI ANAADI-SPB.mp3',
name: 'AADI ANAADI'
},
{
author: 'Dr.RAJ',
cover: 'images/about.jpg',
src: 'audio/BHAVA SAAGARADA AALADI-DR.RAJ.mp3',
name: 'BHAVA SAAGARADA AALADI'
},
{
author: 'Dr.RAJ',
cover: 'images/about.jpg',
src: 'audio/CHARAKULAAMBUDHI CHANDIRA-DR.RAJ (2).mp3',
name: 'CHARAKULAAMBUDHI CHANDIRA'
},
{
author: 'S.JANAKI',
cover: 'images/about.jpg',
src: 'audio/DHYANA MAADUTHIRU-S.JANAKI .mp3',
name: 'DHYANA MAADUTHIRU'
},
{
author: 'OM',
cover: 'images/about.jpg',
src: 'audio/Om Namahshivaya.mp3',
name: 'OM NAMAHSHIVAYA'
}
],
config: {
autoplay: false,
loop: true
}
}
})
})();
audio directive is
(function() {
'use strict';
angular
.module('ngCoolAudio', [])
.directive('ngCoolAudio', [
'$timeout',
'$sce',
function($timeout, $sce) {
return {
restrict: 'AE',
scope: {
source: '=source'
},
replace: true,
templateUrl: 'html/audio.html',
controller: ['$scope', '$element', function($scope, $element) {
//check source file
if (!$scope.source || !$scope.source.audio) {
throw new Error('Source seems not to config right!');
return;
}
var container = $element[0].querySelector('.ng-cool-audio-container');
var audio = $element[0].querySelector('audio');
var volume_range = $element[0].querySelector('.ncv-audio-sound input[type="range"]');
var $audio = angular.element(audio);
$scope.audio = {};
//private method
var calCulateTime = function() {
var secs = parseInt(audio.currentTime % 60);
var mins = parseInt((audio.currentTime / 60) % 60);
// Ensure it's two digits. For example, 03 rather than 3.
secs = ("0" + secs).slice(-2);
mins = ("0" + mins).slice(-2);
return mins + ':' + secs;
}
var calCulateProgress = function() {
var percent = (100 / audio.duration) * audio.currentTime;
return percent;
}
var generateAudio = function(audio) {
if (!audio.src) {
throw new Error('Not found src in your audio config');
return;
}
$audio.attr('src', audio.src);
$scope.audio.cover = audio.cover || 'http://7xj610.com1.z0.glb.clouddn.com/29ce98b4349b72c2778d2f82823159b06f98f8bc.jpeg';
$scope.audio.author = audio.author || 'Unknow';
$scope.audio.name = audio.name || 'Unknow';
}
$scope.currentTrack = 0;
$scope.jumpInterval = 10;
$scope.init = function() {
$scope.currentTime = '00:00';
$scope.progressValue = 0;
$scope.isPlaying = false;
$scope.isMuted = false;
$scope.setInterface($scope.currentTrack);
$scope.addEvents();
};
$scope.setInterface = function(index) {
var isArray = angular.isArray($scope.source.audio);
if (isArray) {
$scope.audioCollection = $scope.source.audio;
generateAudio($scope.audioCollection[index]);
} else {
generateAudio($scope.source.audio);
}
if ($scope.source.config) {
if ($scope.source.config.autoplay) {
$audio.attr('autoplay', 'autoplay');
$scope.play();
}
if ($scope.source.config.loop) {
$audio.attr('loop', 'loop');
}
}
};
//toggle play pause
$scope.play = function() {
audio.play();
$scope.isPlaying = true;
};
$scope.pause = function() {
audio.pause();
$scope.isPlaying = false;
};
//toggle mute
$scope.volumeOn = function() {
audio.muted = false;
$scope.isMuted = false;
};
$scope.volumeOff = function() {
audio.muted = true;
$scope.isMuted = true;
};
//backward forward
$scope.playBackward = function() {
//here jump to pre song
if ($scope.audioCollection && $scope.audioCollection.length > 0) {
$scope.currentTrack -= 1;
if ($scope.currentTrack < 0) {
$scope.currentTrack = $scope.audioCollection.length - 1;
}
$scope.init();
$scope.play();
} else {
var toTime = audio.currentTime - $scope.jumpInterval;
if (toTime < 0) {
audio.currentTime = 0;
} else {
audio.currentTime = toTime;
}
$scope.currentTime = calCulateTime();
$scope.progressValue = calCulateProgress();
}
};
$scope.playForward = function() {
//here jump to next song
if ($scope.audioCollection && $scope.audioCollection.length > 0) {
$scope.currentTrack += 1;
if ($scope.currentTrack > $scope.audioCollection.length - 1) {
$scope.currentTrack = 0;
}
$scope.init();
$scope.play();
} else {
var toTime = audio.currentTime + $scope.jumpInterval;
if (toTime > audio.duration) {
audio.currentTime = audio.duration;
} else {
audio.currentTime = toTime;
}
$scope.currentTime = calCulateTime();
$scope.progressValue = calCulateProgress();
}
};
//skip progress
$scope.skipProgress = function(e) {
//update time and progress
var target = e.target;
var offsetX = 0;
if (!e.pageX || !target.offsetLeft) {
offsetX = e.offsetX ? e.offsetX : e.layerX;
} else {
offsetX = e.pageX - target.offsetLeft;
}
var pos = offsetX / target.offsetWidth;
audio.currentTime = pos * audio.duration;
$scope.currentTime = calCulateTime();
$scope.progressValue = calCulateProgress();
}
$scope.addEvents = function() {
//time update
// progress update
audio.addEventListener('timeupdate', function() {
$scope.currentTime = calCulateTime();
$scope.progressValue = calCulateProgress();
$scope.$apply();
}, false);
audio.addEventListener('ended', function() {
//auto play next
if ($scope.audioCollection && $scope.audioCollection.length > 0) {
$scope.playForward();
}
});
//angular seems dont support input[range] stuff so let's do it event
volume_range.addEventListener('change', function() {
audio.volume = parseFloat(this.value / 10);
}, false);
}
}],
link: function(scope, ele, attrs) {
scope.init();
}
}
}
])
})();

Displaying data in ng-grid by clicking on an <a> element in the sidebar

I have a view in an angularjs application with a sidebar, where I can choose my insurers. By clicking on an insurer, I want my ng-grid show me some insurer's data. Now I can select the insurer, and see the <div class="well well-sm"> changes.
Here is my angular controller:
app.controller('ReportsInsurerPaymentsCtrl', ['$scope', '$http', '$filter', 'toaster', '$state', '$modal', function ($scope, $http, $filter, toaster, $state, $modal) {
$scope.insurer_payments = [];
$scope.insurer_payments = [];
$scope.insurer_payment = {};
$scope.gridOptions = {
data: "insurer_payment",
rowTemplate: '<div ng-style="{\'cursor\': row.cursor, \'z-index\': col.zIndex() }" ng-repeat="col in renderedColumns" ng-class="col.colIndex()" class="ngCell {{col.cellClass}} " ng-cell></div>',
columnDefs: [
{
field: "date",
displayName: "Date",
cellTemplate: '<div class="ngCellText" ng-class="col.colIndex()"><span ng-cell-text>{{row.getProperty(col.field)}}</span></div>',
width: 100
},
{
field: "amount",
displayName: "Amount",
cellTemplate: '<div class="ngCellText" ng-class="col.colIndex()"><span ng-cell-text>{{row.getProperty(col.field)}}</span></div>'
},
{
field: 'comment',
displayName: 'Comment',
cellTemplate: '<div class="ngCellText" ng-class="col.colIndex()"><span ng-cell-text>{{row.getProperty(col.field)}}</span></div>',
}
],
$scope.refresh = function () {
var p = {
name: $scope.filterOptions.filterText,
pageNumber: (allPages >= $scope.pagingOptions.currentPage) ? $scope.pagingOptions.currentPage : $scope.pagingOptions.currentPage = 1,
pageSize: $scope.pagingOptions.pageSize,
sortInfo: sb.join("")
};
$http({
url: "reports/insurer_payments.json",
method: "GET",
params: p
}).success(function (data, insurer_payment) {
$scope.totalServerItems = data.insurerPaymentsCount;
$scope.insurer_payments_count = data.total_insurer_payments_count;
$scope.insurer_payments = data.insurer_payments;
$scope.insurer_payment = data.insurer_payment;
if (insurer_payment) {
$scope.insurer_payment = $filter('orderBy')($scope.insurer_payments, 'name')[0];
} else {
$scope.insurer_payment = $filter('filter')($scope.insurer_payments, {name: insurer_payment.name})[0];
}
if ($scope.insurer_payments) $scope.insurer_payment.selected = true;
$scope.showContent = true;
if ($scope.gridOptions.ngGrid) {
$scope.gridOptions.ngGrid.buildColumns();
}
}).error(function () {
});
}, 100);
};
$scope.selectInsurerPayment = function(item){
angular.forEach($scope.insurer_payments, function(item) {
item.selected = false;
});
$scope.insurer_payment = item;
$scope.insurer_payment.selected = true;
};
$scope.refresh();
}]);
A part of a view:
<a ng-repeat="insurer_payment in insurer_payments | orderBy:'name'"
class="list-group-item m-l"
ng-class="{'select m-l-none': insurer_payment.selected }"
ng-click="selectInsurerPayment(insurer_payment)">
<span class="block text-ellipsis m-l-n text-md" ng-class="{'m-l-none': insurer_payment.selected }">
{{ insurer_payment.name }}
</span>
</a>
<div class="well well-sm">
<div class="row">
<div class="col-sm-4">
<strong>Commission: {{insurer_payment.commission}}</strong>
</div>
<div class="col-sm-4">
<strong>Insurer Ppayment: {{insurer_payment.insurer_payment}}</strong>
</div>
<div class="col-sm-4">
<strong>Inequality: {{insurer_payment.commission - insurer_payment.insurer_payment}}</strong>
</div>
</div>
</div>
<div class="table-responsive">
<div ng-grid="gridOptions" class="gridStyle">
</div>
</div>
And a part of a rails controller:
def index
insurer_payments = current_company.insurers.map do |insurer|
{
commission: insurer.contracts.pluck(:commission).sum.to_f,
name: insurer.name,
insurer_payment: insurer.insurer_payments.pluck(:amount).sum.to_f,
id: insurer.id
}
end
insurer_payment = current_company.insurers.map do |insurer|
{
amount: insurer.insurer_payments.pluck(:amount).map { |x| x.to_f },
comment: insurer.insurer_payments.pluck(:comment),
date: insurer.insurer_payments.pluck(:date),
id: insurer.id
}
end
total_insurer_payments_count = current_company.insurers.map do |insurer|
insurer.insurer_payments.count
end
insurer_payments_count = current_company.insurer_payments.count
respond_to do |format|
format.json { render json: { insurer_payments: insurer_payments, insurer_payment: insurer_payment,
total_insurer_payments_count: total_insurer_payments_count,
insurerPaymentsCount: insurer_payments_count } }
end
end
So, how it can be done, by selecting an insurer to see the corresponding data?
I am not sure on this, since I don't have all of your code, but you may want to try some of the changes I have below.
Because ng-repeat has it's own scope, iterating through insurer_payment in insurer_payments is conflicting with insurer_payment on scope. When you loop through the insurer_payments, you are modifying the value of insurer_payment each time.
I would change
$scope.gridOptions = {
data: "insurer_payment",
to
$scope.gridOptions = {
data: [];
Since the data is an array, not a string.
Within the success callback of $http, you need to set gridOptions.data (unless this is elsewhere, I don't see where the grid is)
.success(function (data, insurer_payment) {
$scope.gridOptions.data = data;
...
}
And the selectInsurerPayment function:
$scope.selectInsurerPayment = function(item){
angular.forEach($scope.insurer_payments, function(item) {
item.selected = false;
});
$scope.selectedInsurerPayment = item;
$scope.insurer_payment.selected = true;
};
And finally, in the well in your HTML, change references to insurer_payment to selectedInsurerPayment, ex:
<strong>Commission: {{selectedInsurerPayment.commission}}</strong>
Old Solution
In ng-repeat:
ng-click="selectInsurerPayment(insurer_payment)"
to
ng-click="selection.insurer_payment = insurer_payment"
In the well,
<div class="well well-sm">
to
<div class="well well-sm" ng-if="selection.insurer_payment">
and change the references to insurer_payment to selection.insurer_payment.{variable}, example:
<strong>Commission: {{selection.insurer_payment.commission}}</strong>

Nested ng-repeat gives error after 10 levels deep: 10 $digest() iterations reached

Getting this error: Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
I have created a reddit style nested comment system using AngularJS. But after 10 levels deep i get a very ugly error on my console that looks like this:
This happens after exactly 10 levels deep:
The nested comments are directives that look like this:
commentCont.tpl.html:
<div class="comment-cont">
<div
class="upvote-arrow"
ng-show="!collapsed"
ng-click="vote()"
ng-class="{'active' : node.votedByMe}"
>
<div class="upvote-arrow-top"></div>
<div class="upvote-arrow-bottom"></div>
</div>
<div class="wrapper">
<div class="info">
<span class="collapse" ng-click="collapsed = !collapsed">[ {{collapsed ? '+' : '–'}} ]</span>
<span ng-class="{'collapsed-style' : collapsed}">
<a ui-sref="main.profile({id: node.userId})">{{node.username}}</a>
<span>{{node.upvotes}} point{{node.upvotes != 1 ? 's' : ''}}</span>
<span mg-moment-auto-update="node.createdTime"></span>
<span ng-show="collapsed">({{node.children.length}} child{{node.children.length != 1 ? 'ren' : ''}})</span>
</span>
</div>
<div class="text" ng-bind-html="node.comment | autolink | nl2br" ng-show="!collapsed"></div>
<div class="reply" ng-show="!collapsed">
<span ng-click="formHidden = !formHidden">reply</span>
</div>
</div>
<div class="input-area" ng-show="!collapsed">
<form ng-show="!formHidden" name="form" autocomplete="off" novalidate ng-submit="submitted = true; submit(formData, form)">
<div class="form-group comment-input-feedback-branch has-error" ng-show="form.comment.$invalid && form.comment.$dirty">
<div ng-messages="form.comment.$error" ng-if="form.comment.$dirty">
<div class="input-feedback" ng-message="required">Comment text is required.</div>
<div class="input-feedback" ng-message="maxlength">Comment text cannot exceed 2500 characters.</div>
</div>
</div>
<div
class="form-group"
ng-class="{ 'has-error' : form.comment.$invalid && form.comment.$dirty, 'has-success' : form.comment.$valid }"
>
<textarea
name="comment"
class="textarea comment-cont-textarea"
ng-model="formData.comment"
required
ng-maxlength="2500"
textarea-autoresize
></textarea>
</div>
<div class="form-group">
<mg-button-loading
mgbl-condition="awaitingResponse"
mgbl-text="Save"
mgbl-loading-text="Saving..."
mgbl-class="btn-blue btn-small"
mgbl-disabled="!form.$valid"
></mg-button-loading>
<mg-button-loading
mgbl-text="Cancel"
mgbl-class="btn-white btn-small"
ng-click="formHidden=true;"
></mg-button-loading>
</div>
</form>
</div>
<div ng-show="!collapsed">
<div ng-repeat="node in node.children" ng-include="'commentTree'"></div>
</div>
</div>
commentCont.directive.js:
(function () {
'use strict';
angular
.module('app')
.directive('commentCont', commentCont);
/* #ngInject */
function commentCont ($http, user, $timeout) {
return {
restrict: 'E',
replace: true,
scope: {
node: '='
},
templateUrl: 'app/post/commentCont.tpl.html',
link: function (scope, element, attrs) {
var textarea = element.querySelector('.comment-cont-textarea');
var voteOK = true;
var action = '';
var userInfo = user.get();
scope.formHidden = true; // Do not ng-init="" inside an ng-repeat.
scope.collapsed = false; // Do not ng-init="" inside an ng-repeat.
// Autofocus textarea when reply link is clicked.
scope.$watch('formHidden', function(newValue, oldValue) {
if (newValue !== true) {
$timeout(function() {
textarea[0].focus();
});
}
});
scope.submit = function (formData, form) {
if (form.$valid) {
scope.awaitingResponse = true;
formData.parentId = scope.node.id;
formData.postId = scope.node.postId;
formData.type = 4;
formData.fromUsername = userInfo.username;
formData.toId = scope.node.userId;
formData.fromImage = userInfo.thumbnail36x36.split('/img/')[1];
// console.log(formData);
$http.post('/api/comment', formData)
.then(function (response) {
scope.awaitingResponse = false;
if (response.data.success) {
if (response.data.rateLimit) {
alert(rateLimitMessage);
return false;
}
// id and createdTime is sent with response.data.comment.
var c = response.data.comment;
var newCommentNode = {
id: c.id,
userId: userInfo.id,
username: userInfo.username,
parentId: formData.parentId,
comment: formData.comment,
upvotes: 0,
createdTime: c.createdTime,
votedByMe: false,
children: [],
postId: scope.node.postId
};
// console.log('user', user.get());
// console.log('scope.node', scope.node);
// console.log('response', response);
// console.log('newCommentNode', newCommentNode);
formData.comment = '';
form.comment.$setPristine();
scope.formHidden = true;
scope.node.children.unshift(newCommentNode);
}
});
}
};
scope.vote = function() {
if (voteOK) {
voteOK = false;
if (!scope.node.votedByMe) {
scope.node.votedByMe = true;
action = 'add';
} else {
scope.node.votedByMe = false;
action = 'remove';
}
var data = {
commentId: scope.node.id,
action: action
};
$http.post('/api/comment/vote', data)
.then(function (response) {
// console.log(response.data);
voteOK = true;
if (action === 'add') {
scope.node.upvotes++;
} else {
scope.node.upvotes--;
}
});
}
};
}
};
}
}());
The tree is being called like this:
<script type="text/ng-template" id="commentTree">
<comment-cont
node="node"
></comment-cont>
</script>
<div ng-repeat="node in tree[0].children" ng-include="'commentTree'"></div>
How can I have more than 10 levels of nested ng-repeat without getting an error like this?
The default implementation of $digest() has limit of 10 iterations . If the scope is still dirty after 10 iterations error is thrown from $digest().
The below stated is one way of configuring the limit of digest iterations to 20.
var app = angular.module('plunker', [], function($rootScopeProvider) {
$rootScopeProvider.digestTtl(20); // 20 being the limit of iterations.
});
But you should look in to stabilizing your model rather than configuring the limit of iterations.
I was dealing with a similar issue. end up with the following directive:
(function () {
'use strict';
angular
.module('app')
.directive('deferDom', ['$compile', ($compile) => {
return {
restrict: 'A',
compile: (tElement) => {
// Find, remove, and compile the li.node element.
let $el = tElement.find( "li.node" );
let transclude = $compile($el);
$el.remove();
return function($scope){
// Append li.node to the list.
$scope.$applyAsync(()=>{
tElement.append(transclude($scope));
});
}
},
};
}]);
})();

Angular Directive in ng-repeat generating randomly uncorrect DOM

Hello i have a directive like this in my ASP MVC application + Kendo Upload (doesn't matter)
<div ng-repeat="dp in dealerphotos" class="imgbox">
<div ng-if="dp.DealerPhotoId != 0">
<div class="img-wrapper">
<a data-lightbox="{{ dp.DealerId }}{{ dp.PhotoId }}{{ dp.FileName }}" data-title="{{ dp.FileName }}" href="~/Upload/{{ dp.DealerId }}/{{ dp.PhotoId }}/{{ dp.FileName }}">
<img ng-src="~/Image/Resized?size={{ resize }}&dealerId={{ dp.DealerId }}&photoId={{ dp.PhotoId }}&fileName={{ dp.FileName }}" alt="" class="uploaded" center-preview />
</a>
</div>
<div class="btnblock">
<button ng-click="changePhoto(dp.DealerPhotoId, dp.PhotoId, $index)" title="#Resources.Strings.ButtonChange"><span class="icon pencil"></span></button>
<button ng-click="removePhoto(dp.DealerPhotoId)" title="#Resources.Strings.ButtonRemove"><span class="icon trash"></span></button>
</div>
</div>
<div ng-show="dp.DealerPhotoId == 0" ng-attr-class="{{ (dp.IsFirstMandatory || dp.IsFirstMandatoryIfInstalled) ? 'mandatory' : '' }}">
<input name="files"
type="file"
kendo-upload
k-multiple="false"
k-async="{ saveUrl: '/Steps/Save', autoUpload: true }"
k-select="onSelect"
k-error="onError"
k-success="onSuccess"
k-upload="onUpload"
title="#Resources.Strings.ButtonAdd" />
<div class="mandatory-marker">
<p ng-if="dp.IsFirstMandatory">#Resources.Strings.StepMandatory</p>
<p ng-if="!dp.IsFirstMandatory && dp.IsFirstMandatoryIfInstalled" class="installed">#Resources.Strings.StepMandatoryIfInstalled</p>
</div>
</div>
</div>
With .js code:
//IFFE
(function () {
var app = angular.module("app");
app.directive('tripleUploadDirective', ['$http', function () {
var controller = [
'$scope', '$http', function ($scope, $http) {
$scope.dealerPhotoId = null;
$scope.removePhoto = function (id) {
swal({
title: "", text: localization.StepRemoveConfirm, type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: localization.AlertYesRemove,
closeOnConfirm: true,
animation: "slide-from-top"
}, function () {
$http.post("/Steps/Remove", { dealerPhotoId: id }).then(function (data) {
for (var i = 0; i < data.data.length; i++) {
if (i == 0 && data.data[i].DealerPhotoId == 0) {
if ($scope.mandatory)
data.data[i].IsFirstMandatory = true;
if ($scope.mandatoryifinstalled)
data.data[i].IsFirstMandatoryIfInstalled = true;
}
}
$scope.dealerphotos = data.data;
});
});
};
$scope.changePhoto = function (dealerPhotoId, id, index) {
swal({
title: "", text: localization.StepChangeConfirm, type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: localization.AlertYesChange,
closeOnConfirm: true,
animation: "slide-from-top"
}, function () {
$scope.dealerPhotoId = dealerPhotoId;
var uploadinput = angular.element("#div_" + id + " input[name=files]")[index];
angular.element(uploadinput).click();
});
};
$scope.onSelect = function (e) {
$.each(e.files, function (index, value) {
var ok = value.extension.toLowerCase() == ".jpg" || value.extension == ".jpeg";
if (!ok) {
e.preventDefault();
swal({ title: "", text: localization.StepValidateExtension, type: "error", confirmButtonText: localization.AlertOK, animation: "slide-from-top" });
}
});
};
$scope.onError = function (e) {
swal({ title: "", text: e.XMLHttpRequest.response, type: "error", confirmButtonText: localization.AlertOK, animation: "slide-from-top" });
$scope.$apply();
};
$scope.onSuccess = function (e) {
if (e.response != null) {
for (var i = 0; i < e.response.length; i++) {
if (i == 0 && e.response[i].DealerPhotoId == 0) {
if ($scope.mandatory)
e.response[i].IsFirstMandatory = true;
if ($scope.mandatoryifinstalled)
e.response[i].IsFirstMandatoryIfInstalled = true;
}
}
$scope.dealerphotos = e.response;
$scope.$apply();
}
};
$scope.onUpload = function (e) {
e.data = { photoId: $scope.photoid, dealerPhotoId: $scope.dealerPhotoId };
$scope.dealerPhotoId = null;
};
}];
return {
restrict: 'EAC',
scope: {
dealerphotos: '=dealerphotos',
photoid: '=photoid',
mandatory: '=mandatory',
mandatoryifinstalled: '=mandatoryifinstalled',
resize: '=resize'
},
replace: true,
templateUrl: '/Directives/TripleUpload',
controller: controller
};
}]);
})();
This directive is used in ng-repeat:
<div ng-repeat="step in matrixCtrl.allSteps" class="m-step">
<div class="step-title">{{step.stepIndex}} | {{ step.Name }}</div>
<div triple-upload-directive dealerphotos="step.dealerPhotos" photoid="step.PhotoId" resize="170" mandatory="step.IsMandatory" mandatoryifinstalled="step.IsMandatoryIfInstalled" class="img-uploder {{ step.IsMandatory ? 'mandatory' : '' }}"></div>
</div>
I am setting allSteps in matrixCtrl in success() callback of $http.get().
My problem is the very strange behaviour of ng-repeat.. sometimes (very randomly), my DOM is not generated correctly. (all directives are rendered only in last ng-repeat iteration), in other iterations there are only titles.
Incorrect rendering image:

Resources