I am trying to show some divs (which are under 3+ ng-repeats) conditionally and then increment a counter variable which. The counter contributes in the conditions which decide the visibility of divs.
When I do something similar tong-show='foo++ < SOME_LIMIT' I get a syntax error:
Error: Syntax Error: Token 'undefined' not a primary expression at column NaN of the expression [moveItem.type != 'account' && team.rowCount++] starting at [moveItem.type != 'account' && team.rowCount++].
or
Error: Syntax Error: Token '2' is an unexpected token at column 42 of the expression [team.expandAccounts || team.rowCount++ < 2] starting at [2]
Giving the code doesn't seem to be required but still I am giving what I was trying.
I tried something similar to:
<div ng-repeat='request in pagedRequests'
<tr ng-repeat='(fooId, foo) in returnFoos(request)'>
<td ng-init='foo.rowCount = 0'>
{{foo.someAttribute}}
<!--Here is just an icon shown when the rowCount reaches some limit, say 3.
Uses ng-switch on foo.rowCount. Skipping this as this doesn't seem
problematic. This toggles rows' collapsing using foo.expandRows.-->
<div ng-repeat='bar in foo.bars'
ng-show='foo.rowCount < 3 || foo.expandRows'>
<span ng-show='bar.type=="multiRowBar"'
ng-repeat='row in bar.rows'>
<span ng-show="foo.expandRows || foo.rowCount++ < 3"> <!--SYNTAX ERROR!-->
<!--Showing the nested objects with more ng-repeats.-->
</span>
<span ng-show='bar.type!="multiRowBar" && foo.rowCount++<3'> <!--SYNTAX ERROR!-->
<!-- This adds a single row per bar of this type.-->
</span>
</div>
</td>
</tr>
</div>
Is it that we can't use post/pre increment/decrement operators (like ++) inside angular expressions?
Why just do not use method like:
<button ng-show='increaseFoo() < SOME_LIMIT'>press me</button>
controller
$scope.SOME_LIMIT = 10;
$scope.foo = 8;
$scope.increaseFoo = function(){
return $scope.foo++;
};
Fiddle
if $scope.foo = 8;, ng-show returns true
if $scope.foo = 9;, ng-show returns false
The question is a bit ignorant to the fact that such an effort triggers cyclic $digest calls which breaks. (Just for the sake of making some sense out of this question I'm giving my solution. Hope it will help someone trying this stuff) The solution I finalized involved flattening all the lists to a single list and switching to show the icon for a particular value of $index.
Related
I have a survey, that people can take. However, if you are one of the first 1000 to fill out the survey you are given a reimbursement. What I am struggling with is how to only show the reimbursement when the users are less than 1000.
The questions in the survey are coming from a CMS backend, while the reimbursement is built on the front end.
<div class="questionContainer" v-if="intentGiven && consentGiven && !surveyComplete">
<div v-if="$store.state.scoring.gameCompleted && this.index == this.QuestionnaireSize && this.ParticipantCount <= 1000">
<ResearchQuestionReimbursement v-on:answered="advanceQuestion"/>
</div>
<div v-else>
<ResearchQuestionMultipleChoice :question="questions[index]"
v-if="questions[index].Type == 'MultipleChoice'"
v-on:answered="advanceQuestion" />
<ResearchQuestionFreeAnswer :question="questions[index]"
v-if="questions[index].Type == 'FreeAnswer'"
v-on:answered="advanceQuestion" />
</div>
</div>
questions[index] is an array of the questions received from the backend CMS. The reimbursement is only supposed to be at the end which is why I have the div wrapped around <ResearchQuestionReimbursement>.
My control logic for the index is:
if (this.index < this.questions.length -1 || (this.$store.state.scoring.gameCompleted && this.index < this.questions.length)) {
this.index++;
console.log(`post: ${this.index}`)
} else {
.....
//required code to save the form
}
Currently, if I have over 1000 users the logic continues and gives me an index undefined error. This makes sense since the index would be 13 and the array would only be 12. I have tried a v-if on <ResearchQuestionReimbursement> to check if there are 1000 users. This resulted in a blank page rendering since it rendered the div but not the content.
What it should do is if there are more than 1000 users it will skip to the else statement.
I have been trying for a few days that when I use the .sort property the data is eliminated or modified instead of it being reloaded as new lines.
Attach Captures from the code Working
Image1 How work the code when i press the button, this sort to the highest price to lowest but how do you can see in the second image, the code appears up and this not delete the old data
Marked with "X" the data that does not have to show
this fragment is the one that generates the tables dynamically
const mostrarProductos = () => {
$.getJSON(URLJSON, (respuesta) => {
for (let z of respuesta) {
productosv2.push(z);
}
for (let x of productosv2) {
$("#fila").append(`
<tr class="deleteProductos">
<div class="card text-center" style="width: 18rem;" id='btnBorrarCarrito'>
<div class="card-body">
<input type="hidden" id="idProd" value="${x.id}"> </td>
<td class="card-title" id="${x.id}">${x.producto}</h2> </td>
<td class="card-text">$ ${x.precio}</p></td>
<div class="btn-group" role="group" aria-label="Basic mixed styles example">
<td><button type="button" class="btn btn-success" onclick="agregarCarrito(${x.id})">Agregar</button></td>
</div>
</div>
</div>
</tr>
`);
}
$("#fila").fadeIn("5000");
});
};
And this function is what orders them
function respuestaClickExpensive() {
$("#fila").html('');
let productosordenados = productosv2.sort((a, b) => {
if (a.precio > b.precio) {
return -1;
}
if (a.precio < b.precio) {
return 1;
}
return 0;
});
return productosordenados;
}
The one that orders them from smallest to largest is the same only that different signs and names.
As you can see I tried to use a ".html ("")" since previously in another cart attempt it used .innerHtml (Which does not work in this case either, Also the code of the cart is totally different from that moment that it worked for me
)
I tried the following:
$ ("#fila"). empty ();
Make another function to clean with .empty
Use Native JavaScript to generate the code.
$ ("#fila"). remove (); this removes all the content for me but does not regenerate it.
Change the HTML tag "Row" to a previous div which, since the div was not generated again, did not generate it again.
$ ("#fila tr"). remove ();
And some more things that I don't remember right now.
If you can guide me on what I did wrong or any suggestions to fix it, I appreciate it.
If I had to follow a template about posting on StackOverFlow or having chosen or named in a different way, I appreciate your comment since it is my first post
Project notes of possible relevance: The complete code outside of html and css is made with Native JavaScript, Jquery, Ajax, SASS and BootStrap.
This is not a duplicate.
In the other post, they are just doing a ternary operation. I wanna changes classes within ng-repeat.
I have this piece of code with little bugs.
HTML:
<div id="server-id-list-container" class="panel-body col-md-12 scrollbar">
<div class="server-id-list-element" ng-class="serverIdLength > 12 ? 'col-md-3' : 'col-md-2'" ng-repeat="server in selection.serverIds">
<p class="alert alert-info">{{server.serverId}}<span ng-click="removeServerId($index)" class="glyphicon glyphicon-remove"></span></p>
</div>
</div>
Controller:
_.forEach($scope.selection.serverIds, function(a) {
$scope.serverIdLength = a.serverId.length;
});
Scope Object:
[
{
"serverId": "loma1pwipdb2002",
"serverName": "",
},
{
"serverId": "shdmqprtp1",
"serverName": "",
}
]
When I enter "loma1pwipdb2002", the class becomes col-md-3 and since I am using ng-repeat applies for all elements. I want the class to be applied only to serverIdLength > 12 and if its lesser than 12, col-md-2 should get applied.
Please advice.
Is it correct that you want to switch your class for each element of selection.serverIds list separately based on serverId string length? Need to know your selection.serverIds, is it your "Scope Object"? If yes, then I would do just
<div
class="server-id-list-element"
ng-repeat="server in selection.serverIds"
ng-class="server.serverId.length > 12 ? 'col-md-3' : 'col-md-2'"> ... </div>
The problem is that your $scope.serverIdLength is being calculated once for all the list. While you want to have a dynamic class based on each item specific property.
Let's continue discussion if I didn't understand the issue and the entry conditions.
the issue seems to lie here:
_.forEach($scope.selection.serverIds, function(a) {
$scope.serverIdLength = a.serverId.length;
});
No matter what $scope.serverIdLength will always be set to the length of the last serverId. That because it's a global variable and there is only one instance of it. This is why all your classes match. They all reference the same variable.
Instead like #dhilt suggested ditch the controller code and acccess the length in the dom:
ng-class="server.serverId.length > 12 ? 'col-md-3' : 'col-md-2'"
Try that:
ng-class="{'col-md-3':server.serverId.length > 12, 'col-md-2':server.serverId.length <= 12}"
I have a div which I need to show when any of 5 conditions are true.
The problem is that ng-show seems to take into consideration only the first 3 conditions. I don't know if it's only my problem, cause I couldn't find any related problem.
In the following example I kept moving conditions around, but each time only the first 3 conditions are taken into consideration. Whatever condition falls into the 4th position, does not show the div.
Please let me know if you know a work around in case it's not possible to have more than 3 conditions.
It's a pretty big div, so I post only a part:
<div class="row " ng-show="selectedAllP[0] || selectedAllF[0] || selectedAllP || selectedAllD || level">
<div class="row selectedCase">
<div><h3 id="choices">Your choices:</h3></div>
<div class="col-md-4" id="publisher">
<h4>Publishers</h4>
<div class="level" ng-hide="selectedAllP[0] || selectedAllP">Select publisher</div>
<table class="table table-bordered table-striped">
<tbody id="pub">
<tr ng-hide="selectedAllP.Name === ' - Any Publisher -'" ng-repeat="roll in selectedAllP" ng-click="deSelectP(roll)">
<td class="deselect">{{roll.Name}}</td>
</tr>
<tr ng-show="selectedAllP.Name === ' - Any Publisher -'" ng-click="deSelectP(selectedAllP)">
<td class="deselect">{{selectedAllP.Name}}</td>
</tr>
</tbody>
</table>
<p ng-show="noticeAnyPublisher && selectedAllP.Name === ' - Any Publisher -'" class="notice">{{noticeAnyPublisher}}</p>
</div>
And the logic for a part of the div above:
$scope.selectedAllP = [];
$scope.setSelectedP = function (publisher) {
$scope.selectedP = publisher.Name;
// Check if the selected publisher is already in the array
// if it isn't, than push it in the array (else do nothing)
if (publisher.Name == " - Any Publisher -"){
$scope.selectedAllP = publisher;
console.log($scope.selectedAllP);
} else if ($scope.selectedAllP.Name == " - Any Publisher -") {
$scope.noticeAnyPublisher = 'Deselect "Any Publisher" first';
console.log("blblblb");
} else if ($scope.selectedAllP.indexOf(publisher) == -1) {
$scope.selectedAllP.push(publisher);
}
console.log($scope.selectedP, $scope.selectedAllP);
};
$scope.deSelectP = function (dePublisher) {
// Check if the selected publisher is already in the array
// if it is, than remove it from the array (else do nothing)
if (dePublisher.Name == " - Any Publisher -") {
$scope.selectedAllP = [];
$scope.selectedP = 0;
$scope.noticeAnyPublisher = '';
} else {
var idx = $scope.selectedAllP.indexOf(dePublisher);
if (idx != -1) {
$scope.selectedAllP.splice(idx, 1);
$scope.selectedP = $scope.selectedAllP[0].Name;
}
}
};
I want when I choose the 4th condition (in this case || selectedAllD) to show the div above, but as u can see, even if the distributor is selected (and selectedAllD exists in console), the div doesn't show. Check next image to see the div showing when I select the 3rd condition (selectedAllF[0]).
div showing when I select the 3rd condition (selectedAllF[0]). It is showing for the fisrt 2 conditions too. Problem is that its behavior is the same, whatever the order of the conditions (it only considers the first 3 conditions).
You could call a method in your ng-show.
<div class="row " ng-show="showRow()">
and declare that function into your .js file.
$scope.showRow = function () {
return $scope.selectedAllP[0] || $scope.selectedAllF[0] || $scope.selectedAllP || $scope.selectedAllD || $scope.level;
}
No matter how many condition you will pass to ng-show or any ohter directive which accepts expressions. See this docs Angular Expressions. So instead, check all of the parts of you condition statement. In such representation it will be evaluated to true every time when there is AT LEAST 1 statement which returns true. Also it is worth to note that this expression will be evaluated from left to right and UNTIL first true statement will be found.
I have an array of tasks. They have titles and and labels.
function Task(taskTitle, taskType) {
this.title = taskTitle;
this.type = taskType;
}
$scope.tasks = [];
I end up declaring a bunch of tasks with different types and adding them to the array
In my html, I show a column of cards, filtered by type of task:
<div ng-model="tasks">
<div class="card" ng-repeat="abc in tasks track by $index" ng-show="abc.type==0">
<p> {{ abc.title }} </p>
</div>
</div>
I want to bind the first card displayed in this filtered view to some other div. I'll be processing an inbox, so I'll whittle this list of cards down to zero. Each time I 'process' a card and remove it from the list, I need the data to refresh.
<div ng-model="firstCardInFilteredArray">
<h4>Title of first card:</h4>
<p> This should be the title of the first card! </p>
</div>
My intuition was to do something like this (in javascript):
// pseudo-code!
$scope.inboxTasks = [];
for (i=0; i<tasks.length(); i++) {
if (tasks[i].type == 0) {
inboxTasks.append(tasks[i]);
}
}
and somehow run that function again any time the page changes. But that seems ridiculous, and not within the spirit of Angular.
Is there a simple way in pure javascript or with Angular that I can accomplish this conditional binding?
You can filter your ng-repeat: https://docs.angularjs.org/api/ng/filter/filter
<div ng-model="tasks">
<div class="card" ng-repeat="abc in filteredData = (tasks | filter: {type==0}) track by $index">
<p> {{ abc.title }} </p>
</div>
</div>
Additionally, by saving the filtered data in a separate list you can display the next task like this:
<div>
<h4>Title of first card:</h4>
<p> filteredData[0].title </p>
</div>
Your data will automatically update as you "process" tasks.
The other answers helped point me in the right direction, but here's how I got it to work:
HTML
<input ng-model="inboxEditTitle" />
JS
$scope.filteredArray = [];
$scope.$watch('tasks',function(){
$scope.filteredArray = filterFilter($scope.tasks, {type:0});
$scope.inboxEditTitle = $scope.filteredArray[0].title;
},true); // the 'true' keyword is the kicker
Setting the third argument of $watch to true means that any changes to any data in my tasks array triggers the watch function. This is what's known as an equality watch, which is apparently more computationally intensive, but is what I need.
This SO question and answer has helpful commentary on a similar problem, and a great fiddle as well.
More on different $watch functionality in Angular
To update inboxTasks, you could use $watchCollection:
$scope.inboxTasks = [];
$scope.$watchCollection('tasks', function(newTasks, oldTasks)
{
for (i=0; i<newTasks.length(); i++)
{
if(newTasks[i].type == 0)
{
$scope.inboxTasks.append(tasks[i]);
}
}
});