Sortable.Js newbie here, so please be gentle.
I'm trying to implement a sortable list in my page using Sortable.js. I'm not getting any errors upon execution, however when I tried to drag my list items, they're not moving at all.
Here's my HTML:
<ul id="forcedranking" class="wizard-contents-container-ul forcedRankCls" ng-show="isForcedRankingQuestion">
<div class="answers-container">
<div class="answer-child-container">
<li ng-repeat="query in currentQuestionObject.choices | orderBy:['sequence','answer']" class="listModulePopup forcedRankingChoice" data-index="{{$index}}" ng-model="query.value">
{{query.answer}}
</li>
</div>
</div>
</ul>
And here's my JS:
/* Get current input type for Clear All checkbox activation */
$scope.currentInputType = $scope.currentQuestionObject.inputType.toLowerCase();
if ($scope.currentInputType == "forced ranking") {
$scope.isForcedRankingQuestion = true;
/* SORTABLE FOR FORCED RANKING */
var mySort = document.getElementById("forcedranking");
// forcedranking is my id for the <ul>
var sortable = Sortable.create(forcedranking, {});
// Tried setting this because I thought this was the culprit. Turns out it's not.
sortable.option("disabled", false);
I called my Sortable.js like so (underneath my angularJS libraries):
<script type="text/javascript" src="content1/carousel/Sortable.js"></script>
Overall, I think Sortable's a really neat library, which is why I want to make it work so bad. But I just don't know what I'm doing wrong here.
The problem is you are not following the sortable angular documentation. They recently change the project and separated the js from the frameworks.
So first you need to include the lib and the angular sortable lib angular-legacy-sortablejs
Sortable.js
ng-sortable.js
Second inject the module 'ng-sortable' in your app
Then you can pass the options (if you need to) in the html via directive or use it in the controller
HTML:
<ul ng-sortable="{group: 'foobar'}">
<li ng-repeat="query in currentQuestionObject.choices">{{query.answer}}</li>
</ul>
Or you can pass an object declared in your controller with the options, ex:
$scope.sortableConfig = {
group: 'collection',
animation: 150,
handle: '.handle',
filter: '.inbox'
}
<ul ng-sortable="sortableConfig">
<li ng-repeat="collection in test">
<div class="handle"></div>
{{collection.name}}
</li>
</ul>
Hope this help you!
I think what's going on here is that your JS is being executed before Angular is done rendering all the LI items, but it's hard to tell from your snippet of JS.
Just for testing, see if the items become draggable after 1 second if you change these two lines:
/* SORTABLE FOR FORCED RANKING */
var mySort = document.getElementById("forcedranking");
// forcedranking is my id for the <ul>
var sortable = Sortable.create(forcedranking, {});
into this:
window.setTimeout(function() {
/* SORTABLE FOR FORCED RANKING */
var mySort = document.getElementById("forcedranking");
// forcedranking is my id for the <ul>
var sortable = Sortable.create(forcedranking, {});
}, 1000);
Also, I don't believe that <div> elements are valid children for <ul> elements, so that may also be your problem. If the Javascript change does nothing, then perhaps you should try to change your html so that your <li> items are direct children of the <ul> element.
Hope this helps.
Related
I am new in AngularJS and I am using component in my module.
I have module:
angular.module("reviewApp",[]).component("applicantDetails",{
templateUrl : '../comp/Applicant_Details_Fields.html',
controller : function($scope){...}
});
At this moment I can use my component from HTML code like this
<applicant-details></applicant-details>
But now I need to change it to be more flexible. Depending on given N value it must add that component in the HTML N times.
For example N = 3; then it must dynamically add in my html code 3 times
<applicant-details></applicant-details>
Can I achieve this functionality with AngularJS with simple way? I tried several ways, and searched many times, but I couldn't find how to do this.
Even I tried to add that tags with JavaScript, but it understands only empty tags without content.
This is an example of simple looping in angular js
<div ng-app="myapp">
<div ng-controller="ctrlParent">
<ul>
<li ng-repeat="i in getNumber(myNumber) track by $index"><span>{{$index+1}}</span></li>
</ul>
<ul>
<li ng-repeat="i in getNumber(myOtherNumber) track by $index"><span>{{$index+1}}</span></li>
</ul>
</div>
var app = angular.module('myapp',[]);
app.controller('ctrlParent',function($scope){
$scope.myNumber = 5;
$scope.myOtherNumber = 10;
$scope.getNumber = function(num) {
return new Array(num);
}
});
check output:http://jsfiddle.net/sh0ber/cHQLH/
I am having an array in controller as follows
function FooController($scope) {
$scope.items = ["a","b","c"];
}
I used ng-repeat to show the data in items,
<div ng-app>
<div ng-controller="FooController">
<ul ng-repeat="item in items">
<li>{{item}}</li>
</ul>
</div>
</div>
I got the result as a,b,c order. I want to show it in reverse order. that means c,b,a without changing its order in the controller or without creating a filter. How to do that?
Check this link
I faced the same problem, but with an array of objects. This quick solution worked for me, but using the filter is still the best practice.
<li data-ng-repeat="item in data.slice().reverse()">
...
</li>
http://bootply.com/SgVBZvZ708
In order to avoid the writting of a custom filter, I suggest you to use this syntax :
<ul ng-repeat="item in items | orderBy:'-toString()'">
The documentation assume you sort an array of Objects but in your case there are just plain strings.
Wrap the strings in objects and use orderBy or create a new filter:
angular.module("app",[])
.filter("reverse", function(){
return function(items){
return items.slice().reverse(); // Create a copy of the array and reverse the order of the items
};
});
And use it like this:
<ul ng-repeat="item in items|reverse">
Updated fiddle (I've also updated the ng-app directive so it's passed the "app" module.)
I'm writing a pagination function in an angular app. I have got a <div> container, that has a size depending on the window size. In that container, there is my list. Now this list has to be split into n pages to avoid having a scrollbar. Everything works fine yet, except for the fact, that I have to hardcode the height of a list element.
What I need to calculate the number of pages for the pagination is:
The length of the list (number of list items),
the height of the container (refreshed on each window.onresize) and
the height of one list element.
I am able to keep track of the list length. I am also able to fetch the height of the container and to refresh that value on window.onresize.
But I am not able to fetch the height of a list element in the DOM tree.
I've written a directive, to pass the scope and selector names from a template into my code:
<div class="multicolumn-list" ng-controller="mymodule.listtypes.multicolumn.controller">
<pagination-updater space-selector=".list-spacer" item-selector="li:first" page-size-model="listmodel.pageSize" listen="listmodel.list.items">
<div class="list-spacer">
<ul>
<li ng-repeat="item in listmodel.pageItems">
<a ng-click="mymodule.selectItem(item)">{{item.id}} ({{item.type}})</a>
</li>
</ul>
</div>
</pagination-updater>
</div>
My directive looks like this right now:
'use strict';
angular.module('shared.directives.paginationUpdater', ['shared.services.dimensions']);
angular.module('shared.directives.paginationUpdater').directive('paginationUpdater', function() {
return {
restrict: 'E',
link: {
'post': function (scope, iElement, iAttrs, controller) {
var pageSizeModelName = iAttrs.pageSizeModel;
var setPageSize = function () {
var spaceElement = iElement.find(iAttrs.spaceSelector);
var itemElement = iElement.find(iAttrs.itemSelector);
console.log(itemElement.height());
};
scope.$watch('$parent.' + iAttrs.listen, function () { setPageSize(); }, true);
scope.$on('windowResized', function () { setPageSize(); });
setPageSize();
}
}
};
});
When I run my application, the list is empty, so there is no <li> and itemElement.height() returns 0. Then my list becomes updated, then the setPageSize() will be called again, but itemElement.height() still returns 0. I think it is, because this code is executed before the list will be rendered into the page. Do you have any idea, how I can archive this? Right now, I have the height of an element hardcoded and the best compromise I see is to pass the height value from the template to the directive:
<div class="multicolumn-list" ng-controller="mymodule.listtypes.multicolumn.controller">
<pagination-updater space-selector=".list-spacer" item-height="{{main.zoomFactor}} * 26 + 1" page-size-model="listmodel.pageSize" listen="listmodel.list.items">
<div class="list-spacer">
<ul>
<li ng-repeat="item in listmodel.pageItems">
<a ng-click="mymodule.selectItem(item)">{{item.id}} ({{item.type}})</a>
</li>
</ul>
</div>
</pagination-updater>
</div>
But then still I have to change the templates if we have a change in the style sheets. I also don't want to write a timeout. Maybe there is an event angular fires after updating the DOM tree?
A little hack you could do to get around this is to have a 'dummy' list item on load. Unfortunately it does need to be visible at render time. So you could do something like this, that way you have a <li> Item rendered and the should be enough to get it to have some height without any content being visible to the user while your data is being fetched.
<ul>
<li class="dummy-item"
ng-show="!listMode.pageItems.length"><a> </a></li>
<li ng-repeat="item in listmodel.pageItems">
<a ng-click="mymodule.selectItem(item)">{{item.id}} ({{item.type}})</a></li>
</ul>
I know this is an old question but hope it still helps.
I am trying to create a contact book in which every person can have a photo.
As I would like to display the contact before getting its photo, I would write something like
<ul>
<li ng-repeat="person in persons">{{person.firstname}} {{persone.lastname}}</li>
</ul>
I would try to call a get_photo(person) available in my scope.
<ul>
<li ng-repeat="person in persons">{{person.firstname}} {{persone.lastname}}
<img ng-src="{{get_photo(person)}}" />
</li>
</ul>
Which doesn't work because it binds the function to ng-src and call get_photo every time ng-repeat is called.
I finally tried to call get_photo when initializing the DOM element and store the result in a specific attribute photo
<ul>
<li ng-repeat="person in persons">{{person.firstname}} {{persone.lastname}}
<img ng-init="get_photo(person)" ng-src="{{person.photo}}" />
</li>
</ul>
$scope.get_photo = function (person) {
// Simple example; I will try to get an image through xhr request later...
person.photo = "http://img.blogduwebdesign.com/benjamin-sanchez/737/AngularJS.jpg";
};
Is there an easier / cleaner way to do the same ?
I also created a jsFiddle : http://jsfiddle.net/V4Mss/
Thanks a lot for your feedback !
t00f
Your last example seems fine but here is another option that keeps the view a little cleaner and doesn't cause the double $digest. Remove the initialization function from each element and use the function as a getter.
HTML
<img ng-src="{{person.photo}}" />
JavaScript
myapp.controller('controller', function ($scope) {
$scope.count = 0;
$scope.get_photo = function (person) {
return "http://img.blogduwebdesign.com/benjamin-sanchez/737/AngularJS.jpg";
};
$scope.persons = [{firstname:"toto", lastname:"TOTO", photo:$scope.get_photo()},
{firstname:"tata", lastname:"TATA", photo:$scope.get_photo()},
{firstname:"titi", lastname:"TITI", photo:$scope.get_photo()}];
});
Here is a fiddle
I am relatively new to AngularJS and am loving every moment of it! I have an unordered list of products. When the user hovers over them, I need the list element to have an active class set on them. I am currently doing it this way:
<ul>
<li ng-repeat="product in products" ng-mouseover="showDetails(product, $event)" ng-class="{active: current == product}">{{product}}</li>
</ul>
And my showDetails is as follows:
function showDetails(product, $event) {
$scope.current = product;
...some other logic...
}
Now, everything is working just fine, however, I was wondering if it was possible to set the class without the ng-repeat and having no product variable to bind to?
<ul>
<li ng-mouseover="showDetails($event)" ng-class="{<i don't know what to put here> }">foo</li>
<li ng-mouseover="showDetails($event)" ng-class="{<i don't know what to put here> }">bar</li>
<li ng-mouseover="showDetails($event)" ng-class="{<i don't know what to put here> }">A</li>
<li ng-mouseover="showDetails($event)" ng-class="{<i don't know what to put here> }">B</li>
</ul>
How should I write my showDetails function to set the class this time? My first try is:
function showDetails($event) {
var text = $event.target.textContent;
$scope.current = text
}
But what do I do in the ng-class attribute?
If a pure CSS solution is not possible then you can create a directive and toggle a CSS class using JQuery within that directive. Apply the directive on the ul as an attribute or class. In that directive you can do
iElement.find("li").hover(...