Dynamic classes using ng-repeat in AngularJS - javascript

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}"

Related

Can I make an attribute appear only once in a vue v-for

I have an array of people with associated teams. I want to display all the people in the record, but I only want to display their team name once.
Meaning, if the v-for loop has encountered this particular team name, it should put it in a new temporary array to signify that it should be unique, then when it encounters that team name again, checks it through that temporary array and prevent it from showing again.
Sample HTML Code:
<div id="a-list">
<div v-for="person in people">{{person.Name}}, {{person.Team}}</div>
</div>
Sample Vue Code:
var crew = new Vue({
el: "#a-list",
data: {
people:
[ { "Name": "Richard","Team":"DMS"}, { "Name": "Mark","Team":"VV"}, { "Name": "Steve","Team":"VV"}, {"Name":"Koji","Team":"MZ"}, {"Name":"Jamie","Team":"VV"} ]
}
});
Expected Output:
Richard, DMS
Mark, VV
Steve,
Koji, MZ
Jaimie,
Is this possible to do directly from the v-for loop and not in the JS file?
Edited to show more data that are not sequential
Update: As Fabio has pointed out, the above scenario wouldn't make much sense unless the order of the team is arranged sequentially in the output first. So his answer is correct.
This could be a solution:
<div id="a-list">
<div v-for="(person,index) in people"> {{person.Name}}, {{ ((index == 0) || person.Team != people[index-1].Team) ? person.Team : '' }}</div>
</div>

How to display just one element with same id

This is my first question at stack overflow
i just wanted to know a simple solution for the following case
<div *ngFor="let d of w.event">
<div class="date" id="d.date" >
<p>
<span style="font-size:1.75em">{{d.date | date:'dd'}}</span>
<br>
<strong> {{d.date | date:'EEE'}}</strong>
</p>
</div>
the looped div can have the same id
I just want to display the first div with a particular date and ignore the rest
can this be achieved with CSS or JavaScript
You can't use the same id on two elements. It's one of the few restrictions on ids.
You can use a class:
<div class="show">Yes</div> <div class="show">No</div>
...and then show either the first or second by using index 0 or index 1 after getting a list of matching elements:
var list = document.querySelectorAll(".show");
list[0].style.display = "none"; // Hides the first one
// or
list[1].style.display = "none"; // Hides the second one
Some other thoughts:
1. Rather than using style.display as I did above, you might add a class that hides the element.
2. You might use separate ids (or classes) for the elements so you don't need to index, e.g.:
<div id="show-yes">Yes</div> <div id="show-no">No</div>
then
document.getElementById("show-yes").style.display = "none";
// or
document.getElementById("show-no").style.display = "none";
On all browsers in my experience, you can do the first thing above (with querySelectorAll) with your invalid HTML with a selector like "[id=show], but don't. Fix the HTML instead.
In your question update, you show:
<div *ngFor="let d of w.event">
<div class="date" id="d.date" >
...
You've said you're aware of the fact you can't have multiple elements with the same id, so why code that? You can easily give them unique ids:
<div *ngFor="let d of w.event; let i = index">
<div class="date" id="d.date{{i}}" >
...
First of all, in HTML ID is a unique selector so one ID can be associate with only one element. if you want to achieve your desired functionality you have to assign different id for both DIV. and use javascript to hide and show DIV
<div id="showYes">Yes</div> <div id="showNo">No</div>
If you want to show one at a time you can go with *ngIf , as it will show only one at a time
<div id="show" *ngIf='your_status'>Yes</div>
<div id="show" *ngIf='!your_status'>No</div>
After your question update , you can create custom filter that will only return unique date , so only first unique date will be shown
// CREATE A PIPE FILTER :
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({name: 'checkUniqueDate'})
export class UniqueDatePipe implements PipeTransform {
transform(dataArray) {
let dates = [];
return dataArray.filter(data => {
return if(dates.indexOf(data.date) === -1) {
dates.push(data.date);
return true;
} else {
return false;
}
});
}
}
// TEMPLATE SIDE :
<div *ngFor="let d of (w.event | checkUniqueDate )">
Add the date in class also, then you can try below code
.YOUR_DATE{
display:none
}
.YOUR_DATE:first-child{
displany:inline
}

ng-repeat with an if-statement in Angular

