Flickity carousel: Items pushed out of viewport with ng-repeat? - angularjs

Im trying to use metafizzy's flickity framework to display content dynamically, using angulars ng-repeat.
But for some reason the items seem to get pushed out from the flickity-viewport when loaded onto the DOM. Anyone know why that happens and how to avoid it?
The gallery works fine When displaying static content inside it like this;
HTML : STATIC MARKUP EXAMPLE
<div ng-controller="FlickityCtrl">
<div id="main-content" class="gallery js-gallery">
<div class="gallery-cell"> Static Title </div>
<div class="gallery-cell"> Static Title </div>
<div class="gallery-cell"> Static Title </div>
</div>
..its When trying to populate the gallery with the help of angular's ng-repeat directive,that the gallery breaks.
HTML : MARKUP USING NG-REPEAT
<div ng-controller="FlickityCtrl" >
<div id="main-content" class="gallery js-gallery">
<div ng-repeat="chapter in chapters" ng-click="loadSubchapters(chapter.title)">
<h1 class="gallery-cell cell-card-bg">
{{ chapter.title | strip_namespace }}
</h1>
</div>
</div>
<hr>
<button ng-click="loadChapters()" >Load chapters</button>
<hr>
<ul>
<li ng-repeat="chapter in subchapters">
{{ chapter.title | strip_namespace }}
</li>
</ul><br />
<hr >
</div>
JAVASCRIPT
angular.module('FlickityApp', [])
.controller('flickityCtrl', ['$scope', '$timeout', function ($scope, $timeout) {
var updateUI = function(data) {
if (!data || !data.query) { return; }
$timeout(function() {
$scope.chapters = data.query.pages;
console.log(data);
});
};
$scope.loadChapters = function() {
mw.loader.using('mediawiki.api', function() {
(new mw.Api()).get({
action: 'query',
generator: 'categorymembers',
gcmtitle: 'Category:examplepage'
}).done(function(data) {
$timeout(function() {
$scope.chapters = data && data.query ? data.query.pages : {};
});
});
});
};
$scope.loadSubchapters = function(chapterTitle) {
mw.loader.using('mediawiki.api', function() {
(new mw.Api()).get({
action: 'query',
generator: 'categorymembers',
gcmtitle: chapterTitle
}).done(function(data) {
$timeout(function() {
$scope.subchapters = data && data.query ? data.query.pages : {};
});
});
});
};
}])
.filter('strip_namespace', ['$sce', function($sce){
return function(text) {
text = text.split(":");
return text.length > 1 ? text[1] : text[0];
};
}]);
.directive('flickity', [function() {
return {
restrict: 'E',
templateUrl: 'templates/view.html',
replace: true,
scope: { chapters: '=' },
link: function(scope, elem, attrs, ctrl) {
scope.$watch('chapters', function() {
elem.flickity({
// settings
});
});
}
};
}]);
angular.element(document).ready(function() {
angular.bootstrap(document, ['FlickityApp']);
var flkty = new Flickity('.gallery');
});
Link to flickity api : http://flickity.metafizzy.co/api.htm

Related

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));
});
}
},
};
}]);
})();

Using custom tags with angular directives seems to replace all custom tag content

I'm working on an app right now and I am running into an issue it doesn't seem a lot of tutorials answer. An example can be viewed here.
var app = angular.module('test',[])
var box = angular.element(document.querySelector('#box ul'));
var offset = 0;
app.controller('box',function($scope, $compile, $http){
$scope.add=function(){
offset+=1;
box.append($compile('<user-list-item></user-list-item>')($scope));
}
})
app.directive('userListItem',function($http, $compile){
return{
template:'<li ng-repeat="user in users" >{{user.f_name}} {{user.l_name}}</li>',
restrict:'E',
controller:function($scope){
$http.get('http://dev.firepixel.com/high5rn/api/users&limit=1&offset='+offset).success(function(data){
if(typeof data.output != 'string'){
$scope.users = data.output;
}
else{
alert('end of users')
$('button').remove()
}
})
}
}
})
#box{
min-height:200px;
border: 1px dashed #666;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div ng-app="test">
<div id="box" ng-controller="box">
<ul>
</ul>
<button ng-click="add()">add</button>
</div>
</div>
When inserting the tag and compiling it so it renders info, the first one displays fine, but when I append another, it replaces the original one with the new one, and adds another one with the same information. How can I solve this?
try this :
Script :
var app = angular.module('test',[])
var box = angular.element(document.querySelector('#box ul'));
var offset = 0;
app.controller('box',function($scope, $compile, $http){
$scope.users = [];
$scope.add=function(){
offset+=1;
$http.get('http://dev.firepixel.com/high5rn/api/users&limit=1&offset='+offset).success(function(data){
if(typeof data.output != 'string'){
$scope.users.push(data.output[0]);
}
else{
alert('end of users')
$('button').remove()
}
})
}
})
app.directive('userListItem',function($http, $compile){
return{
template:'<li ng-repeat="user in users">{{user.f_name}} {{user.l_name}}</li>',
scope: {
users : '=data'
},
restrict:'E',
replace:true,
controller:function($scope){
}
}
})
HTML :
<div ng-app="test">
<div id="box" ng-controller="box">
<ul>
<user-list-item data="users"></user-list-item>
</ul>
<button ng-click="add()">add</button>
</div>
</div>
I think the error is that You assign each time a new value to users array instead pushing.
Try to replace your directive with this one:
app.directive('userListItem',function($http, $compile){
return{
template:'<li ng-repeat="user in users" >{{user.f_name}} {{user.l_name}}</li>',
restrict:'E',
controller:function($scope){
$http.get('http://dev.firepixel.com/high5rn/api/users&limit=1&offset='+offset).success(function(data){
if(typeof data.output != 'string'){
$scope.users.push(data.output);
}
else{
alert('end of users')
$('button').remove()
}
})
}
}
})
create a new scope for all directive: like this->
app.directive('userListItem',function($http, $compile){
return{
template:'<li ng-repeat="user in users" >{{user.f_name}} {{user.l_name}}</li>',
restrict:'E',
scope:{},
controller:function($scope){
$http.get('http://dev.firepixel.com/high5rn/api/users&limit=1&offset='+offset).success(function(data){
if(typeof data.output != 'string'){
$scope.users = data.output;
}
else{
alert('end of users')
$('button').remove()
}
})
}
}
})
see link : http://plnkr.co/edit/lBzTmXALihUS2l8qDJib?p=preview

