angular-ui-tree two events instead of one - javascript

I'm sure it is pretty simple question, but I can't find answer. I use pretty common pattern for creating tree. I need to know if user click parent node or child node. Everything works fine if I click parent node. But if I click child node, the callback function calls two times - first from child node, second from parent node. Could somebody explain me why? Thank you.
<div id="groups-tree" class="col-sm-4">
<script type="text/ng-template" id="nodes_renderer.html">
<div ui-tree-handle class="tree-node tree-node-content">
<a class="btn btn-success btn-xs" ng-if="node.nodes && node.nodes.length > 0"
ng-click="selected(node)" data-nodrag>
<span class="glyphicon"
ng-class="{
'glyphicon-chevron-right': collapsed,
'glyphicon-chevron-down': !collapsed
}"></span>
</a>
{{node.title}}
</div>
<ol ui-tree-nodes="" ng-model="node.nodes" ng-class="{hidden: collapsed}">
<li ng-repeat="node in node.nodes" ui-tree-node ng-include="'nodes_renderer.html'"
ng-click="selected(node)"*# data-nodrag>
</li>
</ol>
</script>
<div class="row">
<div class="col-sm-6">
<div ui-tree id="tree-root">
<ol ui-tree-nodes ng-model="data" data-nodrag>
<li ng-repeat="node in data" ui-tree-node ng-include="'nodes_renderer.html'"
ng-click="selected(node)"></li>
</ol>
</div>
</div>
</div>
</div>

I found solution here. The angular-ui-tree is sequence of enclosed elements. So I should stop propagate the event from child element to parent element.

Related

How can I detect a html element by event click without getElements...?

On one page, several Collapsibles can occur! I would like to now, if a link is activated. Get the ProductItem in which the link was activated.
I have tried it with getElementsByClass but get a complete Node-List which doesn't help me! Because there can be several collapsibles on one page
<div class="collapsible">
<div class="collapsible__item collapsible__item--active">
<div class="collapsible__body" style="">
<section id="c395" class="frame centered frame-type-flux_productlistitem frame-layout-0">
<section>
<div class="products">
<ul class="products__list">
<li class="products__item">
<p><a>Link</a></p>
</li>
<li class="products__item">
<p><a>Link</a></p>
</li>
</ul>
</div>
</section>
</section>
</div>
</div>
</div>
Simply use event delegation, where you set up an event handler on a high level DOM element and then investigate the instigating element through the event reference:
// Set up the event on a high-level element
document.querySelector(".collapsible").addEventListener("click", function(evt){
// Get information about the element that originated the event
console.log("The event originated with: ", evt.target);
console.log("The product is: ", evt.target.closest("li").classList.toString());
});
<div class="collapsible">
<div class="collapsible__item collapsible__item--active">
<div class="collapsible__body" style="">
<section id="c395" class="frame centered frame-type-flux_productlistitem frame-layout-0">
<section>
<div class="products">
<ul class="products__list">
<li class="products__item1">
<p><a>Link 1</a></p>
</li>
<li class="products__item2">
<p><a>Link 2</a></p>
</li>
</ul>
</div>
</section>
</section>
</div>
</div>
</div>

JavaScript Event for Capturing Dynamically Generated Non-Children Elements