I have a website with a huge list of 30 articles. Until now this was like 300 lines of HTML code and with angularjs it is now like 10 lines. But some of these articles are special and so I need to be more specific what to show. Here is the HTML code:
<div id="illustratorcontent" ng-repeat="illustrator in illustrators">
<article id="illustratorcontent{{illustrator.ID}}">
<header>
<h3>{{illustrator.name}}</h3>
</header>
<div class="illustratorcontentext">
<p>This work is copyrighted and owned by {{illustrator.name}}...</p>
<p>Details of the copyright holder: </p>
<ul class="listplacing">
<li>Name: {{illustrator.name}}</li>
<li>Website: {{illustrator.name}}'s website</li>
<li>Website: {{illustrator.name}}'s {{illustrator.website2text}}</li>
</ul>
</div>
</article>
</div>
and the IllustratorController is:
function Illustrator ($scope){
$scope.illustrators = [
{
"ID" : "Ilus1",
"name" : "Ilus1",
"website" : "http://Ilus1page.com/"
},{
"ID" : "Ilus2",
"name" : "Ilus2",
"website" : "http://Ilus2page.com/"
}, ...
{
"ID" : "IlusX",
"name" : "IlusX",
"website" : "http://IlusXpage.com/"
"website2text" : "Twitter IlusX",
"website2url" : "http://twitterilusx.com/"
}
]
}
So my question is how can I show the existing information and leave the li out where no info is given.
So on this line:
<li>Website: {{illustrator.name}}'s {{illustrator.website2text}}</li>
how can I say that this line is just shown when the object parameter is given or not empty. I already looked for some ways. There is something called ng-if and ng-ui but that did not work out for me. Also there is a way to realize it with classes and hiding divs. But actually I would prefer to not generate them. And the website2url/website2text is just an example, there are more values than this.
Similar questions, but mine is a bit more special I believe:
angular ng-repeat with condition
https://stackoverflow.com/questions/28334600/paginating-angular-ng-repeat-with-nested-filtering
Hello create filter for to show the link of not like
<li ng-if="showSecondWebLink(illustrator)">....</li>
create function in your controller
$scope.showSecondWebLink = function(illustrator){
if(illustrator.website2url && illustrator.website2text ){
return true;
}
return false;
}
Just try with:
<li ng-if="illustrator.website2url && illustrator.website2text">...</li>

Conditionally bind data in AngularJS

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]);
}
}
});

dynamic id ng-repeat

I am trying to set a dynamic id to my div within my ng-repeat. Let me show an example.
<div id="$index" ng-repeat="repeat in results.myJsonResults">
<div id="$index" ng-click="saveID($index)" ng-repeat="subRepeat in results.myJsonResults.subresults">
</div>
My problem is, when I click on my child div, I want to get my parent id name, but looks like angular doesn't set the ID properly to the div. Is it possible to set a dynamic ID in this concept?
PS: I tried, in the past, create a counter method on my controller and set an index, but it turns out that angular only recognizes the last value of this ID instead.
To answer your question, try this:
<div id="{{$index}}" ...>
While the above should work, this might be not what you want really (!). Please note that this is rather rare with AngularJS to manipulate elements by referring those by id.
You should focus on your model, declarative describe UI and let AngularJS do the rest without doing low-level DOM manipulations "by hand".
A use case I can think of is associating <label> elements with their respective <input> elements, as seen in http://jsfiddle.net/YqUAp/
Applied there is a pkozlowski.opensource's method
<label for="{{ 'textField-' + $index }}">Option {{ $index }}</label>
<input type="text" id="{{ 'textField-' + $index }}" ng-model="field"/>
Though I'm unsure if this is the most effective method. (Even if only for readability)
<div id="{{$index}}" ...>
Works well, but you can also put a dynamic id with the field repeated if - for instance - subRepeat would have an id field.
That would be:
<div id="subRepeat{{subRepeat.id}}" ...>
That will put ids like subRepeat1, subRepeat2, ... on your repeated div
you need to hold the index in your objects
results = { myJsonResults : [
{ name: "abc", id: 1, subResults: [
{ subName: "123", id: 1 },
{ subName: "456", id: 2 }] }
{ name: "xyz", id: 2, subResults: [
{ subName: "789", id: 1 },
{ subName: "098", id: 2 }] }
] };
Also your repeat refers to results, which disconnects them, instead use thisResult:
<div id="thisResult.id" ng-repeat="thisResult in results.myJsonResults">
{{ thisResult.name }}
<div id="subResult.id" ng-click="saveID(subResult.id)" ng-repeat="subResult in thisResult.subResults"> {{ subResult.subName }} </div>
</div>

Categories