Toggle other elements inside ng-repeat on ng-click - javascript

I'm trying to apply class by using ng-class and ng-click and this working fine for selected element, but how can I toggle this class in the other elements?
Improved description:
Current behaviour:
Click on elelement, class applied.
Clik on the other element, this element also class applied.
Desired behaviour:
Click on element, class applied.
Other element - class removed.
<div ng-repeat="element in ngModel | orderBy:'Field_Order'" class='elementForm' ng-hide="element.IsDeleted">
<div layout="row" style="width:100%" class="container" ng-mouseover="hovering=true" ng-mouseleave="hovering=false" flex ng-click="selected = !selected">
<div class="hover-space" ng-class="{'hoveredFormElement':hovering, 'selected':selected}" flex="2" ></div>
....
</div>
</div>

The problem is, selected is inside an isolated scope which is not shared by other items.
One easy solution using index is
var app = angular.module('my-app', [], function() {})
app.controller('AppController', function($scope) {
$scope.selected = -1;
$scope.ngModel = [{
i: 1
}, {
i: 2
}, {
i: 3
}, {
i: 4
}];
})
.hoveredFormElement {
color: green;
}
.selected {
background-color: grey;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="my-app">
<div ng-controller="AppController">
{{selected}}
<div ng-repeat="element in ngModel | orderBy:'Field_Order'" class='elementForm' ng-hide="element.IsDeleted">
<div layout="row" style="width:100%" class="container" ng-mouseover="hovering=true" ng-mouseleave="hovering=false" flex ng-click="$parent.selected = $parent.selected == $index ? -1 : $index">
<div class="hover-space" ng-class="{'hoveredFormElement':hovering, 'selected':selected == $index}" flex="2">{{element}}</div>
</div>
</div>
</div>
</div>

Maintain flag in controller scope and use it over ng-class
$scope.selected = { index: undefined };
Markup
<div ng-repeat="element in ngModel | orderBy:'Field_Order'" class='elementForm' ng-hide="element.IsDeleted">
<div layout="row" style="width:100%" class="container"
ng-mouseover="hovering = true"
ng-mouseleave="hovering = false"
flex ng-click="selected.index = !selected">
<div class="hover-space"
ng-class="{'hoveredFormElement':hovering , 'selected':selected.index }" flex="2" ></div>
....
</div>
</div>

Related

How to display last part of string containing multiple dots inside ng repeat?

Below are my list of items :
$scope.items = [
{id=1,name:'code.lmn.1234.Lodashjs'},
{id=2,name:'xyz.Suv.Angularjs'},
{id=3,name:'www.kius.reactjs'}
]
Now I want to display last part of name so expected output is like below :
Lodashjs
Angularjs
reactjs
I don't want to create filter for this as that will impact performance. So is there any way to do this without filter?
var app = angular.module("myApp", []);
app.controller("myController", function ($scope) {
$scope.items = [
{id:1,name:'code.lmn.1234.Lodashjs'},
{id:2,name:'xyz.Suv.Angularjs'},
{id:3,name:'www.kius.react.js'}
]
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myController">
<div ng-repeat="item in items">
<span >{{item.name}}</span>
</div>
</div>
Try this:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myController">
<div ng-repeat="item in items">
<span >{{ item.name.split('.').reverse()[0] }}</span>
</div>
</div>
Yes it's so simple.
Just copy paste the below code:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myController">
<div ng-repeat="item in items">
<span >{{(item.name).substr((item.name).lastIndexOf('.') + 1, (item.name).length)}}</span>
</div>
</div>
You can also use directive that will manipulate the name and return string after last "." dot.
<div ng-repeat="item in items" my-directive my-directive-fn="item.name">
app.directive("myDirective", function(){
return {
scope: {
"myDirectiveFn": "="
},
link: function(scope){
scope.myDirectiveFn.match(/([^.]+)$/);
}
}
});

ngRepeat on custom directive tags

I have a list of custom directives which are different widgets on my dashboard. The directives are defined as follows:
angular.module('core').directive('graphCardWidget', function() {
return {
restrict: 'E',
replace: true,
scope: {
target: '=target'
},
templateUrl: 'modules/core/widgets/graph-card.client.widget.html'
};
});
angular.module('core').directive('pieChartWidget', function() {
return {
restrict: 'E',
replace: true,
scope: {
target: '=target'
},
templateUrl: 'modules/core/widgets/pie-chart.client.widget.html'
};
});
In my controller, I have a list of widgets to be displayed. The list is as follows:
$scope.dashboardWidgets = [
{
directive : 'graph-card-widget',
target : 'widgets.dashboards.activeDevicesCard'
},
{
directive : 'graph-card-widget',
target : 'widgets.dashboards.activeSessionsCard'
},
{
directive : 'pie-chart-widget',
target : 'widgets.dashboards.devices'
},
{
directive : 'pie-chart-widget',
target : 'widgets.dashboards.sessions'
}
];
Now in my view, I use ng-repeat to iterate this array and display the items. Here is the code of my view:
<div layout="row" layout-wrap layout-align="center" layout-xs="column" ng-drop="true">
<div ng-repeat='widget in dashboardWidgets'>
<{{widget.directive}} ng-drag="true" flex='45' target='{{widget.target}}'>
</{{widget.directive}}>
<span flex='5'></span>
</div>
</div>
But the browser renders this as text. Here is what I get in my DOM:
<div layout="row" layout-wrap="" layout-align="center" layout-xs="column" ng-drop="true" class="layout-wrap layout-xs-column layout-align-center-stretch layout-row">
<div ng-repeat="widget in dashboardWidgets" class="ng-binding ng-scope">
“<graph-card-widget ng-drag="true" flex='45' target='widgets.dashboards.activeDevicesCard’>”
<span flex="5" class="flex-5"></span>
</div>
<div ng-repeat="widget in dashboardWidgets" class="ng-binding ng-scope">
“<graph-card-widget ng-drag="true" flex='45' target='widgets.dashboards.activeSessionsCard’>”
<span flex="5" class="flex-5"></span>
</div>
<div ng-repeat="widget in dashboardWidgets" class="ng-binding ng-scope">
“<pie-chart-widget ng-drag="true" flex='45' target='widgets.dashboards.devices’>”
<span flex="5" class="flex-5"></span>
</div>
<div ng-repeat="widget in dashboardWidgets" class="ng-binding ng-scope">
“<pie-chart-widget ng-drag="true" flex='45' target='widgets.dashboards.sessions’>”
<span flex="5" class="flex-5"></span>
</div>
</div>
So what can I do to make the directive rendered as a tag and not plain text?
I saw a couple of questions similar to these such as, question 1, question 2 but they all are adding the dynamic directives in standard HTML tags. What I need is a dynamic tag.
Update:
After following #cnexans answer, I got it working partially. The widget is drawn but the target attribute is not evaluated which leads to a blank widget.
Here's the plunkr with the issue: https://plnkr.co/edit/BGN6C4LAHguWthU4fGy0?p=preview
You can use ng-if inside ng-for in order to switch the name "directive" within each iteration to show one or another directive.
<div ng-repeat="data in dashboardWidgets">
<div ng-if="data.directive == 'graph-card-widget'">
<graph-card-widget ng-drag="true" flex='45' target=data.target>
</graph-card-widget>
<span flex='5'></span>
</div>
<div ng-if="data.directive == 'pie-chart-widget'">
<pie-chart-widget ng-drag="true" flex='45' target=data.target>
</pie-chart-widget>
<span flex='5'></span>
</div>
</div>
You can create a container directive to take care of this logic too, so you can share this functionality to other pages if needed.
<widgets-container widgets-list=data></widgets-container>
Working example: https://codepen.io/anon/pen/jBzEpe?editors=1010#0
Edit:
Checking the example you gave, you need to pass a Widget object to the directive, and you are passing a string. Here is a working example forked from the plunkr given
https://plnkr.co/edit/3Oxxmp?p=preview
It has a function namespaceToObject which transform the string into the desired object from $scope.
don't know whether this will match your requirement. but i did some modification to your array.
I did this using ng-bind-html and when your are binding custom element like directive you need to compile it again. for that i create(actually borrow) this directive.
.directive('compileTemplate', function($compile, $parse){
return {
link: function(scope, element, attr){
var parsed = $parse(attr.ngBindHtml);
function getStringValue() { return (parsed(scope) || '').toString(); }
//Recompile if the template changes
scope.$watch(getStringValue, function() {
$compile(element, null, -9999)(scope); //The -9999 makes it skip directives so that we do not recompile ourselves
});
}
}
});
I changed the ng-repeat like this
<div ng-repeat='widget in dashboardWidgets' compile-template ng-bind-html="trust(widget.directive)">
<span flex='5'></span>
</div>
trust function will return trusted html
$scope.trust = function(someHTML){
return $sce.trustAsHtml(someHTML);
}
modify the array like this
$scope.dashboardWidgets = [
{
directive : '<graph-card-widget ng-drag="true" flex="45" target="widget.target"></ graph-card-widget>',
target : 'widgets.dashboards.activeDevicesCard'
},
{
directive : '<graph-card-widget ng-drag="true" flex="45" target="widget.target"></ graph-card-widget>',
target : 'widgets.dashboards.activeSessionsCard'
},
{
directive : '<pie-chart-widget ng-drag="true" flex="45" target="widget.target"></ pie-chart-widget>',
target : 'widgets.dashboards.devices'
},
{
directive : '<pie-chart-widget ng-drag="true" flex="45" target="widget.target"></ pie-chart-widget>',
target : 'widgets.dashboards.sessions'
}
];
Demo

AngularJS, problems with $index variable in ng-repeat

I'm trying to fill out a 2 column table in AngularJS. I'm using ng-repeat directive to fill out the table, but it's not's working the way I'm planning. My $scope.items is: [Coors, Jameson, Bacardi, Corona]
I want the table to look like this:
| Coors (0) | Jameson (1) |
| Bacardi (2) | Corona (3) |
however, it looks like this:
| Coors (0) | Coors (1) |
| Bacardi (2) | Bacardi (3) |
I'm confused as to why the [$index+1] directive in the my script is only working in the actual text portion of the script (in parenthesis), while the <item-card> div does not seem to properly displaying items[$index+1], and instead is displaying items[$index]. Here is my script:
<div class=row ng-repeat="item in items" ng-if="$index %2 ==0">
<div class="col col-50" ng-if="$index < items.length">
<item-card item="{{item[$index]}}"></item-card>
({{$index}})
</div>
<div class="col col-50" ng-if="$index +1 < items.length">
<item-card item="{{items[$index+1]}}"></item-card>
({{$index+1}})
</div>
</div>
Does anyone know why this might not be working as intended?
Edit: Including is itemcard.html.
<div class = "card" >
<img id = "cardImage" ng-src= "data:image/jpeg;base64,{{item.image}}" width = "100%"/>
{{item.cartQuantity}}
<cardHeader>{{item.itemName}}</cardHeader><br>
<cardHeader ng-if= "item.paksize >1">{{item.paksize}} pack</cardHeader>
<button class="button" ng-click="addToCart(item)">+</button>
<button class="button" ng-click="decrementCart(item)">-</button>
</div>
What you're trying to do seems a little odd to me. Rather than trying to split the array of items into even chunks of 2 columns with Directive Fu, might you consider using lodash.chunk?
<script>
angular.module('example', [ ])
.run(function($scope){
$scope.items = _.chunk([ /* . . . */ ], 2);
});
</script>
<div ng-app="example">
<div class=row ng-repeat="chunk in items">
<div class="col col-50">
<item-card item="{{chunk[0]}}"></item-card>
({{$index}})
</div>
<div class="col col-50">
<item-card item="{{chunk[1]}}"></item-card>
({{$index+1}})
</div>
</div>
</div>
If you wanted to be really Angular about it (a pun!), you could register lodash.chunk as a custom Filter instead:
<script>
angular.module('example', [ ])
.run(function($scope){
$scope.items = [ /* . . . */ ];
})
.filter('chunk', function(){
return _.chunk;
});
</script>
<div ng-app="example">
<div class=row ng-repeat="chunk in items | chunk:2">
<div class="col col-50">
<item-card item="{{chunk[0]}}"></item-card>
({{$index}})
</div>
<div class="col col-50">
<item-card item="{{chunk[1]}}"></item-card>
({{$index+1}})
</div>
</div>
</div>

How to make template without ng-repeat and pass data to $scope with angular-drag-and-drop-lists?

I want to use angular-drag-and-drop-lists with my own grid template to WYSIWYG editor. How to build my own HTML template without ng-repeat so it will be able to drop items in any predefined column and store information in $scope in which column item has been dropped?
I want to store dropped items in columns array:
.controller('DragDropGrid', ['$scope',
function($scope) {
$scope.layouts = {
selected: null,
templates: [
{name: "Plugin", type: "item", id: 2},
],
columns: [],
oldaproachcolumns: [
"A": [
],
"B": [
]
}
]
};
}
]);
This is currently not a working template. On drop it throws the error "undefined is not an object (evaluating 'targetArray.splice')":
<div ng-include="'grid.html'"></div>
<script type="text/ng-template" id="grid.html">
<div class="col-md-12 dropzone box box-yellow">
<div class="row template-grid" dnd-list="list">
<div class="col-xs-12 col-md-8" ng-repeat="item in list" dnd-draggable="item" ng-include="item.type + '.html'">Header</div>
<div class="col-xs-6 col-md-4">News</div>
</div>
</div>
</script>
<script type="text/ng-template" id="item.html">
<div class="item">Plugin {{item.id}}</div>
</script>
This is the standard working aproach:
<div class="row">
<div ng-repeat="(zone, list) in layouts.oldaproachcolumns" class="col-md-3">
<div class="dropzone box box-yellow">
<h3>{{zone}}</h3>
<div ng-include="'list.html'"></div>
</div>
</div>
</div>
<script type="text/ng-template" id="list.html">
<ul dnd-list="list">
<li ng-repeat="item in list" dnd-draggable="item" dnd-effect-allowed="move" dnd-moved="list.splice($index, 1)" dnd-selected="layouts.selected = item" ng-class="{selected: layouts.selected === item}" ng-include="item.type + '.html'">
</li>
</ul>
</script>
Based on this example: demo
How to build my own HTML template without ng-repeat
Use $templateCache and a for loop as an alternative:
var app = angular.module('foo', []);
function bop(model, data)
{
return data ? model : 'foo';
}
function baz()
{
return bop;
}
function foo($templateCache)
{
var i = 0, len = 5, bar = "";
for (i; i < len; i++)
{
bar = bar.concat("<li>",i,"<p ng-include=\u0022'foo'\u0022></p></li>");
}
$templateCache.put('listContent', bar);
}
angular.module('foo').filter('baz', baz);
app.run(foo);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="foo">
<script type="text/ng-template" id="foo">
<span>hi</span>
</script>
<input ng-model="dude">
<ul ng-include="'listContent' | baz:dude"></ul>
</div>
References
AngularJS: API: $templateCache
AngularJS: Is there a better way to achieve this than the one specified?

angular.js two directives, second one does not execute

I have two directives defined in an angular.js module. The HTML element that is declared first executes its directive, but the second HTML element that uses the other directive does not execute it.
Given this HTML:
<div ng-app="myApp">
<div ng-controller="PlayersCtrl">
<div primary text="{{primaryText}}"/>
<div secondary text="{{secondaryText}}"/>
</div>
</div>
and this angular.js code:
var myApp = angular.module('myApp', []);
function PlayersCtrl($scope) {
$scope.primaryText = "Players";
$scope.secondaryText = "the best player list";
}
myApp.directive('primary', function(){
return {
scope: {
text: '#'
},
template: '<h1>{{text}}</h1>',
link: function(scope, element, attrs){
console.log('primary directive');
}
};
});
myApp.directive('secondary', function(){
return {
scope: {
text: '#'
},
template: '<h3>{{text}}</h3>',
link: function(scope, element, attrs){
console.log('secondary directive');
}
};
});
The resulting HTML is only the "primary" directive, and the "secondary" directive does not render:
<div ng-app="myApp" class="ng-scope">
<div ng-controller="PlayersCtrl" class="ng-scope">
<div primary="" text="Players" class="ng-isolate-scope ng-scope">
<h1 class="ng-binding">Players</h1>
</div>
</div>
</div>
The console output verifies this as well, as only the "primary directive" text is output.
Then if I switch the order of the primary and secondary elements, the secondary directive is executed and the primary directive is not:
<!-- reversed elements -->
<div secondary text="{{secondaryText}}"/>
<div primary text="{{primaryText}}"/>
<!-- renders this HTML (secondary, no primary) -->
<div ng-app="myApp" class="ng-scope">
<div ng-controller="PlayersCtrl" class="ng-scope">
<div secondary="" text="the best player list" class="ng-isolate-scope ng-scope">
<h3 class="ng-binding">the best player list</h3>
</div>
</div>
</div>
Why is this? What am I doing wrong?
div's are not void elements and require a closing tag.
<div ng-app="myApp">
<div ng-controller="PlayersCtrl">
<div primary text="{{primaryText}}"></div>
<div secondary text="{{secondaryText}}"></div>
</div>
</div>
Example

Categories