How to create multiple elements drag and drop in angularjs - angularjs

Anyone knows or can point me to an example in which multiple draggable elements are being dragged into a container ?
I need to achieve this behavior using Angular JS.
So far I've tried this : http://codef0rmer.github.io/angular-dragdrop/#/ but it only seems to work for 1 element at a time.
Thansk

That plugin you mentioned doesn't support multi drag&drop out of box.
Here is a working method to support multi drag&drop using the same plugin:
http://embed.plnkr.co/lyCnU3gNeGRrTk1D9hh0/
After you open the link, click on any area to get it to focus and detect your keyboard presses, then hit ctrl and click on the items you want to drag them to make them selected. Finally, drag them.
How it works:
<div class="container form-inline" style="text-align: center;">
<div class="btn btn-droppable" ng-repeat="item in list5" data-drop="true" ng-model='list5' data-jqyoui-options="{accept:'.btn-draggable:not([ng-model=list5])'}" jqyoui-droppable="{index: {{$index}}}">
<div class="btn btn-info btn-draggable"
ng-class="{'selected':(multiSelectedDataColumnsIndecies.indexOf($index) > -1)}"
data-html="true"
data-drag="true"
data-jqyoui-options="draggableOptionsList"
ng-model="list5"
jqyoui-draggable="{index: {{$index}},animate:false,placeholder: 'keep'}"
ng-click="dataItemClick($index,$event,item)">
{{item.title}}
</div>
</div>
</div>
.
$scope.draggableOptionsList = {
appendTo: 'body',
snap: true,
cursor: 'move',
revert: 'invalid',
helper: function() {
console.log('Im in helper');
var selected = $('.selected');
if (selected.length === 0) {
selected = $(this);
}
var container = $('<div/>').attr('id', 'draggingContainer');
container.append(selected.clone());
return container;
}
};
Using jquery UI's helper method, I select all selected items and return them to show dragging effect. And then on click, if ctrl is pressed I save the selected items in a gloabl list array.
<div class="row-fluid">
<ul class="thumbnails">
<li class="span3" style='margin:10px;width: 100%; '>
<div class="thumbnail"
data-drop="true"
ng-model='list1'
data-jqyoui-options
jqyoui-droppable="{onDrop:'dropCallback(list1,$index)',beforeDrop: 'beforeDrop(list1)', multiple:true}">
<div class="caption">
<div class="btn btn-info btn-draggable" ng-repeat="item in list1" ng-show="item.title" data-drag="{{item.drag}}" data-jqyoui-options="{revert: 'invalid'}" ng-model="list1" jqyoui-draggable="{index: {{$index}},animate:true}">{{item.title}}</div>
</div>
</div>
</li>
</ul>
</div>
.
$scope.beforeDrop = function(event, ui, dataModel) {
//In case of multi drop
for (var i = 0; i < $scope.multiSelectedDataColumnsForDrop.length; i++) {
var isExisting = false;
for (var j = 0; j < dataModel.length; j++) {
if (dataModel[j].title == $scope.multiSelectedDataColumnsForDrop[i].title) {
isExisting = true;
break;
}
}
if (!isExisting) {
dataModel.push(angular.copy($scope.multiSelectedDataColumnsForDrop[i]));
}
}
var deferred = $q.defer();
deferred.resolve();
return deferred.promise;
};
In beforeDrop method I select set the model value using the global list of selected items.

If you are talking about nested dropzones check out one of these:
http://marceljuenemann.github.io/angular-drag-and-drop-lists/ (HTML5)
https://github.com/JimLiu/angular-ui-tree (pure JavaScript)
If you just want to drop two elements into the container, then the library you mentioned also supports that, see this example:
http://codef0rmer.github.io/angular-dragdrop/#/list

Related

AngularJS ng-if ng-show/hide within ng-repeat hiding all divs

