I am looking for some help with my code I have so far.
The main objective is to be able to click on any Plus icon and have it place a cover over all other div blocks.
And when a plus icon is clicked it will also show a div block to the right.
As you will see when block 2 is clicked it does all that is intended.
I am looking for an efficient way to do this with Angular when any plus icon is clicked.
This is just a small sample I show here, in reality there would be 10 to 20 blocks to cover.
If someone could see a way to use less code here and make better use of the scope, this would be greatly appreciated.
I have looked at many options like in this post here.
Tried this but it doesn't want to work...
data-ng-class="{coverThisBlock: value2 == 'off',coverThisBlock: value == 'on'}"
If I had to use this type of option with even say 10 blocks, it would be a real mess.
The main Questions
Is there a better Angular way for this to work... when any plus icon is clicked it changes scope to then be used by ngclass and ng-show?
How to correctly wire up scope for this example?
Many Thanks.
I have set up a working FIDDLE HERE.
HERE IS THE FINAL WORKING EXAMPLE by Avijit Gupta.
<div class="container" ng-app="plusMinusApp" ng-controller="plusMinusController">
<div class="row" ng-init="value1 = 'off'">
<!--<div class="col-xs-4" data-ng-class="{coverThisBlock: value2 == 'off',coverThisBlock: value == 'on'}"> -->
<div class="col-sm-4 col-xs-6" data-ng-class="{coverThisBlock: value2 == 'off'}">
<div class="divClass"
data-ng-click="(selectBlock(1)) ; (status1 = !status1) ; (value1 = { 'on': 'off', 'off':'on'}[value1])"
data-ng-class="{'active-selection': status1 == activeClass}">
1
</div>
<i ng-click="(selectBlock(1)) ; (status1 = !status1) ; (value1 = { 'on': 'off', 'off':'on'}[value1])"
class="btn btn-primary text-center fa"
ng-class="{'fa-minus': status1, 'fa-plus': !status1}"></i>
</div>
<div ng-show="value1 == 'on'" class="col-xs-4 textdiv">Hello</div>
</div>
<div class="row" >
<div class="col-sm-4 col-xs-6" ng-init="value2 = 'on'">
<div class="divClass"
data-ng-click="(value2 = { 'on': 'off', 'off':'on'}[value2])"
data-ng-class="{'active-selection': value2 == 'off'}">
2
</div>
<i ng-click="(value2 = { 'on': 'off', 'off':'on'}[value2])"
class="btn btn-primary text-center fa"
ng-class="{'fa-minus': (value2 == 'off'), 'fa-plus': value2}"></i>
</div>
<div ng-show="value2 == 'off'" class="col-xs-3 textdiv">Hello</div>
</div>
<div class="row">
<div class="col-sm-4 col-xs-6" data-ng-class="{'coverThisBlock': value2 == 'off'}">
<div class="divClass"
data-ng-click="(selectBlock(3)) ; (status3 = !status3)"
data-ng-class="{'active-selection': !status3 == activeClass}">
3
</div>
<i ng-click="(selectBlock(3)) ; (status3 = !status3)"
class="btn btn-primary text-center fa"
ng-class="{'fa-minus': status3, 'fa-plus': !status3}"></i>
</div>
</div>
<div class="row">
<div class="col-sm-4 col-xs-6" data-ng-class="{'coverThisBlock': value2 == 'off'}">
<div class="divClass"
data-ng-click="(selectBlock(1)) ; (status4 = !status4)"
data-ng-class="{'active-selection': status4 == activeClass}">
4
</div>
<i ng-click="(selectBlock(1)) ; (status4 = !status4)"
class="btn btn-primary text-center fa"
ng-class="{'fa-minus': status4, 'fa-plus': !status4}"></i>
</div>
<div ng-show="status4" class="col-xs-4 textdiv">Hello</div>
</div>
<div class="row" ng-init="value = 'off'">
<div class="col-sm-4 col-xs-6" data-ng-class="{'coverThisBlock': value2 == 'off'}">
<div class="divClass"
data-ng-click="(selectBlock(1)) ; (status = !status) ; (value = { 'on': 'off', 'off':'on'}[value])"
data-ng-class="{'active-selection': status == activeClass}">
5
</div>
<i ng-click="(selectBlock(1)) ; (status = !status) ; (value = { 'on': 'off', 'off':'on'}[value])"
class="btn btn-primary text-center fa"
ng-class="{'fa-minus': status, 'fa-plus': !status}"></i>
</div>
<div ng-show="value == 'on'" class="col-xs-4 textdiv">Hello</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="js/plusMinusApp.j"></script>
<script>
var myModule = angular.module('plusMinusApp', []);
myModule.controller('plusMinusController', function ($scope) {
$scope.status = false;
$scope.status1 = false;
$scope.status2 = false;
$scope.status3 = false;
$scope.status4 = false;
$scope.blocks = [{
id: '1',
block: "1",
}, {
id: '2',
block: "2",
}, {
id: '3',
block: "3",
}, {
id: '4',
block: "4",
}, {
id: '5',
block: "5"
}];
// $scope.activeClass = 0;
$scope.selectBlock = function(id) {
$scope.activeClass = id;
console.log(id);
};
});
</script>
TO ANSWER THE QUESTION TO DO WITH NG-REPEAT
Can ng-repeat use multiple css classes for each different div
Apparently it can.
By using the scope id like this...
<div class="block-{{block.id}}">
and the css like this...
.block-1 {...
WORKING FIDDLE OF THIS HERE
EDIT (Based on asker's comment):
UPDATED FIDDLE
only one can be clicked at a time? Or if another is open/clicked the first opened is reset and closed
This simplifies your code by almost 2x.
Initial State:
None of the blocks are selected.
None of the blocks are covered.
Code:
$scope.setToInitialState = function() {
$scope.blocks.forEach(function(block) {
$scope.isSelected[block.id] = false;
$scope.isCovered[block.id] = false;
});
};
Touched State:
Toggle the selected state of the clicked block. (toggle between select and deselect).
If selected, then cover and deselect all the other blocks.
If deselected, then bring the app to the initial state.
Code:
$scope.selectBlock = function(id) {
$scope.isSelected[id] = !$scope.isSelected[id];
$scope.isCovered[id] = false;
if ($scope.isSelected[id]) {
$scope.blocks.forEach(function(block) {
if (block.id !== id) {
$scope.isCovered[block.id] = true;
$scope.isSelected[block.id] = false;
}
});
}
else {
$scope.setToInitialState();
}
};
The demo uses all the same set block sizes, but the actual use all div Blocks are different heights and widths. And each block is a different image
You should consider using ng-src.
I am assuming that you might be retreiving all this content from the DB. Then, if possible, you may place each of your image inside a div of fixed size, so that they all come out to be of the same size.
Plus the display is divided into 2 vertical half screen sections
That can be set right tweaking a little bit of css.
ORIGINAL ANSWER:
WORKING FIDDLE
Let's say your app has 2 states:
Initial state (untouched)
Touched state
Initially, you are in the Initial state:
No right divs are shown.
All the icons are 'plus' (no 'minus')
None of the blocks are covered.
Code:
$scope.setToInitialState = function() {
$scope.plusCount = 0;
$scope.blocks.forEach(function(block) {
$scope.isPlus[block.id] = true;
$scope.isShowDiv[block.id] = false;
$scope.isCoverBlock[block.id] = false;
$scope.plusCount += 1;
});
};
When you are in the Touched state:
All those blocks are covered, which have a 'plus' icon.
The right divs of only those blocks are shown, which are not covered.
Code:
// Run when user clicks on the 'plus' or 'minus' icon.
$scope.selectBlock = function(id) {
$scope.isPlus[id] = !$scope.isPlus[id]; // toggle between 'plus' and 'minus' icons
if ($scope.isPlus[id]) {
$scope.plusCount += 1;
}
else {
$scope.plusCount -= 1;
}
$scope.blocks.forEach(function(block) {
if ($scope.isPlus[block.id]) {
$scope.isCoverBlock[block.id] = true;
}
else {
$scope.isCoverBlock[block.id] = false;
}
$scope.isShowDiv[block.id] = !$scope.isCoverBlock[block.id];
});
};
So, basically when the user interacts with the view and actually clicks on the icon, then he/she goes to the touched state (the above code is run).
Only when all the icons are 'plus', then the user must be sent to the initial state:
if ($scope.plusCount === $scope.blocks.length) {
$scope.setToInitialState();
}
Changes in html:
Add ng-init="setToInitialState()" to the outermost div, so that we are in the initial state initially.
Code:
<div class="container" ng-app="plusMinusApp" ng-controller="plusMinusController" ng-init="setToInitialState()">
Use ng-repeat instead of copy-pasting code for each block:
Code:
<div class="row" ng-repeat="block in blocks">
<div class="col-sm-4 col-xs-6" data-ng-class="{ 'coverThisBlock': isCoverBlock[block.id]}">
<div class="divClass"
data-ng-class="{'active-selection': !isPlus[block.id]}">
{{block.id}}
</div>
<i data-ng-click="selectBlock(block.id)"
class="btn btn-primary text-center fa"
data-ng-class="{'fa-minus': !isPlus[block.id], 'fa-plus': isPlus[block.id]}"></i>
</div>
<div data-ng-show="isShowDiv[block.id]" class="col-xs-3 textdiv">Hello</div>
</div>
Hope it helps you!
UPDATE
Shoot, it's only one at a time. Well... Almost all of what I had written applies.
New Fiddle
So only difference is the JS. Instead of an array of selected blocks, we have a number that represents the selected block.
this.blocks = Array.apply(null, Array(10)).map(function (val, index) {return index;});
this.activeIndex = null;
this.isActive = function(index) {
return that.activeIndex === index;
};
this.hasSelected = function() {
return that.activeIndex !== null;
};
this.selectBlock = function(index) {
if (that.activeIndex === index) {
that.activeIndex = null;
} else {
that.activeIndex = index;
}
};
See, good JS code is easily maintainable, even when the requirements change (or when I find out they do).
(We actually can get by without these helper functions, but we use them for the sake of prettier code and maybe encapsulation.)
Original Answer
Fiddle here
I hope I've explained why we should do the stuff in the fiddle, and gave credible references/further reading material.
First things first
Don't use $scope. I like controller as syntax. Basically, in JS, you use this.prop and HTML, you use myCtrlAs.prop. Docs
This is a CSS problem. Take advantage of :not()
Two principles: KISS and DRY
So we have a problem of same copy-pasted code blocks. Like #avijit said, you should use the ng-repeat directive.
<div class="row" ng-repeat="block in plusMinus.blocks track by $index">
//This entire block will be repeated
</div>
For each row, we need to keep track of whether or not it is selected. Following the KISS principle, the only state we need to keep track of is this.
Otherwise, things get too complex.
//each element represents the state of a row
this.blocks = [0,0,0,0,0];
So this block array is used for the ng-repeat.
You ask, how will we keep track of what to turn dark and what is active?
The answer, is .... wait for it
You don't
Instead, we use functions located on the controller to get information about the single blocks variable. You could argue against my wording, but notice how I said "keep track"? Again, don't store duplicate state data. It becomes a nightmare, based off my prior experience.
(We actually can get by without these helper functions, but we use them for the sake of prettier code and maybe encapsulation.)
So the first function that I want to point out:
this.hasSelected = function() {
return that.blocks.indexOf(true) !== -1;
};
What is this for? You guessed it! To determine if we should "cover up" the rows that aren't selected.
Use CSS–it's fun useful!
So we conditionally apply the .has-selected class to a wrapper.
So that the "cover" only applies when it has an ancestor with .has-selected, we have
has-selected :not(.active) > .col-sm-4 {
width: 33%;
height: inherit;
background-color: rgba(00,00,00,0.8);
z-index: 9;
}
.has-selected :not(.active) .col-sm-4 {
#media (max-width:420px){
width: 80%;
}
}
back to ng-repeat
Oh, and by now, you should know that $index is used to access the index of the current HTML in ng-repeat. (Docs and SO Thread
<div class="label-index" data-ng-class="{'active-selection': !plusMinus.isActive($index)}">
{{$index}}
</div>
Concluding remarks
Read the docs
Don't try to do everything with one tool
If you think I didn't explain something particularly well, just ask.
Also, I think I screwed up some styling. I hope that you can fix it on your own.
the best solution you can find in the snippet below
angular.module('plusMinusApp', [])
.controller('plusMinusController', function ($scope) {
$scope.blocks = [1, 2, 3, 4, 5, 6];
var expandedBlock;
$scope.isAnyBlockExpanded = function() {
return expandedBlock !== undefined;
};
$scope.isBlockExpanded = function(blockId) {
return expandedBlock === blockId;
};
$scope.toggleBlockExpandingStatus = function(blockId) {
expandedBlock = $scope.isBlockExpanded(blockId) ? undefined : blockId;
};
});
body {
padding-top: 15px;
}
.divClass{
width: 35%;
height:50px;
text-align:center;
line-height:50px;
float: left;
left:15px;
margin-top: 25px;
margin-bottom: 25px;
margin-right: 15px;
color: orange;
font-size: 18px;
border:2px solid #000;
background-color: rgba(00,00,00,0.6);
cursor: pointer;
}
.textdiv {
border:2px solid green;
background-color: rgba(100,100,100,0.1);
height:50px;
text-align:center;
line-height:50px;
margin-top: 25px;
display: none;
}
.expanded-block .textdiv {
display: block;
}
i {
color:#000;
font-size: 40px;
line-height:50px;
}
.btn {
height: 50px;
margin-top: 25px;
margin-left: 15px;
}
.expanded-block .divClass {
background-color:rgba(100,100,100,0.1);
width: 35%;
font-size: 40px;
text-align: center;
border:2px solid green;
}
.collapsed-block .block-item {
width: 33%;
height: inherit;
background-color: rgba(00,00,00,0.8);
z-index: 9;
}
#media (max-width:420px){
.collapsed-block {
width: 80%;
}
}
<html>
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css"/>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"/>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
</head>
<body ng-app="plusMinusApp">
<div class="container" ng-controller="plusMinusController">
<div ng-repeat="block in blocks" class="row" ng-class="{'collapsed-block': isAnyBlockExpanded() && !isBlockExpanded(block), 'expanded-block': isBlockExpanded(block)}">
<div class="col-sm-4 col-xs-6 block-item">
<div class="divClass" ng-click="toggleBlockExpandingStatus(block)">{{block}}</div>
<i ng-click="toggleBlockExpandingStatus(block)" class="btn btn-primary text-center fa" ng-class="isBlockExpanded(block) ? 'fa-minus': 'fa-plus'"></i>
</div>
<div class="col-xs-4 textdiv">Hello</div>
</div>
</div>
</body>
</html>
Related
I've been trying to learn Angular but hit a small blocker. My markup has a list of DIVs which when clicked get toggled (a new class). This is a static list of options and I would like to avoid the ng-repeat directive.
<div ng-app="sampleApp" ng-controller="MainCtrl">
<div class="item" id="germany">Germany</div>
<div class="item" id="poland">Poland</div>
<div class="item" id="sweden">Sweden</div>
<div class="item" id="japan">Japan</div>
<button type="button" ng-click="selected = []">Reset</button>
</div>
I would like to bind it to an array of selections. When clicked, the item's id should be added to the array and when unselected, it should be removed. The existence of the item's id in the selection array defines whether it should have the "active" class.
Below is how I've done it in Angular but this means that I cannot use my static list of DIVs but use the ng-repeat with the JSON. I've toyed around with this and got it correctly bound to the selected array. Clicking the button correctly resets the active class showing that the binding works.
Can I still accomplish this without going down the JSON + ng-repeat way? An example pointing me to this would be nice. Thank you
Here's the CSS:
.item {
border-color: #ddd;
background-color: #fff;
color: #444;
position: relative;
z-index: 2;
display: block;
margin: -1px;
padding: 16px;
border-width: 1px;
border-style: solid;
font-size: 16px;
}
.item.active {
border-color: #ccc;
background-color: #d9d9d9;
}
Here's the JS:
var SampleApp;
(function (SampleApp) {
var app = angular.module('sampleApp', []);
app.controller('MainCtrl', function ($scope) {
$scope.selected = [];
$scope.clicked = function (member) {
var index = $scope.selected.indexOf(member.name);
if (index > -1) {
$scope.selected.splice(index, 1);
member.selected = false;
} else {
$scope.selected.push(member.name);
member.selected = true;
}
console.log($scope.selected);
}
$scope.items = [{
name: "item1"
}, {
name: "item2"
}, {
name: "item3"
}, {
name: "item4"
}, {
name: "item5"
}];
});
})(SampleApp || (SampleApp = {}));
Here's the markup:
<div ng-app="sampleApp" ng-controller="MainCtrl">
<div class="item" ng-repeat="item in items" ng-click="clicked(item)" ng-class="{ active: selected.indexOf(item.name) !== -1 }" id="item.name">{{ item.name }}</div>
<button type="button" ng-click="selected = []">Reset</button>
</div>
<div class="item" id="germany"
ng-click="toggle('germany')"
ng-class="{ active: selected.indexOf('germany') >= 0 }">Germany</div>
and repeat the same pattern for the other countries.
In the controller:
$scope.toggle = function(country) {
var index = $scope.selected.indexOf(country);
if (index >= 0) {
$scope.selected.splice(index, 1);
} else {
$scope.selected.push(country);
}
};
Not sure why you want to avoid ng-repeat though. Using it would avoid repeating the same code again and again in the view, and thus reduce the risk of introducing a bug.
Can we have multiple expression to add multiple ng-class ?
for eg.
<div ng-class="{class1: expressionData1, class2: expressionData2}"></div>
If yes can anyone put up the example to do so.
.
To apply different classes when different expressions evaluate to true:
<div ng-class="{class1 : expression1, class2 : expression2}">
Hello World!
</div>
To apply multiple classes when an expression holds true:
<!-- notice expression1 used twice -->
<div ng-class="{class1 : expression1, class2 : expression1}">
Hello World!
</div>
or quite simply:
<div ng-class="{'class1 class2' : expression1}">
Hello World!
</div>
Notice the single quotes surrounding css classes.
For the ternary operator notation:
<div ng-class="expression1? 'class1 class2' : 'class3 class4'">
An incredibly powerful alternative to other answers here:
ng-class="[ { key: resulting-class-expression }[ key-matching-expression ], .. ]"
Some examples:
1. Simply adds 'class1 class2 class3' to the div:
<div ng-class="[{true: 'class1'}[true], {true: 'class2 class3'}[true]]"></div>
2. Adds 'odd' or 'even' classes to div, depending on the $index:
<div ng-class="[{0:'even', 1:'odd'}[ $index % 2]]"></div>
3. Dynamically creates a class for each div based on $index
<div ng-class="[{true:'index'+$index}[true]]"></div>
If $index=5 this will result in:
<div class="index5"></div>
Here's a code sample you can run:
var app = angular.module('app', []);
app.controller('MyCtrl', function($scope){
$scope.items = 'abcdefg'.split('');
});
.odd { background-color: #eee; }
.even { background-color: #fff; }
.index5 {background-color: #0095ff; color: white; font-weight: bold; }
* { font-family: "Courier New", Courier, monospace; }
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js"></script>
<div ng-app="app" ng-controller="MyCtrl">
<div ng-repeat="item in items"
ng-class="[{true:'index'+$index}[true], {0:'even', 1:'odd'}[ $index % 2 ]]">
index {{$index}} = "{{item}}" ng-class="{{[{true:'index'+$index}[true], {0:'even', 1:'odd'}[ $index % 2 ]].join(' ')}}"
</div>
</div>
Yes you can have multiple expression to add multiple class in ng-class.
For example:
<div ng-class="{class1:Result.length==2,class2:Result.length==3}"> Dummy Data </div>
Using a $scope method on the controller, you can calculate what classes to output in the view. This is especially handy if you have a complex logic for calculating class names and it will reduce the amount of logic in your view by moving it to the controller:
app.controller('myController', function($scope) {
$scope.className = function() {
var className = 'initClass';
if (condition1())
className += ' class1';
if (condition2())
className += ' class2';
return className;
};
});
and in the view, simply:
<div ng-class="className()"></div>
Your example works for conditioned classes (the class name will show if the expressionDataX is true):
<div ng-class="{class1: expressionData1, class2: expressionData2}"></div>
You can also add multiple classes, supplied by the user of the element:
<div ng-class="[class1, class2]"></div>
Usage:
<div class="foo bar" class1="foo" class2="bar"></div>
Here is an example comparing multiple angular-ui-router states using the OR || operator:
<li ng-class="
{
warning:
$state.includes('out.pay.code.wrong')
|| $state.includes('out.pay.failed')
,
active:
$state.includes('out.pay')
}
">
It will give the li the classes warning and/or active, depening on whether the conditions are met.
Below active and activemenu are classes and itemCount and ShowCart is expression/boolean values.
ng-class="{'active' : itemCount, 'activemenu' : showCart}"
With multiple conditions
<div ng-class="{'class1' : con1 || can2, 'class2' : con3 && con4}">
Hello World!
</div>
Found another way thanks to Scotch.io
<div ng-repeat="step in steps" class="step-container step" ng-class="[step.status, step.type]" ng-click="onClick(step.type)">
This was my reference.PATH
Other way we can create a function to control "using multiple class"
CSS
<style>
.Red {
color: Red;
}
.Yellow {
color: Yellow;
}
.Blue {
color: Blue;
}
.Green {
color: Green;
}
.Gray {
color: Gray;
}
.b {
font-weight: bold;
}
</style>
Script
<script>
angular.module('myapp', [])
.controller('ExampleController', ['$scope', function ($scope) {
$scope.MyColors = ['It is Red', 'It is Yellow', 'It is Blue', 'It is Green', 'It is Gray'];
$scope.getClass = function (strValue) {
if (strValue == ("It is Red"))
return "Red";
else if (strValue == ("It is Yellow"))
return "Yellow";
else if (strValue == ("It is Blue"))
return "Blue";
else if (strValue == ("It is Green"))
return "Green";
else if (strValue == ("It is Gray"))
return "Gray";
}
}]);
</script>
Using it
<body ng-app="myapp" ng-controller="ExampleController">
<h2>AngularJS ng-class if example</h2>
<ul >
<li ng-repeat="icolor in MyColors" >
<p ng-class="[getClass(icolor), 'b']">{{icolor}}</p>
</li>
</ul>
You can refer to full code page at ng-class if example
I use this:
[ngClass]="[{
'basic':'mat-basic-button',
'raised':'mat-raised-button',
'stroked':'mat-stroked-button'
}[
button.style ?? 'raised'
]]"
I have problem with swapping the dragged/dropped elements.
DOM / Angular Structure:
angular.module('app', ['ngDragDrop'])
.controller('controller', function($scope) {
$scope.listItems = [
{name: "some name", title: "title"},
{name: "some name2", title: "title2"},
];
$scope.input = {};
$scope.draggableOprions = {
revert: 'invalid'
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<script src="https://code.angularjs.org/1.4.9/angular.js"></script>
<script src="https://cdn.jsdelivr.net/angular.dragdrop/1.07/angular-dragdrop.min.js"></script>
<div ng-app="app">
<div ng-controller="controller">
<div class="container">
<ul>
<li data-drag="true"
data-jqyoui-options="draggableOprions"
jqyoui-draggable="{animate:true}"
ng-model="item" ng-repeat="item in listItems track by $index">
{{item.title}}
</li>
</ul>
</div>
<div class="container"
data-drop="true"
data-jqyoui-options jqyoui-droppable
ng-model="input">
<input ng-model="input.name">
</div>
</div>
</div>
The problem:
While I drag and drop an list item 1 - the name property of the list item 1 comes to the input model. But no longer is available in list item 1. Actually the list item 1 value goes undefined || null after I drop it in the input. If I now try to drag-n-drop list item 2 item in the input - the values swapping (list item 1 = undefined || null ? and list item 2 = list item 1 value and input model equal to list item 2 value`. So everything shuffle.
What I need:
I need to drag and drop list items in the input, avoiding losing values in list items. Every time i drop list item in the input, I need it's value to bind to the input.
Out of the box
I can change the drag and drop library, or even use source code, but the library is the better choice. I accept almost every good working answer which didn't broke any standards of good code (I mean that I need code which will not broke the other code and has good structure).
I suggest to use ngDraggable, an Angular module with no depency from jQuery or jQuery-ui.
Here below is a working snippet or check my Codepen:
angular.module('app', ['ngDraggable'])
.controller('controller', function($scope) {
$scope.listItems = [{
name: "some name",
title: "title1"
}, {
name: "some name2",
title: "title2"
}, {
name: "some name3",
title: "title3"
}, ];
$scope.droppedObjects = [];
$scope.input = {};
// drag complete over drop area
$scope.onDragComplete = function(data, evt) {
console.log("drag success, data:", data);
var index = $scope.droppedObjects.indexOf(data);
if (index > -1) {
$scope.droppedObjects.splice(index, 1);
}
}
// drop complete over drop area
$scope.onDropComplete = function(data, evt) {
console.log("drop success, data:", data);
var index = $scope.droppedObjects.indexOf(data);
if (index == -1)
$scope.droppedObjects.push(data);
}
// drop complete over input box
$scope.onDropCompleteInput = function(data, evt) {
console.log("drop on input success, data:", data);
$scope.input = data;
}
// drop complete over items area (remove from dropped list)
$scope.onDropCompleteRemove = function(data, evt) {
console.log("drop success - remove, data:", data);
var index = $scope.droppedObjects.indexOf(data);
if (index != -1)
$scope.droppedObjects.splice(index);
}
// other draggable events handlers
var onDraggableEvent = function(evt, data) {
console.log("128", "onDraggableEvent", evt, data);
}
$scope.$on('draggable:start', onDraggableEvent);
//$scope.$on('draggable:move', onDraggableEvent);
$scope.$on('draggable:end', onDraggableEvent);
});
* {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
[ng-drag] {
-moz-user-select: -moz-none;
-khtml-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
.item {
width: 100px;
height: 60px;
background: rgba(255, 0, 0, 0.5);
color: white;
text-align: center;
padding-top: 5%;
display: inline-block;
margin: 0 10px;
cursor: move;
}
ul.draggable-objects:after {
display: block;
content: "";
clear: both;
}
.draggable-objects li {
float: left;
display: block;
width: 120px;
height: 100px;
}
[ng-drag].drag-over {
border: solid 1px red;
}
[ng-drag].dragging {
opacity: 0.5;
}
.drop-container {
background: rgba(0, 255, 0, 0.5);
text-align: center;
width: 600px;
height: 200px;
padding-top: 90px;
display: block;
margin: 20px auto;
position: relative;
}
.drop-input {
width: 200px;
height: 40px;
}
.drag-enter {
border: solid 5px red;
}
.drop-container span.title {
display: block;
position: absolute;
top: 10%;
left: 50%;
width: 200px;
height: 20px;
margin-left: -100px;
margin-top: -10px;
}
.drop-container div {
position: relative;
z-index: 2;
}
<script src="//code.angularjs.org/1.4.8/angular.js"></script>
<script src="//rawgit.com/fatlinesofcode/ngDraggable/master/ngDraggable.js"></script>
<body ng-app="app">
<div ng-controller="controller">
<div class="row">
<h1>ngDraggable Example</h1>
</div>
<div ng-drop="true" ng-drop-success="onDropCompleteRemove($data,$event)">
<ul class="draggable-objects">
<li ng-repeat="obj in listItems">
<div ng-drag="true" ng-drag-data="obj" data-allow-transform="true" class="item"> {{obj.title}} </div>
</li>
</ul>
</div>
<hr/>
<div ng-drop="true" ng-drop-success="onDropComplete($data,$event)" class="drop-container">
<span class="title">Drop area</span>
<div ng-repeat="obj in droppedObjects" ng-drag="true" ng-drag-data="obj" ng-drag-success="onDragComplete($data,$event)" class="item">
{{obj.title}}
</div>
</div>
<hr/>
<div class="container">
Drop on input:
<input ng-model="input.name" class="drop-input" ng-drop="true" ng-drop-success="onDropCompleteInput($data,$event)">
</div>
<br>
<hr/>
<pre>listItems = {{listItems|json}}</pre>
<pre>input = {{input|json}}</pre>
<pre>droppedObjects = {{droppedObjects|json}}</pre>
</div>
</body>
<!DOCTYPE html>
<html>
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<script src="https://code.angularjs.org/1.4.9/angular.js"></script>
<script src="https://cdn.jsdelivr.net/angular.dragdrop/1.07/angular-dragdrop.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="MyController">
<div id="products">
<h1 class="ui-widget-header">Products</h1>
<div id="catalog">
<div>
<ul>
<li ng-repeat='item in list1' ng-show="item.title" data-drag="true" data-jqyoui-options="{revert: 'invalid', helper: 'clone'}" ng-model="list1" jqyoui-draggable="{index: {{$index}}, animate: true, placeholder: 'keep'}">{{item.title}}</li>
</ul>
</div>
</div>
</div>
<div id="cart">
<h1 class="ui-widget-header">Shopping Cart</h1>
<div class="ui-widget-content">
<ol data-drop="true" ng-model='list4' jqyoui-droppable="{multiple:true}">
<li ng-repeat="item in list4 track by $index" ng-show="item.title" data-drag="true" data-jqyoui-options="{revert: 'invalid', helper: 'clone'}" ng-model="list4" jqyoui-draggable="{index: {{$index}},animate:true}">{{item.title}}</li>
<li class="placeholder" ng-hide="hideMe()">Add your items here</li>
</ol>
</div>
</div>
</div>
<script>
var app = angular.module('myApp', ['ngDragDrop']);
app.controller('MyController', function ($scope,$http,$sce)
{
$scope.list1 = [{'title': 'Lolcat Shirt'},{'title': 'Cheezeburger Shirt'},{'title': 'Buckit Shirt'}];
$scope.list4 = [];
$scope.hideMe = function()
{
return $scope.list4.length > 0;
}
});
</script>
</body>
</html>
You have to use helper: 'clone' in data-jqyoui-options
<div class="col item item-text-wrap" ng-repeat="course in appplication.course">
<div>
<br>
<h3>{{course.courseName}} </h3>
</div>
<div>
<br>
<h3 align="right" ng-class="Application">{{course.stageName}} </h3>
</div>
</div>
I have list of stage name. Stage start with three category Application, Interview, Offer using the prefix. I want to show different the stages with colors
ex:Application-Waiting for confirmation.
Try to give some suggestion
By using ng-class directive you can add class based on stage of your object, for example
<h3 align="right" ng-class="{ 'style-1': course.stageName === 'Application', 'style-2': course.stageName === 'Interview', 'style-3': course.stageName }">{{course.stageName}} </h3>
Hope this helps
Finally i did it another way Thanks for your help
$scope.getClass = function (strValue) {
if (strValue.match("Application"))
return "Blue";
else if (strValue.match("Interview"))
return "Red";
else if (strValue.match("Offer"))
return "Green";
}
.Red {
color: Red;
}
.Yellow {
color: Yellow;
}
.Blue {
color: Blue;
}
.Green {
color: Green;
}
<h3 align="right" ng-class="[getClass(course.stageName)]">{{course.stageName}} </h3>
i created an shopping cart using using angularJS drag and drop. when i drag the item from catalog to cart and drop it, it goes successfully in the cart,
the problem is when i drag and drop same item in the cart, it again accept the product and add it in the cart.
how to remove this cloning?
to access the complete code please download it from here click to download the .rar file
or here is the code of main index file
the bold code below is attachment of CSS and script you can download the same version from the .rar file above or by googling the script name.
`
<meta charset="utf-8" />
<title>Drag & Drop</title>
**<script src="js/jquery.min.js"></script>**
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.min.js">
<script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.7/angular.min.js">
**<script src="angular_drag_drop/angular-dragdrop.js"></script>
<link href="assets/css/jquery-ui.css" rel="stylesheet">**
<style>
.thumbnail { height: 280px !important; }
.btn-droppable { width: 180px; height: 30px; padding-left: 4px; }
.btn-draggable { width: 160px; }
.emage { height: 215px; }
h1 { padding: .2em; margin: 0; }
#products { float:left; width: 500px; margin-right: 2em; }
#cart { width: 200px; float: left; margin-top: 1em; }
#cart ol { margin: 0; padding: 1em 0 1em 3em; }
</style>
<script>
$(function() {
$("#catalog").accordion();
});
var App = angular.module('drag-and-drop', ['ngDragDrop']);
App.controller('oneCtrl', function($scope, $timeout) {
$scope.list1 = [{'title': 'Lolcat Shirt'},{'title': 'Cheezeburger Shirt'},{'title': 'Buckit Shirt'}];
$scope.list4 = [];
$scope.hideMe = function() {
$scope.list1 = [{'title': 'Lolcat Shirt'},{'title': 'Cheezeburger Shirt'},{'title': 'Buckit Shirt'}];
return $scope.list4.length > 0;
}
});
</script>
<body ng-controller="oneCtrl">
<div id="products">
<h1 class="ui-widget-header">Products</h1>
<div id="catalog">
<h2>T-Shirts</h2>
<div data-drop="true" ng-model='list1' jqyoui-droppable="{multiple:true}">
<ul>
<li ng-repeat='item in list1' ng-show="item.title" data-drag="true" data-jqyoui-options="{revert: 'invalid', helper: 'clone'}" ng-model="list1" jqyoui-draggable="{index: {{$index}}, animate: true, placeholder: 'keep'}">{{item.title}} {{list1.length}}
</li>
</ul>
</div>
</div>
</div>
<div id="cart">
<h1 class="ui-widget-header">Shopping Cart</h1>
<div class="ui-widget-content">
<ol data-drop="true" ng-model='list4' jqyoui-droppable="{multiple:true}">
<li ng-repeat="item in list4" ng-show="item.title" data-drag="true" data-jqyoui-options="{revert: 'invalid', helper: 'clone'}" ng-model="list4" jqyoui-draggable="{index: {{$index}},animate:true}">
{{item.title}}
</li>
<li class="placeholder" ng-hide="hideMe()">
Add your items here
</li>
</ol>
</div>
</div>
</body>
</html>`
If your shopping cart is the only use-case for drag and drop, then you could tackle this by disabling dragging once the object is in the cart. You can do this by binding the draggable class to an expression which checks whether the object is in the cart.
<div ng-class="{draggable: cart.indexOf(object) != -1}"></div>
Here is a basic fiddle to show you what I mean - note, this doesn't include any dragging or dropping, but is just to highlight the class binding, and get you started.
Fiddle