How to listen to `iron-select` event in the child of iron-pages? - polymer-1.0

Consider the following code from PSK:-
<iron-pages
selected="[[page]]"
attr-for-selected="name"
fallback-selection="view404"
role="main">
<my-view1 name="view1"></my-view1>
<my-view2 name="view2"></my-view2>
<my-view3 name="view3"></my-view3>
<my-view404 name="view404"></my-view404>
</iron-pages>
Is it possible to listen to iron-select event within <my-view#> element whenever it is selected by <iron-pages>?

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>

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

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.

Angular Scope and ng-repeat

My question in short
how do i manipulate the isolated scope created by ng-repeat inside of my controller?
To read the full problem i am having continue reading below:
I have a collection on timelines and each of them further has a collection of captures (essentially images).
HTML
<ul>
<li ng-repeat="timeline in timelines | filter:search | orderBy:'event_date':sort">
<ul ng-if="timeline.captures">
<li ng-repeat="captures in timeline.captures | limitTo:imageLimit">
<img ng-src="<% captures.capture_location %>">
</li>
</ul>
<a ng-click="(imageLimit = imageLimit + 1)">Load more</a>
</li>
</ul>
I want to limit the number of images being loaded on page load.. which i am able to do using limitTo
I want to load more images when the user clicks on 'Load more'. I am also able to do this by ng-click="(imageLimit = imageLimit + 1)
What i want to do:
I want to extract this out as a function
so that the following
Load more
becomes
Load more
To achieve this, i tried the following:
inside my controller
$scope.imageLimit = 1; // number of images to show initially on page load
$scope.loadMore = function() {
$scope.imageLimit += 1;
};
Now what is happening is that this this not create the imageLimit model inside the repeated scope (if you understand what i mean) but instead creates the imageLimit model at the controller scope. This is also the expected behaviour but i want to know how do i manipulate the scope within the repeated timeline (isolated scope for that timeline) inside my controller?
If I understand correctly, you want to be able to loadMore() for each timeline, independantly of the other ones. The loadMore() function should thus take the timeline as argument, and the limit should be a property of the timeline:
angular.forEach($scope.timelines, function(timeline) {
timeline.imageLimit = 10; // default value
});
$scope.loadMore(timeline) {
timeline.imageLimit++;
}
And in your template:
<li ng-repeat="captures in timeline.captures | limitTo:timeline.imageLimit">
...
<a ng-click="loadMore(timeline)">Load more</a>

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