Angular sce.trustAsHtml not working

I have this angular controller :
var applicaton = angular.module("appUsed", ['ui.router','ngSanitize'] );
applicaton.controller('gamesController', ['$scope','$http','$sce','$stateParams',function(scope,http,sce,stateParams){
http.get('/'+stateParams.category+'/'+stateParams.id)
.success(function(result){
scope.Game = result.gameDetails;
scope.relatedGames = result.relatedGames;
console.log(scope.Game.title);
console.log(scope.Game.url);
scope.gameUrl = sce.trustAsHtml('<iframe allowfullscreen width="80%" height="600px src="'+scope.Game.url+'"></iframe>');
});
}]);
and this html :
<div class="game_and_description">
<div ng-bind-html="gameUrl"></div>
<h3> Description</h3>
<p> {{Game.description}}</p>
It shows me a white iframe. I searched over the internet and i've done everything right. The modules form angular ng-sanitize is running(called from <script> tag) and i have no error. the console log on scopes works like a charm. Don't know where should i look anymore. Please help.
You need to give a trust to the URL you are using in the iframe, and compile the html:
<div ng-controller="gamesController">
<div bind-html-compile="gameFrame"></div>
</div>
var myApp = angular
.module('appUsed',['ngSanitize'])
.controller('gamesController', ['$scope', '$sce', function (scope, sce) {
scope.Game = {
url: 'https://play.famobi.com/hop-dont-stop/A-DXC93'
};
scope.gameUrl = sce.trustAsResourceUrl(scope.Game.url);
scope.gameFrame = sce.trustAsHtml('<iframe allowfullscreen width="80%" height="600px" ng-src="{{gameUrl}}"></iframe>');
}])
.directive('bindHtmlCompile', ['$compile', function ($compile) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.$watch(function () {
return scope.$eval(attrs.bindHtmlCompile);
}, function (value) {
element.html(value && value.toString());
var compileScope = scope;
if (attrs.bindHtmlScope) {
compileScope = scope.$eval(attrs.bindHtmlScope);
}
$compile(element.contents())(compileScope);
});
}
};
}]);
See https://github.com/incuna/angular-bind-html-compile.
The working fiddle: http://jsfiddle.net/masa671/k2e43nvf/
I had a similar problem. I solved it like this :
my view :
<div ng-bind-html="getDescription()"></div>
my controller :
$scope.getDescription = function () {
if ($scope.description != null && $scope.todo.description.length > 0) {
return $sce.trustAsHtml($scope.description);
} else {
return 'no description.';
}
};

Angularjs dropzone, variable out of context