I have seen similar questions related to this but this is different. In most of the questions, ng-hide/show fired on ng-click event.
Here is the code.
$scope.showDetails = 0;
$scope.delete = function(event) {
alert(event.target.id);
$scope.showDetails = 1;
//There would be more code for delete function. Some Ajax calls will be here.
}
<div ng-repeat="suggestions1 in suggestions">
<div class="col-xs-12 alert alert-info" ng-if="showDetails == '0'">
<center>
<a ng-click="delete($event)" id={{suggestions1.id}} class = "btn btn-danger">
<font size = "4">
<i class="fa fa-times-circle"></i>
</font>
Delete
</a>
</center>
</div>
</div>
Problem is when I click on the button it hides all the divs. I am expecting one div to hide but it is hiding all the div inside ng-repeat.
I have searched for multiple questions and tried the solutions but the issue still persists.
In that case you need to have a property named showDetails in each object of your new_suggestions array and enable ng-if based on that.
<div ng-repeat="suggestions1 in news_suggestions">
<div class="col-xs-12 alert alert-info" ng-if="suggestions1.showDetails == '0'">
Your code is logically wrong. You are keeping only one copy of showDetails variable. You need some property which is related to each object.
Try like this
$scope.delete = function(index) {
$scope.news_suggestions[index].hideDetails = true;
}
<div ng-repeat="suggestions1 in news_suggestions">
<div class="col-xs-12 alert alert-info" ng-hide="suggestions1.hideDetails">
<center> <a ng-click="delete($index)" id={{suggestions1.id}} class = "btn btn-danger"> <font size = "4"><i class="fa fa-times-circle"></i></font> Delete </a> </center></div></div>
There's one more approach:
// add an array
$scope.hiddenIds = [];
<div ng-repeat="suggestions1 in news_suggestions">
<!-- check if hidden -->
<div ng-hide=" hiddenIds.indexOf(suggestions1.id)>-1 " class="col-xs-12 alert alert-info">
<center>
<!-- remove/add to hidden array on click -->
<a ng-click=" hiddenIds.indexOf(suggestions1.id)>-1 ? hiddenIds.splice(hiddenIds.indexOf(suggestions1.id),1) : hiddenIds.push(suggestions1.id) " id={{suggestions1.id}} class = "btn btn-danger">
Here you don't modify the existing collection of elements.
Hint: if you hide the show/hide button, you won't be able to show the element back again. So you probably want to change your html layout a bit ;)
EDIT
Version 2:
You can always move it from your html to the scope.
// add an array
$scope.hiddenIds = [];
$scope.checkHide = function(suggestions1){
return $scope.hiddenIds.indexOf(suggestions1.id)>-1;
};
$scope.clickHide = function(suggestions1){
$scope.hiddenIds.indexOf(suggestions1.id)>-1 ? $scope.hiddenIds.splice($scope.hiddenIds.indexOf(suggestions1.id),1) : $scope.hiddenIds.push(suggestions1.id) ;
};
<div ng-repeat="suggestions1 in news_suggestions">
<!-- check if hidden -->
<div ng-hide="checkHide(suggestions1)" class="col-xs-12 alert alert-info">
<center>
<!-- remove/add to hidden array on click -->
<a ng-click="clickHide(suggestions1)" id={{suggestions1.id}} class = "btn btn-danger">
EDIT 2
If you don't need that element in news_suggestions (you're not planning to show it back again), you can simply remove it, which is even easier :)
$scope.remove = function(i){
$scope.news_suggestions.splice(i,1);
};
<div ng-repeat="suggestions1 in news_suggestions track by $index">
<div class="col-xs-12 alert alert-info">
<center>
<!-- remove by index on click -->
<a ng-click="remove($index)" id={{suggestions1.id}} class = "btn btn-danger">
JSBin
Yes, something wrong with your logic. Try this:
$scope.delete = function(el){
el.hideDetails = true;
}
<div ng-repeat="suggestions1 in news_suggestions">
<div class="col-xs-12 alert alert-info" ng-hide="hideDetails">
<center> <a ng-click="delete(this)" id={{suggestions1.id}} class = "btn btn-danger"> <font size = "4"><i class="fa fa-times-circle"></i></font> Delete </a> </center></div></div>

