Identify the ng-grid on which the sort event was fired - angularjs

I have two ng-grids in the same view/partial and need to identify on which ng-grid was the sort event fired. How do I do that? I am using the ngGridEventSorted event.

You can access the scope on which the event was fired with event.targetScope
$scope.$on('ngGridEventSorted', function(event, args) {
var targetScope = event.targetScope;
// inspect targetScope's properties to differentiate between the two grids
});
Another way would be to create two wrapping div's around the grids, each with their own controller that handles the event.
<div ng-controller="controllerOne">
<ng-grid ...>
</div>
<div ng-controller="controllerTwo">
<ng-grid ...>
</div>

That what you need:
$scope.$on('ngGridEventSorted', function(event,data) {
if ($scope.gridOptions1.gridId==event.targetScope.gridId){
...
}
if ($scope.gridOptions2.gridId==event.targetScope.gridId){
...
}
});
And no need to define gridId.

Related

How to change the class using click and hovering

View:
<a ng-repeat="control in controls | filter:name" ng-href="#{{control.id}}" ng-click="restart(control.name)" ng-class="{active: control.name == selected}">{{control.name}}
controller- app.js
$scope.restart = function (controlName) {
$scope.selected = controlName;
}
Here i have added the active class while clicking.how can i add the hover class using mouseover and remover the class using mouse-leave.
As suggested above, use ng-class with ng-mouseover and ng-mouseleave:
<div ng-class="class" ng-click="class='active'" ng-mouseover="class='hovering'" ng-mouseleave="class=''"></div>
This way, you'll have 3 event listener to a single variable (in a mutually exclusive way).
If you want to have both at the same time, use an array this way:
<div ng-class="[classClick, classHover]" ng-click="classClick='active'" ng-mouseover="classhover='hovering'" ng-mouseleave="classHover=''"></div>
Use ng-class, ng-mouseover anf ng-mouseleave:
<div ng-class='{"classtoadd": add, "active": click, "hover": hover}' ng-mouseover="add=true;" ng-mouseleave="add=false" ng-click="click = true" ng-mouseover="hover = true"></div>
Thanks for your response. i want to add 'active' class while clicking.and i want add the add the class' hover' while 'hovering'

How to find out if something in a given div has focus?