I need to be able to get access to an element that will be available via AJAX after several click events. I cannot simply use $(element).on('click', selector, event) because the element I need access to is not a child element.
For example, what appears on initial page load is this:
<ol class="opc" id="checkoutSteps">
<li id="opc-billing" class="section opc-step"
data-step-number="0">
<div class="step-title" data-href="#step-1">
<span class="number">1</span>
<h2>Billing Information</h2>
<a onclick="stepTo('#step-2'); return false;"
href="#step-2">
View </a>
<a data-toggle="collapse" data-parent="#step-all"></a>
</div>
<div id="billing-step-login" class="step a-item">
<div id="step-2"
class="panel-collapse collapse in">
<div class="panel-body">
</div>
</div>
</div>
</li>
<li id="opc-shipping" class="section opc-step"
data-step-number="1">
<div class="step-title" data-href="#step-2">
<span class="number">2</span>
<h2>Shipping Information</h2>
<a onclick="stepTo('#step-3'); return false;"
href="#step-3">
Edit </a>
<a data-toggle="collapse" data-parent="#step-all"></a>
</div>
<div id="shipping-step-login" class="step a-item">
<div id="step-3"
class="panel-collapse collapse ">
<div class="panel-body">
<button class="button btn-next" type="submit"><span><span>Continue</span></span></button>
</div>
</div>
</div>
</li>
<li id="opc-shipping_method" class="section opc-step"
data-step-number="2">
<div class="step-title" data-href="#step-3">
<span class="number">3</span>
<h2>Shipping Method</h2>
<a onclick="stepTo('#step-4'); return false;"
href="#step-4">
Edit </a>
<a data-toggle="collapse" data-parent="#step-all"></a>
</div>
<div id="shipping_method-step-login" class="step a-item">
<div id="step-4"
class="panel-collapse collapse ">
<div class="panel-body">
</div>
</div>
</div>
</li>
<li id="opc-payment" class="section opc-step"
data-step-number="3">
<div class="step-title" data-href="#step-4">
<span class="number">4</span>
<h2>Payment Method</h2>
<a onclick="stepTo('#step-5'); return false;"
href="#step-5">
Edit </a>
<a data-toggle="collapse" data-parent="#step-all"></a>
</div>
<div id="payment-step-login" class="step a-item">
<div id="step-5"
class="panel-collapse collapse ">
<div class="panel-body">
</div>
</div>
</div>
</li>
<li id="opc-review" class="section">
<div class="step-title" data-href="#step-6">
<span class="number">5</span>
<h2>Order Review</h2>
Edit
<a data-toggle="collapse" data-parent="#step-all"></a>
</div>
<div id="review-step-login" class="step a-item">
<div id="step-6" class="panel-collapse collapse">
<div class="panel-body"></div>
</div>
</div>
</li>
</ol>
Once I click the button inside div.#step-3, some content with a button loads in div.#step-4. After clicking the button in that div, a button loads in #step-5. Finally, after clicking on the button inside #step-5, there is content (textarea and another button) loaded in #step-6 that I'm interested in.
How do I go about writing a jQuery or JavaScript event that would allow me to get access to the div.#step-6 textarea? Can I write several nested $(element).on() events?
P.S. I don't have access to change any of the existing HTML, CSS, or JavaScript files. I can only write new JavaScript.
It seems that you can bind the delegated event to a common static ancestor.
For example, #checkOutSteps:
jQuery('#checkOutSteps').on('focus','div.#step-6 textarea',function(){
...
});
Event delegation allows us to attach a single event listener, to a parent element, that will fire for all descendants matching a selector, whether those descendants exist now or are added in the future. --Event Delegation

Selection of elements using nth-child selectors

$(".sidebar>i:nth-child(3)").on("click", showMenu);
<div class="sidebar">
<i class="fa fa-volume-up"></i>
<div class="colorPalette">
<button class="submit">Submit</button>
<p>Color Pallette:</p><br />
<div class="colors_1">
<span id="color1"></span>
<span id="color2"></span>
<span id="color3"></span>
</div>
<div class="colors_2">
<span id="color4"></span>
<span id="color5"></span>
<span id="color6"></span>
</div>
</div>
<i class="fa fa-arrow-circle-o-left" title="Back to Menu"></i>
</div>
I need to select the 2nd (Back to Menu) button. So why nth-child(3) works but not nth-child(2)?
Since :nth-child(n) selects all children inside a parent element, regardless of what they are. So, in your case:
.sidebar>i:nth-child(3)
// This is the 3rd child, i tag
.sidebar>i:nth-child(2)
// This is the 2nd child, div tag
console.log($('.sidebar>i:nth-child(1)')[0])
console.log($('.sidebar>:nth-child(2)')[0])
console.log($('.sidebar>i:nth-child(2)')[0])
console.log($('.sidebar>i:nth-child(3)')[0])
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="sidebar">
<i class="fa fa-volume-up"></i>
<div class="colorPalette">Test</div>
<i class="fa fa-arrow" title="Back to Menu"></i>
</div>
What you need is actually :nth-of-type() selector.
console.log($('.sidebar>i:nth-of-type(1)')[0])
console.log($('.sidebar>i:nth-of-type(2)')[0])
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="sidebar">
<i class="fa fa-volume-up"></i>
<div class="colorPalette">
Test
</div>
<i class="fa fa-arrow" title="Back to Menu"></i>
</div>
From JQuery's docs:
The :nth-child(n) pseudo-class is easily confused with :eq(n), even though the two can result in dramatically different matched elements. With :nth-child(n), all children are counted, regardless of what they are, and the specified element is selected only if it matches the selector attached to the pseudo-class. With :eq(n) only the selector attached to the pseudo-class is counted, not limited to children of any other element, and the (n+1)th one (n is 0-based) is selected.

add CSS element using an angular directive

