I am trying to decrement a counter within a nested AnguarJs ng-repeat. I know it is a problem of scope, because the increment in the outer ng-repeat works fine, but in the inner ng-repeat is where I have to decrement and that is where I run into the problem (nothing happens). Is there any way to get an inner scope to increment/decrement/affect an outer scope? Here is the code I am working with.
<div class = "col-md-12 section-bg-conversations" ng-repeat="o in form.question | filter:searchText | orderBy:predicate" ng-show="$index<5 || form.showMoreQuestions" >
<!--section for question and option to answer-->
<span class="pull-left" style="margin-bottom:5px">
<ul style="color:#107A77; margin-left: -10px; list-style-type:none">
<li >
<!--below is only to toggle form view-->
<h7><strong><a style="color:#107A77;" ng-click="o.Answer = true" ng-show="!o.Answer">Comment</a></strong></h7>
</li>
<li>
<h7 ng-init="replyCounter=o.replyCounter">Replies ({{replyCounter}}) </h7>
</li>
<li>
<h7>
<a style="color:#107A77;text-size: 6px"
ng-init="followCounter=o.followCounter" ng-click="followConversation(o.id); o.isFollowing=true;
followCounter=followCounter+1" ng-show="!o.isFollowing">
Follow ({{followCounter}})
</a>
<span ng-show="o.isFollowing">
Following ({{followCounter}})
</span>
</h7>
</li>
</ul>
</span>
<span ng-show="o.isFollowing">
<a style='color:gold' ng-init="followCounter = o.followCounter" ng-click="unfollowConversation(o.followId); o.isFollowing = false;
followCounter = followCounter-1 " ng-hide="o.unfollowValue">Unfollow</a>
</span>
<!--form div below showing only submit and cancel-->
<div style="margin-top:5px">
<button class="btn btn-xs btn-primary" ng-init="replyCounter=o.replyCounter" style='background:#107A77'
ng-click="submitAnswer(o.id); replyCounter = replyCounter+1;">Submit</button>
<button class="btn btn-xs btn-primary" style='background:red' ng-click="o.Answer = false" ng-show="o.Answer">Cancel</button>
</div>
<div class = "col-md-offset-1" style="padding:5px"ng-repeat="a in o.replies" ng-show="$index<3 || o.showMoreReplies">
<span ng-show="a.isAuthor">
<a ng-init="replyCounter=o.replyCounter"style='color:red' ng-click="doDelete(a.id); a.deleteValue = true;
replyCounter = replyCounter-1">Delete</a>
</span>
</div>
I also included the followCounter as an exmaple of a counter that is working. Pretty much the main problem is in the last inner div that does ng-repeat="a in o.replies" and I'm not sure how to solve the problem of incrementing/decremeting the counter in the overall scope.
Related
So I have ul's on my webpage similar to the one below and I'm trying to loop through each of them to grab a span within the li. Below is an example of how it appears in my HTML:
<ul id="item-NineAM" class="list-group">
<li class="list-group-item w-100 border-dark past">
<span class="badge badge-dark" id="item-time">09:15 AM</span>
<p class="m-1 w-75 item-text">fsda</p>
<button class="btn btn-danger" id="trashCan"><span class="oi oi-trash"></span></button></li>
</ul>
I've set up an interval to grab the span with id #item-time and pass it into my audit function
const listGroup = $(".card .list-group li #item-time");
$.each(listGroup, function(i, itemSpan){
console.log(itemSpan)
auditItem(itemSpan)
})
I've verified in the console that the span is passed into function but I cannot seem to grab the text of it.
<span class="badge badge-dark" id="item-time">09:15 AM</span>
I need to grab the time that's inside of the span. My console tells me it's an object. Any ideas? I've tried .text() and .innerHTML but no luck.
Thanks!
The jquery selector you're assigning to listGroup should be pretty general, so you wouldn't include an id as you'd only get one match.
It looks like you're misusing id. Remember, you can only have a single instance of id and it should only be used to indicate uniqueness. Instead you want item-time to be a class since you can have several instances of classes.
I've corrected some others in your html as well.
const listGroup = $(".list-group li span.item-time");
$.each(listGroup, function(){
console.log($(this).text())
//auditItem(itemSpan)
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="list-group item-NineAM">
<li class="list-group-item w-100 border-dark past">
<span class="badge badge-dark item-time">09:15 AM</span>
<p class="m-1 w-75 item-text">fsda</p>
<button class="btn btn-danger trashCan">
<span class="oi oi-trash"></span>
</button>
</li>
</ul>
I am trying to do this:
Search for all the spans in my structure
Get the id value from each span
Update the parent with that text for test purposes
The reason for this work is that I am doing front-end customizations for an application and trying get some WAI-ARIA labelled-by values set on a parent element.
The problem is that many of the needed values come from an COTS application that I am working with/around. These needed input are not always set in a good sequence in the DOM.
I have been looking at a JS solution to get around this.
<div class="fluid-form-container">
<ul id="accordionGroup" class="Accordion" data-allow-multiple="">
<li class="fluid-form-group-container">
<h3 aria-labelledby="accordion1id">
<button aria-expanded="true" class="Accordion-trigger" aria-controls="sect1" id="accordion1id">
<span class="Accordion-title"><div class="fluid-form-title">
<div class="FormSection">
<span id="More_Info_Form_Section_Label">More Info</span>
</div>
</div>
</span>
</button>
</h3>
</li>
<li class="fluid-form-group-container">
<h3 aria-labelledby="accordion2id">
<button aria-expanded="true" class="Accordion-trigger" aria-controls="sect2" id="accordion2id">
<span class="Accordion-title"><div class="fluid-form-title">
<div class="FormSection">
<span id="Even_More_Info_Form_Section_Label">Even More Info</span>
</div>
</div>
</span>
</button>
</h3>
</li>
</div>
//My bad javaScript so far
var found_elements = [];
var outers = document.querySelectorAll('.FormSection');
for(var i=0; i<outers.length; i++) {
var elements_in_outer = outers[i].querySelectorAll("span");
var updateValue = elements_in_outer.getAttr("id");
outers[i].closest("h3").innerHTML = updateValue;
}
The expect results:
- parent tag innerHTML set to the id value of each span in the structure
Actual results:
- I'm getting errors because I am not sure what I need to use to get that id from each span found
querySelectorAll() returns a NodeList , so elements_in_outer.getAttr("id") won't work and should be replaced with querySelector()
there is no getAttr, use getAttribute
( i replaced your for with a forEach )
var found_elements = [];
var outers = document.querySelectorAll('.FormSection').forEach(div => {
var elements_in_outer = div.querySelector("span");
var updateValue = elements_in_outer.getAttribute("id");
div.closest("h3").innerHTML = updateValue;
});
<div class="fluid-form-container">
<ul id="accordionGroup" class="Accordion" data-allow-multiple="">
<li class="fluid-form-group-container">
<h3 aria-labelledby="accordion1id">
<button aria-expanded="true" class="Accordion-trigger" aria-controls="sect1" id="accordion1id">
<span class="Accordion-title"><div class="fluid-form-title">
<div class="FormSection">
<span id="More_Info_Form_Section_Label">More Info</span>
</div>
</div>
</span>
</button>
</h3>
</li>
<li class="fluid-form-group-container">
<h3 aria-labelledby="accordion2id">
<button aria-expanded="true" class="Accordion-trigger" aria-controls="sect2" id="accordion2id">
<span class="Accordion-title"><div class="fluid-form-title">
<div class="FormSection">
<span id="Even_More_Info_Form_Section_Label">Even More Info</span>
</div>
</div>
</span>
</button>
</h3>
</li>
</div>
If you know beforehand that you will only use the span elements that have an id, then use [id] in the querySelectorAll selector likewise
document.querySelectorAll('span[id]')
If you will use that as an array, then you need
[... document.querySelectorAll('span[id]')]
Assigning the new value would be something like this:
[... document.querySelectorAll('span[id]')].forEach(s => s.closest("h3").innerHTML = s.id)
i have html div where i use ng-repeat that gives me back elements from array
<div>
<div class="col-sm-3" ng-repeat="el in vm.filmovi " id="filmovi">
<img src="http://image.tmdb.org/t/p/w500/{{el.poster_path}}" style="width:100%;"><br>
<a ng-click="vm.set_favorit(el)" style="cursor:hand; color:white;" uib-tooltip="Postavi u omiljene">
<i class="glyphicon" ng-class="{'glyphicon-star-empty':el.favorit!=true, 'glyphicon-star':el.favorit==true}"
aria-hidden="true"></i></a>
<a href="http://www.imdb.com/title/{{el.imdb_id}}/" style="color:white;">
<strong>{{ el.title | limitTo: 20 }}{{el.title.length > 20 ? '...' : ''}}</strong></a>
<a class="glyphicon glyphicon-share-alt" style="margin-left:5px; color:white;" ng-click="vm.open()" uib-tooltip="share" ></a><br>
{{el.popularity}} <br>
<a style="color:white;" href="#" ng-click="vm.filter(genre)" ng-repeat="genre in el.genres"><small>{{genre.name}} </small></a>
<div ng-init="x = 0">
<span uib-rating ng-model="x" max="5"
state-on="'glyphicon-star'"
state-off="'glyphicon-star-empty'"></span></div>
</div>
</div>
now i created a button that changes value of id "filmovi"
<li><a href="#" ng-hide="vm.ulogovan" ng-click="vm.dugme();" >losta</a></li>
and created function vm.dugme() that gets element by id and sets class atribute to col-sm-4
vm.dugme=function(){
document.getElementById("filmovi").setAttribute("class","col-sm-4");
};
but when i did that only the first element changed
but i need for all of them to change to col-sm-4 , any suggestion?
Don't do DOM manipulation from angularjs controller. Instead make use of directive provided by angular.
You could use ng-class with expression so that whenever expression gets satiesfied the class will be added over that DOM. To add class put addColSm4 flag inside a controller and change that flag from dugme method of your controller. Also by looking at screenshot it seems like you need col-sm-3 class at the beginning, afterwards you need to apply col-sm-4.
HTML
<div class="row">
<div class="col-sm-3" ng-repeat="el in vm.filmovi"
ng-class="{'col-sm-4': vm.addColSm4, 'col-sm-3': !vm.addColSm4 }" >
.. Html will stay as is ..
</div>
</div>
<li>
<a href="#" ng-hide="vm.ulogovan"
ng-click="vm.dugme()">
losta
</a>
</li>
Code
vm.dugme = function (){
vm.addColSm4 = true;
};
Demo Plunker
I currently have a directive to dynamically edit a field. I have in the header of the accordion a field to edit and another field in the content of the accordion. If I click the edit button, the fields in the respective row can be edited, and this works fine. My problem is when I save or when I cancel it (when I click on the save or cancel button) immediately disappears the text field of both the header and the contents of the header. I need the text field ONLY disappear for the item in which I am going to save or cancel. When you click on the edit button, the 2 text fields should appear in both the header and the content (this works). And when clicking save or cancel, the text field in the selected element should disappear / appear.
<div ng-controller="AccordionDemoCtrl">
<uib-accordion close-others="true">
<div ng-repeat="faq in faqs">
<div class="col-sm-11" >
<div uib-accordion-group class="panel-default" is-open="faq.open">
<uib-accordion-heading >
<span ng-click="ignoreClick($event);" ><a href='' click-to-edit edit-state='faq.editState' ng-model='faq.pregunta' typeinput='textarea' >{{faq.pregunta}}</a></span> <i class="pull-right glyphicon" ng-class="{'glyphicon-chevron-down': faq.open, 'glyphicon-chevron-right': !faq.open}"></i>
</uib-accordion-heading>
<span click-to-edit edit-state='faq.editState' ng-model="faq.respuesta" >{{faq.respuesta}}</span>
</div>
</div>
<div class="col-sm-1" >
<button type="button" ng-click="toggleEditState($index)" class="btn btn-default">
<span class="glyphicon glyphicon glyphicon-edit"></span>
</button>
</div>
</div>
</uib-accordion>
</div>
https://plnkr.co/edit/S4OllJV64EYFNo6WIjVH?p=preview
use one-way (one-directional) binding for editState
scope: {
model: '=ngModel',
editState: '<'
},
https://plnkr.co/edit/dNehOxAIRHsRqgK9wXJx?p=preview
I believe you'll need to manage two separate booleans at the controller level so one directive doesn't overwrite the status of the other and then you have to set both booleans with your open button. I kept the master state so your glyph button will open or close both and the save/cancel should operate independently of each other.
$scope.editState = false;
$scope.editHeader = false;
$scope.editBody = false;
$scope.toggleEditState = function(index, val) {
debugger;
$scope.editState = !$scope.editState;
$scope.faqs[index].editHeader = $scope.editState;
$scope.faqs[index].editBody = $scope.editState;
}
<div ng-repeat="faq in faqs">
<div class="col-sm-11" >
<div uib-accordion-group class="panel-default" is-open="faq.open">
<uib-accordion-heading >
<span ng-click="ignoreClick($event);" ><a href='' click-to-edit edit-state='faq.editHeader' ng-model='faq.pregunta' typeinput='textarea' >{{faq.pregunta}}</a></span> <i class="pull-right glyphicon" ng-class="{'glyphicon-chevron-down': faq.open, 'glyphicon-chevron-right': !faq.open}"></i>
</uib-accordion-heading>
<span click-to-edit edit-state='faq.editBody' ng-model="faq.respuesta" >{{faq.respuesta}}</span>
</div>
</div>
<div class="col-sm-1" >
<button type="button" ng-click="toggleEditState($index)" class="btn btn-default">
<span class="glyphicon glyphicon glyphicon-edit"></span>
</button>
</div>
https://plnkr.co/edit/91cGWoTKyJsZQKxYapiT?p=preview
You could also communicate the master status with the directives if you wanted their input fields to open and close both.
I have an ng-repeat that repeats a dropdown. Each repeated div that holds the dropdown has a unique ID generated by the controller that I can reference.
How can I pass back the selected option for that specific dropdown? Right now, if one dropdown is selected, the value for selectedParameter.name changes for all dropdowns.
<div id="{{ mergeVar.name }}" class="alert {{ selectedParamClass }}" ng-repeat="mergeVar in mergeVars">
<b>merge value: </b> {{mergeVar.name}}
<div class="dropdown pull-right">
<button type="button" class="btn btn-sm btn-control dropdown-toggle" data-toggle="dropdown">
{{selectedParameter.name || 'Match the Paramater'}}
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li ng-repeat="param in availableParams">
<a ng-click="selectParameter(parampass)">{{param.name}}</a>
</li>
</ul>
</div>
</div>
//controller.js
$scope.selectParameter = function(parampass) {
console.log('parameter selected')
$scope.selectedParameter = parampass
$scope.selectedParamClass = 'alert-success'
}
Do this instead to affect only one instance of your object:
$scope.selectParameter = function(parampass) {
console.log('parameter selected')
parampass.selectedParamClass = 'alert-success';
}
What you need is to add the property to the instance "row" object.
You can still store the selected object:
$scope.selectedParameter = parampass
But based on what I see in your code what you probably want do to is to use the ng-class="selectedParamClass" when an item is selected for the object selected.