I'm trying to bind a variable to detect the upload progress of the dropzone queue but for some reason my control variable $scope.dropzone.sFileUploading jumps out of context when I alter it and angular doesn't update the template
Here is my controller:
angular.module('DBDescriptorApp')
.controller('DesignerController', ['$scope', '$rootScope', 'user',
function($scope, $rootScope, user) {
$rootScope.currentNav = 'designer';
$rootScope.currentUser = user.data;
$scope.$watch('dropzone', function (value) {
if(value != undefined) {
$scope.dropzone.sFileUploading = false;
$scope.dropzone.on('addedfile', function(file) {
$scope.dropzone.sFileUploading = true;
});
}
});
$scope.dropzoneConfig = {
url : 'dropzone',
parallelUploads : 3,
uploadMultiple : true,
maxFileSize : 30,
addRemoveLinks : 'dictCancelUpload'
};
}]);
Here is my template:
<div ng-include="'dist/templates/header.html'"></div>
<div class="container-fluid">
<div class="row-fluid">
<div class="col-md-12">
DESGINER
<hr/>
<form class="dropzone" method="post" enctype="multipart/form-data" ng-dropzone dropzone="dropzone" dropzone-config="dropzoneConfig">
</form>
<div ng-show="dropzone.sFileUploading">UPLOADING!</div>
</div>
</div>
</div>
I manage to update the scope using $apply(), and placed all in init as the Dropzone Docs suggested, here is the code:
$scope.dropzoneConfig = {
init : function() {
this.on('addedfile', function() {
$scope.$apply(function() {
$scope.filesUploading = true;
});
});
},
url : 'dropzone',
parallelUploads : 3,
uploadMultiple : true,
maxFileSize : 30,
addRemoveLinks : 'dictCancelUpload'
};

Accessing a service or controller in my link function - Angular.js

I have a directive, but I am having a problem access the controller and my service that is injected into it. Here is my directive:
angular.module('clinicalApp').directive('chatContainer', ['encounterService', function(encounterService) {
return {
scope: {
encounter: '=',
count: '='
},
templateUrl: 'views/chat.container.html',
controller: 'EncounterCtrl',
link: function(scope, elem, attrs, controller) {
scope.addMessage = function(message) {
//RIGHT HERE
scope.resetChat();
};
scope.resetChat = function() {
scope.chatText = '';
scope.updateCount(scope.chatText);
};
}
};
}]);
You can see that I am attaching a couple of functions to my scope inside the link function. Inside those methods, like addMessage, I don't have access to my controller or the service that is injected into the directive. How do I acceess the controller or service?
UPDATE
Here is the service:
angular.module('clinicalApp').factory('encounterService', function ($resource, $rootScope) {
var EncounterService = $resource('http://localhost:port/v2/encounters/:encounterId', {encounterId:'#id', port: ':8280'}, {
search: {
method: 'GET'
}
});
var newEncounters = [];
var filterTerms = {};
EncounterService.pushNewEncounter = function(encounter) {
newEncounters.push(encounter);
$rootScope.$broadcast('newEncountersUpdated');
};
EncounterService.getNewEncounters = function() {
return newEncounters;
}
EncounterService.clearNewEncounters = function() {
newEncounters = [];
}
EncounterService.setFilterTerms = function(filterTermsObj) {
filterTerms = filterTermsObj;
$rootScope.$broadcast('filterTermsUpdated');
EncounterService.getFilterTerms(); //filter terms coming in here, must redo the search with them
}
EncounterService.getFilterTerms = function() {
return filterTerms;
}
return EncounterService;
});
and the chat.container.html
<div class="span4 chat-container">
<h5 class="chat-header">
<span class="patient-name-container">{{encounter.patient.firstName }} {{encounter.patient.lastName}}</span>
</h5>
<div class="chat-body">
<div class="message-post-container">
<form accept-charset="UTF-8" action="#" method="POST">
<div class="text-area-container">
<textarea id="chatBox" ng-model="chatText" ng-keyup="updateCount(chatText)" class="chat-box" rows="2"></textarea>
</div>
<div class="counter-container pull-right">
<span class="muted" id="counter">{{count}}</span>
</div>
<div class="button-container btn-group btn-group-chat">
<input id="comment" class="btn btn-primary btn-small btn-comment disabled" value="Comment" ng-click="addMessage(chatText)"/>
</div>
</form>
<div messages-container messages="encounter.comments">
</div>
</div>
</div>
</div>
Here is Demo Plunker I played with.
I removed scope{....} from directive and added 2 values in controller and directive to see how they change regards to action.
JS
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
// listen on any change of chatText in directive
$scope.$watch(function () {return $scope.chatText;},
function (newValue, oldValue) {
if (newValue == oldValue) {return;}
$scope.chatTextFromController = newValue;
}, true);
});
app.directive('chatContainer', ['encounterService', function(encounterService) {
return {
templateUrl: 'chat.container.html',
link: function(scope, elem, attrs) {
scope.countStart = scope.count;
scope.updateCount = function(chatText) {
alert('updateCount');
scope.count = scope.countStart - chatText.length;
};
scope.addMessage = function(message) {
alert('addMessage');
encounterService.sayhello(message);
scope.resetChat();
};
scope.resetChat = function() {
alert('resetChat');
scope.chatText = 'someone reset me';
scope.name = "Hello " + scope.name;
scope.updateCount(scope.chatText);
};
}
};
}]);
app.service('encounterService', function() {
var EncounterService = {};
EncounterService.sayhello = function(message) {
alert("from Service " + message);
};
return EncounterService;
});
HTML
<body ng-controller="MainCtrl">
<div chat-container></div>
<pre>chatText from directive: {{chatText|json}}</pre>
<pre>chatText from controller: {{chatTextFromController|json}}</pre>
<pre>name: {{name|json}}</pre>
</body>

Resources