Im using angular and ng-repeat to populate a list of studies. This list is dynamic so you can toggle into child elements of each element. So basically I have a accordion style toggle list that can go three levels deep on each list item. I have a jQuery issue that I think should be solved with some angular directive or something. Basically I have an arrow (glyphicon) that should switch to up or down depending on if your looking at a child or a parent element in this list. I have this working with pure jQuery just adding and removing a css class from each list item. However, it only works on the first item in the list because ng-repeat creates multiple id’s but jQuery will only work on the first element with that id tag.
This is the HTML from the page.
<!-- User Studies List -->
<div ng-controller="StudiesController" id="before">
<h3 class="center"> User Studies </h3>
<div ng-repeat="study in studies" arrow>
<div class="panel-group" style="margin-bottom:0">
<div class="panel panel-default">
<div class="panel-heading" data-toggle="collapse" href="#collapse{{$index}}">
<h3 class="panel-title">{{ study.study }} <span class="glyphicon glyphicon-menu-down pull-right"></span></h3>
<p></p>
</div>
<div id="collapse{{$index}}" class="panel-collapse collapse">
<ul class="list-group">
<li class="list-group-item" data-toggle="collapse" href="#collapse{{study.id}}" style="margin-left:1%" id="sampleID{{$index}}">
<i>Sample: </i>{{ study.sample }}
<span id="glyph-switch" class="glyphicon glyphicon-menu-down pull-right"></span>
<span id="glyph-up" class="glyphicon glyphicon-menu-up pull-right"></span>
</li>
<div id="collapse{{study.id}}" class="panel-collapse collapse">
<ul class="list-group">
<li class="list-group-item" style="margin-left:3%"><i>Fastq: </i>{{ study.fastq }}
<div class="dropdown center pull-right">
<button class="btn-xs btn-default hvr-shadow" id="dLabel" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Action <span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="dLabel">
<li id="list-item" data-toggle="modal" data-target="#more-metadata-modal" ng-click="showMore(study)">More Data</li>
<li id="list-item" data-toggle="modal" data-target="#edit-metadata-modal" ng-click="showMore(study)">Edit</li>
<li id="list-item" data-toggle="modal" data-target="#delete-metadata-modal" ng-click="showMore(study)">Delete</li>
</ul>
</div>
</li>
</ul>
</div>
</ul>
</div>
</div>
</div>
</div>
</div>
This is the Javascript
var count = 1;
$(document).on("click", "li[id*='sampleID']", function() {
console.log("clicked");
if(count > 0) {
$('#glyph-switch').css('visibility','hidden');
$('#glyph-up').css('visibility', 'visible');
count -= 1;
console.log("count", count);
} else {
$('#glyph-switch').css('visibility','visible');
$('#glyph-up').css('visibility', 'hidden');
count += 1;
console.log("count",count);
}
});
I would just use angular code (no jQuery at all).
Since you already have an object of studies, just handle an onClick="showGlyph($index)" passing the index and then in the code just set the a state for the up/down arrow $scope.GlyphUp = !$scope.GlyphUp.
In the template file have the up/down arrow object bind ng-show="GlyphUp"

AngularJS nested ng-repeat

I have object which contains question and list of choices. I'm display this object on the page using nested ng-repeats. When I'm trying to change 'question' everything changes just fine, but when I'm trying to change 'choice' nothing happens. Where is the problem?
My page:
<div class="panel">
<div class="row">
<div class="large-12 columns">
<h3>{{ title }}</h3>
<ol>
<li ng-repeat="q in quiz">
<input type="text" ng-model="q.question">
<ul class="remove-li-dots">
<li ng-model="choice" ng-repeat="choice in q.choices">
<input type="text" ng-model="choice" name="answer{{ q.id }}" >
</li>
</ul>
</br>
</li>
</ol>
<a class="button" ng-click="submitQuiz()">Submit</a><br>
{{ quiz }}
</div>
</div>
Screenshot of the page:
https://www.dropbox.com/s/i52fwq1os0cvcr9/repeat.png?dl=0
Problem is that choice is a string, so when you are changing it in child scope it is changing only in child scope.
To fix this - reference it by index in array ng-repeat="(choiceIndex,choice) in q.choices,ng-model="q.choices[choiceIndex]".
Also to prevent inputs from loosing focus when changing items - you will need to add track by $index in ng-repeat directive.
<ol>
<li ng-repeat="q in quiz">
<input type="text" ng-model="q.question">
<ul class="remove-li-dots">
<li ng-model="choice" ng-repeat="(choiceIndex,choice) in q.choices track by choiceIndex">
<input type="text" ng-model="q.choices[choiceIndex]" name="answer{{ q.id }}">
</li>
</ul>
</li>
</ol>
working example:
http://plnkr.co/edit/GJacjkzfT4FpfC6szsNk?p=preview
For better understanding how angular scopes works, I recommend reading this: https://github.com/angular/angular.js/wiki/Understanding-Scopes

Categories

Resources