How to select first element in every page in pagination like $event click in angular?

I have created a pagination example dynamically in angular. Now ng-click event works fine with ng-repeat. when I click on any row element I styled it dynamically using ng-style(with red border). After that if I click on collapse button the selected row collapses. Now I want this selection to inherit to every first row element with border red without clicking it by default on pageload. just by directly clicking on collapse button, the first row should collapse just like how ng-click is working for every row. when I click on prev, next buttons in both gold and silver links how can I make this work only for every first element selected with red border & be also able to work with collapse button to it. do I have to use data-ng-init="";? Any help would be grateful. I have my plunkr link demo below including json file
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<style>
body{background:#fff;height:100%}h2{text-transform:capitalize;font-size:14px;color:#636363;font-weight:700}.small{width:100%;text-align:left;float:left;background:#efefef;line-height:50px;border-bottom:1px solid #bfbfbf}#btns{float:right;z-index:0;position:relative}#gold,#hide,#leftBtn,#rightBtn,#silver{width:auto;min-width:48px;height:48px;border:none;outline:0;float:left;border-radius:7px;margin:10px 10px 10px 0}#hide{margin:10px 0}#rightBtn{float:right}#gold,#silver{background:pink}.link{color:#2196F3;cursor:pointer}button[disabled],html input[disabled]{cursor:default;opacity:.4}div.caption{font-family:Oswald,sans-serif;color:#000;text-transform:uppercase;font-size:15px;margin:0;float:left}
</style>
<div ng-app="myApp" ng-controller="tabCtrl">
<div class="col-md-12" id="main">
<div class="row">
<button id="gold" class="active" ng-click="gold()">gold</button>
<button id="silver" class="" ng-click="silver()">silver</button>
<button id="hide" ng-click="hide()" class="pull-right"> collapse </button>
<div class="small" ng-repeat="x in myData | startFrom:(currentPage)*pageSize | limitTo:pageSize" ng-click="setActive(x, $event)" ng-style="activeMenu === x && divStyle">
<h2 class="col-md-4 pull-left"> {{ x.caption }} </h2>
<div class="col-md-4 pull-left"> {{ x.description }} </div>
<div class="col-md-4 link text-right">view site</div>
</div>
</div>
<!--previous, next buttons-->
<div id="btns">
<button id="rightBtn" ng-disabled="currentPage >= myData.length/pageSize - 1" ng-click="currentPage=currentPage+1;">next</button>
<button id="leftBtn" ng-disabled="currentPage == 0" ng-click="currentPage=currentPage-1">prev</button>
</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script>
var app = angular.module('myApp', []);
app.controller('tabCtrl', function ($scope, $http, $timeout) {
$scope.currentPage = 0;
$scope.pageSize = 5;
$scope.myData = [];
for (var i = 0; i < $scope.myData; i++) {
$scope.myData.push(i);
}
$scope.numberOfPages = function () {
return Math.ceil($scope.myData.length / $scope.pageSize);
}
var x, y;
$http.get("category.json").then(function (response) {
$scope.myData = response.data.gold;
$scope.gold = function () {
$scope.currentPage = 0;
$scope.myData = response.data.gold;
}
$scope.silver = function () {
$scope.currentPage = 0;
$scope.myData = response.data.silver;
}
$scope.setActive = function (list, $event) {
$scope.activeMenu = list;
y = '';
y = angular.element($event.target).prop('clientHeight');
$scope.divStyle = {
"border": '2px solid red',
"pointer-events": "none"
}
}
$scope.hide = function () {
$scope.divStyle = {
"height": y * 2 + 'px',
"border": '2px solid green',
"pointer-events": "none"
}
}
});
});
app.filter('startFrom', function () {
return function (input, start) {
/*if (!angular.isArray(input)) { return []; }*/
if (!input || !input.length) {
return;
}
start = +start; //parse to int
return input.slice(start);
};
});
</script>
PLUNKR
I think that this should be your solution, I think I have understood your requirements, please go through and let me know if you have any issues with the code.
Plunkr Demo
I am using the condition $index === 0, but I am also adding another condition where if there is any selection, the first row will get disabled. This gets reset when you press the buttons gold and silver
There was another issue in your code, you had declared the style object inside the $scope.setActive function, I have moved it out, only then the style will get applied by default.
Apart from this, this is how I implemented the functionality!
<div class="small" ng-repeat="x in myData | startFrom:(currentPage)*pageSize | limitTo:pageSize" ng-click="setActive(x, $event)"
ng-style="$index === 0 && testBool ? divStyle : activeMenu === x ? divStyle: ''">
<h2 class="col-md-4 pull-left"> {{ x.caption }} </h2>
<div class="col-md-4 pull-left"> {{ x.description }} </div>
<div class="col-md-4 link text-right">view site</div>
</div>
This testBool scope variable is intially set to true. Then when any of the elements is clicked its set to false using the function.
$scope.setActive = function (list, $event) {
$scope.testBool = false;
$scope.activeMenu = list;
y = '';
y = angular.element($event.target).prop('clientHeight');
}
Then finally when the user presses the gold or the silver button, the variable is reset to true so that the first element will be selected again.
<button id="gold" class="active" ng-click="gold();testBool = true;">gold</button>
<button id="silver" class="" ng-click="silver();testBool = true;">silver</button>
I hope my explanation was understandable, please let me know if you have any queries.
To select by default 1st element you can write something like:
ng-style="$index === 0 ? divStyle : ''"
You can also make your code simple by put out and write something like:
<div id="btns">
<button id="rightBtn" ng-disabled="currentPage >= myData.length/pageSize - 1"
ng-click="currentPage=currentPage+1;activeMenu=myData[currentPage*5];">next</button>
<button id="leftBtn" ng-disabled="currentPage == 0"
ng-click="currentPage=currentPage-1;activeMenu=myData[currentPage*5];">prev</button>
</div>
Demo PLunker
You can use $first property with ng-class in ng-repeat like
<div class="small" ng-class="$first?'active':''" ng-repeat="x in myData | startFrom:(currentPage)*pageSize | limitTo:pageSize" ng-click="setActive(x, $event)" ng-style="activeMenu === x && divStyle">

AngularJS - Enable and disable button

I have a condition and it is only on selecting an image among the optional images the next button needs to get enabled and also next should not get enabled on last outer select.
The problem here is on selecting the first image, the button is getting enabled and the scope is assigning the value to false and it is not getting true for the second iteration of images. How can I fix this issue?
<div class="col-xs-6 col-md-10 col-lg-7">
<div ng-repeat="outer in outers track by $index">
<div ng-if="outer_index== $index">
<div ng-bind="::outer.label"></div>
<ul class="list-group">
<li class="cursorPointer" ng-repeat="inner in outer.inners | orderBy: 'id'" ng-class="{'selected': getSelectedClass($parent.$index, $index)}" class="deselected">
<div class="col-xs-6">
<div class="img">
<img ng-src="data:image/png;base64,{{inner.base64Icon}}" alt="{{inner.description}}" title="{{inner.description}}"
ng-click="process()">
<div class="desc" ng-bind="inner.description"></div></div>
</div>
</li>
</ul>
</div>
</div>
</div>
<div><button type="button" class="btn btn-info" ng-disabled="outer_index == outers.length -1 || disableNext" ng-click="getNext()">Next</button></div>
In JS code, to activate the button only if,
$scope.disableNext=true;
$scope.process = function() {
$scope.disableNext=false;
}
$scope.getNext = function (){
$scope.outer_index = $scope.outer_index + 1;
$scope.datas = $scope.outers[$scope.outer_index];
}
NOTE: I tried
ng-click="process();disableNext=false;" for this condition won't work within ng-repeat.
Just set the disableNext to true after selecting next and it works!
$scope.getNext = function() {
$scope.disableNext=true;
$scope.outer_index = $scope.outer_index + 1;
$scope.datas = $scope.outers[$scope.outer_index];
}

applying class using ng-class for nested items in Angularjs

I am displaying the menu items in the below format. The data contains list of items and for each item there is a subitem. I need to apply the selected class for the subitem selected and all other subitems for all the items should be deselected. How to do this inside the controller. I tried by adding the ng-click event inside the html and toggling the class inside the controller but it is not applying for all the other subitems inside other items. Both html and controller code is shown below for more details.
<ol ng-model="data">
<li ng-repeat="item in data" collapsed="true">
<div ng-click="toggle(this)">
<a class="btn btn-success btn-xs" ng-if="item.children && item.children.length > 0">{{item.name}}</a>
</div>
<ol ng-model="item.children" ng-class="{hidden: collapsed}">
<li ng-repeat="subItem in item.children" ng-model="subItem.name" collapsed="true" ng-click="handleClick(subItem)">
<div ng-class="{'selected-li': subItem.value, 'deselected-li': false}">
{{subItem.name}}
</div>
</li>
</ol>
</li>
</ol>
Inside my controller I am having the code as below:
$scope.toggle = function (scope) {
scope.toggle();
};
$scope.handleClick=function(subitem){
subitem.value = subitem.value ? !subitem.value: true;
}
The data object used inside the UI contains the children also. Please let me know where I am going wrong.
HTML
<li ng-repeat="subItem in item.children" ng-model="subItem.name" collapsed="true" ng-click="handleClick($index, item.children)">
<div ng-class="{'selected-li': subItem.value, 'deselected-li': false}">
{{subItem.name}}
</div>
</li>
JS
$scope.handleClick = function(index, subItems) {
for(var i = 0; i < subItems.length; i++) {
if(i == index) {
subItems[i].value = true;
} else {
subItems[i].value = false;
}
}
}
What I'm doing here is, the index of the sub item and the entire item.children array are sent to the ng-click handler method and then in a for loop I am updating the value of all the sub items in that list.

Update facebook page list in to a drop down list

I am working on an angular app which integrates with facebook. I have given the code below,
$scope.getCompanyPages = function () {
fb.login(function (response) {
fb.api('/me/accounts', function (apiresponse) {
if (typeof apiresponse !== 'undefined' && typeof apiresponse.data !== 'undefined') {
$scope.facebookPages = apiresponse.data;
$scope.facebookPages.push({ id: "", name: 'Please select a page' });
$scope.selectedItem = $scope.facebookPages[2];
console.log($scope.facebookPages);
}
});
}, { scope: 'manage_pages' });
};
HTML
<div class="row">
<div class="col-xs-12 col-sm-6 center">
<a href="#" ng-click="shareToMyWall()">
<div class="primary-task">
<i class="fa fa-user fa-3x">
</i>
<h2 class="heading-text fa-bold">Personal Wall</h2>
</div>
</a>
</div>
<div class="col-xs-12 col-sm-6 center">
<a href="#" ng-click="getCompanyPages()">
<div class="primary-task">
<i class="fa fa-building fa-3x">
</i>
<h2 class="heading-text fa-bold">Company Wall</h2>
</div>
</a>
<select ng-if="facebookPages.length > 0" ng-change="shareToFacebookPage(selectedItem.id)" ng-model="selectedItem" ng-options="item.name for item in facebookPages track by item.id"></select>
</div>
</div>
The above method is called in hyperlink click (ng-click) event and the when the response comes back the drop down should update with data. The data does come back but it doesn't update straight away, rather I have to do another click anywhere in the page to update drop down list.
The data in the model is updated when you do the first ng-click. However, AngualrJS probably does not do check so the view still shows the old data. You can force a $digest() check and tell AngualrJS to compare old data and new data and update it if there is any difference. You can use $timeout, $evalAsync, or $apply to trigger $digest() which will update the data immediately.
For example:
$timeout(function () {
$scope.getCompanyPages = function () {...}
//or
$scope.facebookPages = apiresponse.data;
})
Edit: according to Angular JS best coding practice
Always wrap 3rd party API call-backs in to $apply to notify AngularJS
regarding out of environment changes.

Resources