Using angularjs, I'm showing a 2-level list like this
- first main item
- first subitem of the first main item
- second subitem of the first main item
- AN EMPTY ITEM AS PLACEHOLDER TO ENTER THE NEXT SUBITEM
- second main item
- first subitem of the second main item
- second subitem of the second main item
- AN EMPTY ITEM AS PLACEHOLDER TO ENTER THE NEXT SUBITEM
In order to save place, I'd like to show the PLACEHOLDER only if anything in the corresponding div has focus, so that there's only one such placeholder. I know that there's ngFocus, but I'd prefer something simpler than creating tons of event handlers. Maybe something like this :
<div ng-focus-model="mainItem.hasFocus" ng-repeat="mainItem in list">
... main item line
... all subitems
</div>
A unidirectional binding would be sufficient as I don't need to set the focus.
The problem here is the following; we want to avoid adding event listener to each and every child, but add it only to the parent. The parent will be responsible for taking the appropriate action. The general solution to this, is to use even propagation (delegation). We attach only one listener to the parent, when an event occurs on the child (focus on input element in this example), it will bubble up to the parent and the parent will execute the listener.
Here's the directive:
app.directive('ngFocusModel', function () {
return function (scope, element) {
var focusListener = function () {
scope.hasFocus = true;
scope.$digest();
};
var blurListener = function () {
scope.hasFocus = false;
scope.$digest();
};
element[0].addEventListener('focus', focusListener, true);
element[0].addEventListener('blur', blurListener, true);
};
});
The directive listens for events and accordingly sets the value on scope, so we can make conditional changes.
There are several things to notice here.
focus and blur events don't "bubble", we need to use "event capturing" to catch them. That's why element.on('focus/blur') is not used (it doesn't allow for capture, afaik) but an addEventListener method. This method allows us to specify if the listener will be executed on "event bubbling" or "event capturing" by setting the third argument to false or true accordingly.
We could have used focusin and focusout events which "bubble", unfortunatelly these aren't supported in Firefox (focusin and focusout).
Here's a plunker with the implementation.
Update:
It occurred to me that this can be done with pure CSS using the :focus pseudo-class, the only downside is that the placeholder needs to be in proper position (sibling) relative to the input elements. See codepen.
Unfortunately the only rock solid way to do what you want is to respond to the focus\blur events on the inputs...that's the only way to get notified.
You could put a hidden input as the first element in each div and put the NgFocus attribute on it but that only works if a user tabs into it.
DEMO
I created a small directive that can be used for what you need:
app.directive('childFocus', function($window){
var registered = [];
// observing focus events in single place
$window.addEventListener('focus', function(event){
registered.forEach(function(element){
if(element.contains(event.target)){
// if element with focus is a descendant of the
// element with our directive then action is triggered
element._scope.$apply(element._scope.action);
}
});
}, true)
return {
scope : {
action : '&childFocus' // you can pass whatever expression here
},
link : function(scope, element){
// keep track ref to scope object
element[0]._scope = scope;
// (probably better would be to register
// scope with attached element)
registered.push(element[0]);
scope.$on('destroy', function(){
registered.splice(registered.indexOf(element[0]),1);
});
}
}
});
You could use the focus event of the '.parent *' selector to capture any focus events, then loop through each of the parent DIVs and use the :focus JQuery selector to check for child elements with focus, then add a class to the parent DIV and use that class to show/hide the placholder (see this jsfiddle):
$(function(){
$('.parent *').focus(function(){
$('.selected').removeClass('selected');
$('.parent').each(function(index, el){
(function($el){
setTimeout(function(){
console.log($el.attr('id'));
if($el.find(':focus').length){
$el.addClass('selected');
}
});
})($(el));
});
});
});
.parent{
padding:1rem;
margin:1rem;
border:solid 1px green;
}
.selected{
border:solid 1px red;
}
.parent .placeholder{
display:none;
}
.parent.selected .placeholder{
display:block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='parent' id='div1'>
<input type="text" />
<div class='placeholder'>Placeholder</div>
</div>
<div class='parent' id='div2'>
<input type="text" />
<div class='placeholder'>Placeholder</div>
</div>
<div class='parent' id='div3'>
<input type="text" />
<div class='placeholder'>Placeholder</div>
</div>
<div class='parent' id='div4'>
<input type="text" />
<div class='placeholder'>Placeholder</div>
</div>

angular-ui-tree: dropped location + catch dropped event in directive

I'm using angular-ui-tree for building a tree of items in my app.
I'm using its drag & drop feature and I need to know when & where (on what element) the dropping occurs.
For example, I drag item1, and drop it on a panel. I want the panel to display the item name. (each item has a name property). the panel is just a simple div with text inside.
I saw in the documentations that I can access the "dropped" event in my controller. But I don't understand how to change the panel content according to the dragged & dropped item.
As in documentations $callbacks (type: Object)
$callbacks is a very important property for angular-ui-tree. When some
special events trigger, the functions in $callbacks are called. The
callbacks can be passed through the directive.
you define the events in a treeOptions collection
myAppModule.controller('MyController', function($scope) {
// here you define the events in a treeOptions collection
$scope.treeOptions = {
accept: function(sourceNodeScope, destNodesScope, destIndex) {
return true;
},
dropped: function(e) {
console.log (e.source.nodeScope.$modelValue);
}
};
});
then in your tree div add callbacks="treeOptions" which you defined above in the controller
<div ui-tree callbacks="treeOptions">
<ol ui-tree-nodes ng-model="nodes">
<li ng-repeat="node in nodes" ui-tree-node>{{node.title}}</li>
</ol>
</div>
then you can access the old parent from here
e.source.nodeScope.$parentNodeScope.$modelValue
and you can access the new parent from here
e.dest.nodesScope.$parent.$modelValue
Hey guys i just found it !
$scope.treeOptions = {
dropped: function (event) {
//To catch the event after dragged
//Value of model which is moving
event.source.nodeScope.$modelValue;
//Source Parent from where we are moving model
event.source.nodeScope.$parentNodeScope.$modelValue;
//Destination Parent to where we are moving model
//Edit: Use "nodesScope" instead of "nodeScope" for dest object
event.dest.nodesScope.$nodeScope.$modelValue;
}};
Hope it works for you too:)
You access the "dropped" item like this.
$scope.elOptions = {
dropped: function(e) {
console.log (e.source.nodeScope.$modelValue);
}
};
Addional information which might be useful can be found on this issue of the project : https://github.com/angular-ui-tree/angular-ui-tree/issues/272
For example in my case, I was dragging from one tree to another one, and in this case, the dropped function must be overriden in the SOURCE tree options (and not the DESTINATION one like I initially thought).
The discussion in the related issue helped me a lot to find this out.

How to expand/collapse all rows in Angular

I have successfully created a function to toggle the individual rows of my ng-table to open and close using:
TestCase.prototype.toggle = function() {
this.showMe = !this.showMe;
}
and
<tr ng-repeat="row in $data">
<td align="left">
<p ng-click="row.toggle();">{{row.description}}</p>
<div ng-show="row.showMe">
See the plunkr for more code, note the expand/collapse buttons are in the "menu".
However, I can't figure out a way to now toggle ALL of the rows on and off. I want to be able to somehow run a for loop over the rows and then call toggle if needed, however my attempts at doing so have failed. See them below:
TestCase.prototype.expandAllAttemptOne = function() {
for (var row in this) {
if (!row.showMe)
row.showMe = !row.showMe;
}
}
function expandAllAttemptOneTwo(data) {
for (var i in data) {
if (!data[i].showMe)
data[i].showMe = !data[i].showMe;
}
}
Any ideas on how to properly toggle all rows on/off?
Using the ng-show directive in combination with the ng-click and ng-init directives, we can do something like this:
<div ng-controller="TableController">
<button ng-click="setVisible(true)">Show All</button>
<button ng-click="setVisible(false)">Hide All</button>
<ul>
<li ng-repeat="person in persons"
ng-click="person.visible = !person.visible"
ng-show="person.visible">
{{person.name}}
</li>
</ul>
</div>
Our controller might then look like this:
myApp.controller('TableController', function ($scope) {
$scope.persons = [
{ name: "John", visible : true},
{ name: "Jill", visible : true},
{ name: "Sue", visible : true},
{ name: "Jackson", visible : true}
];
$scope.setVisible = function (visible) {
angular.forEach($scope.persons, function (person) {
person.visible = visible;
});
}
});
We are doing a couple things here. First, our controller contains an array of person objects. Each one of these objects has a property named visible. We'll use this to toggle items on and off. Second, we define a function in our controller named setVisible. This takes a boolean value as an argument, and will iterate over the entire persons array and set each person object's visible property to that value.
Now, in our html, we are using three angular directives; ng-click, ng-repeat, and ng-show. It seems like you already kinda know how these work, so I'll just explain what I'm doing with them instead. In our html we use ng-click to set up our click event handler for our "Show All" and "Hide All" buttons. Clicking either of these will cause setVisible to be called with a value of either true or false. This will take care of toggling all of our list items either all on, or all off.
Next, in our ng-repeat directive, we provide an expression for angular to evaluate when a list item is clicked. In this case, we tell angular to toggle person.visible to the opposite value that it is currently. This effectively will hide a list item. And finally, we have our ng-show directive, which is simply used in conjunction with our visible property to determine whether or not to render a particular list item.
Here is a plnkr with a working example: http://plnkr.co/edit/MlxyvfDo0jZVTkK0gman?p=preview
This code is a general example of something you might do, you should be able to expand upon it to fit your particular need. Hope this help!

Why is my click event called twice in jquery?

Why is my click event fired twice in jquery?
HTML
<ul class=submenu>
<li><label for=toggle><input id=toggle type=checkbox checked>Show</label></li>
</ul>
Javascript
$("ul.submenu li:contains('Show')").on("click", function(e) {
console.log("toggle");
if ($(this).find("[type=checkbox]").is(":checked")) console.log("Show");
else console.log("Hide");
});
This is what I get in console:
toggle menu.js:39
Show menu.js:40
toggle menu.js:39
Hide menu.js:41
> $("ul.submenu li:contains('Show')")
[<li>​ ]
<label for=​"toggle">​
<input id=​"toggle" type=​"checkbox" checked>​
"Show"
</label>​
</li>​
If I remember correctly, I've seen this behavior on at least some browsers, where clicking the label both triggers a click on the label and on the input.
So if you ignore the events where e.target.tagName is "LABEL", you'll just get the one event. At least, that's what I get in my tests:
Example with both events | Source
Example filtering out the e.target.tagName = "LABEL" ones | Source
I recommend you use the change event on the input[type="checkbox"] which will only be triggered once. So as a solution to the above problem you might do the following:
$("#toggle").on("change", function(e) {
if ($(this).is(":checked"))
console.log("toggle: Show");
else
console.log("toggle: Hide");
});
https://jsfiddle.net/ssrboq3w/
The vanilla JS version using querySelector which isn't compatible with older versions of IE:
document.querySelector('#toggle').addEventListener('change',function(){
if(this.checked)
console.log('toggle: Show');
else
console.log('toggle: Hide');
});
https://jsfiddle.net/rp6vsyh6/
This behavior occurs when the input tag is structured within the label tag:
<label for="toggle"><input id="toggle" type="checkbox" checked>Show</label>
If the input checkbox is placed outside label, with the use of the id and for attributes, the multiple firing of the click event will not occur:
<label for="toggle">Show</label>
<input id="toggle" type="checkbox" checked>
I found that when I had the click (or change) event defined in a location in the code that was called multiple times, this issue occurred. Move definition to click event to document ready and you should be all set.
Not sure why this wasn't mentioned. But if:
You don't want to move the input outside of the label (possibly because you don't want to alter the HTML).
Checking by e.target.tagName or even e.target doesn't work for
you because you have other elements inside the label
(in my case it had spans holding an SVG with a path so e.target.tagName sometimes showed SVG and other times it showed PATH).
You want the click handler to stay on the li (possibly because you have
other items in the li besides the checkbox).
Then this should do the trick nicely.
$('label').on('click', function(e) {
e.stopPropagation();
});
$('#toggle').on('click', function(e) {
e.stopPropagation();
$(this).closest('li').trigger('click');
});
Then you can write your own li click handler without worrying about events being triggered twice. Personally, I prefer to use a data-selected attribute that changes from false to true and vice versa each time the li is clicked instead of relying on the input's value:
$('ul.submenu li').on('click', function() {
let _li = $(this),
ticked = _li.attr('data-selected');
ticked = (ticked === 'false') ? true : false;
_li.attr('data-selected', ticked);
_li.find('#toggle').prop('checked', ticked);
});

Resources