AngularJS unique tab data for each tab - javascript

I am pretty close to having this app finished, but have one last hurdle. I am dynamically populating tabs and data via the WordPress Rest API and when I only had 2 tabs it worked wonderfully, but when I added tab 3 and 4 I ran into issues. When I click tabs 2-4 all tabs receive the "active" class instead of just the one that was clicked; thus also all 3 tabs content data also displays.
Here is the code:
var homeApp = angular.module('homeCharacters', ['ngSanitize']);
homeApp.controller('characters', function($scope, $http) {
$scope.myData = {
tab: 0
}; //set default tab
$http.get("http://bigbluecomics.dev/wp-json/posts?type=character").then(function(response) {
$scope.myData.data = response.data;
});
});
homeApp.filter('stripTags', function() {
return function(text) {
return text ? String(text).replace(/<[^>]+>/gm, '') : '';
};
});
<section class="characters" ng-app="homeCharacters" ng-controller="characters as myData">
<div class="char_copy">
<h3>Meet the Characters</h3>
<div class="char_inject" ng-repeat="item in myData.data" ng-show="myData.tab === item.menu_order">
<div class="copy_wrap">
<h3>{{ item.acf.team }}:</h3>
<h2>{{ item.acf.characters_name }} <span>[{{item.acf.real_name}}]</span></h2>
<p class="hero_type">{{ item.acf.hero_type }}</p>
<div class="description" ng-repeat="field in item.acf.character_description">
<p>{{field.description_paragraph}}</p>
</div>
Learn More
</div>
<div class="image_wrap">
<img src="{{ item.acf.homepage_full_image.url }}" />
</div>
</div>
</div>
<div class="char_tabs">
<nav>
<ul ng-init="ch.tab = 0">
<li class="tab" ng-repeat="item in myData.data" ng-class="{'active' : item.menu_order == myData.tab}">
<a href ng-click="myData.tab = item.menu_order">
<img src="{{ item.featured_image.source }}" />
<div class="tab_title_wrap">
<h3>{{ item.acf.characters_name }}</h3>
</div>
</a>
</li>
</ul>
</nav>
</div>
</section>
I would love any ideas! Thanks!

The code seems to work, see Fiddle. What are the values of menu_order? If they are the same for cases 2-4, then that would explain the behaviour.

Related

How to target an item inside ngFor with introjs

I need to use introjs to tour new features on an angular 6 app.
Inside the tour some step target items in ngFor in a child component of the component where I start introjs. The library is able to target child component html elements but seems to not be able to target element inside ngFor. The usage I want is simple I have a li that represent a card item I loop on as :
<div *ngFor="let source of sourcesDisplay; let idx = index" class="col-lg-5ths col-md-4 col-sm-6 col-xs-12">
<!-- item -->
<div class="item blue-light">
<div class="header">
<div class="categories">
<div class="truncat">{{source.categories.length > 0? source.categories : 'NO CATEGORY'}}</div>
</div>
<a routerLink="{{ organization?.name | routes: 'sources' : source.id }}" class="title">
<div class="truncat">{{source.name}}</div>
</a>
<div class="corner-ribbon blue-light" *ngIf="isARecentSource(source.createdAt)">New</div>
<div class="corner-fav" [class.is-active]="isSourceFavorite(source.id)"
(click)="toggleFavorite(source.id)"><em class="icon-heart-o"></em></div>
</div>
<a class="content" routerLink="{{ organization?.name | routes: 'sources' : source.id }}">
<div class="icon">
<em [ngClass]="source.icon? 'icon-'+source.icon : 'icon-cocktail3'"></em>
</div>
</a>
<div class="actions">
<ul class="buttons three">
<li class="icon-font" ><a (click)="openDashboardModal(source)"
[class.disable]="source.powerbi.length === 0" class="btn-effect"><em
class="icon-chart-bar"></em></a></li>
<li class="icon-font"><a
(click)="openDatalakeModal(source)" [class.disable]="source.datalakePath.length === 0"
class="btn-effect"><em class="icon-arrow-to-bottom"></em></a>
</li>
<li class="icon-font"><a routerLink="{{ organization?.name | routes: 'sources' : source.id }}"
class="btn-effect"><em class="icon-info-circle"></em></a></li>
</ul>
</div>
</div>
</div><!-- /item -->
And I want to target part of this card like a button for example as :
<li class="icon-font" id="step4{{idx}}">
<a (click)="openDashboardModal(source)" [class.disable]="source.powerbi.length === 0" class="btn-effect">
<em class="icon-chart-bar"></em>
</a>
</li>
and then in my component :
const intro = IntroJs.introJs();
intro.setOptions({
steps: [
{
element: document.querySelector('#step40'),
intro: 'Welcome to the Data Portal ! Explore and download data accross any division. Let\'s discover the new home page.'
}]})
intro.start();
Is there something I am doing wrong ? is introjs not able to do it at all ? is there another lib that can do it?
Thank you in advance
The sections generated by the *ngFor loop may not be rendered yet in the AfterViewInit event. To detect the presence of these elements, you should use #ViewChildren and monitor the QueryList.changes event.
In the template, define a template reference variable #step instead of id:
<li class="icon-font" #step>
<a (click)="openDashboardModal(source)" [class.disable]="source.powerbi.length === 0" class="btn-effect">
<em class="icon-chart-bar"></em>
</a>
</li>
In the code, retrieve the elements with #ViewChildren, and subscribe to the QueryList.changes event. You may have to convert the QueryList to an array to access an element with a specific index.
#ViewChildren("step") steps: QueryList<ElementRef>;
ngAfterViewInit() {
this.startIntroJs(); // Maybe already present
this.steps.changes.subscribe((list: QueryList<ElementRef>) => {
this.startIntroJs(); // Created later
});
}
private startIntroJs(): void {
if (this.steps.length > 40) {
const intro = IntroJs.introJs();
intro.setOptions({
steps: [
{
element: this.steps.toArray()[40].nativeElement,
intro: 'Welcome to the Data Portal...'
}
]
});
intro.start();
}
}

Angular HTTP request follow link to another endpoint

I am using ng-repeat to print a list of posts to the page via the WordPress REST-API. I am using Advanced Custom Fields on each post. From what I can see everything is working, but the post data is not showing in one container, yet it is displaying in another. I should also mention that this is set up like tabs. (user clicks a tab for a post and it displays that posts data)
Here is the code:
var homeApp = angular.module('homeCharacters', ['ngSanitize']);
homeApp.controller('characters', function($scope, $http) {
$scope.myData = {
tab: 0
}; //set default tab
$http.get("http://bigbluecomics.dev/wp-json/posts?type=character").then(function(response) {
$scope.myData.data = response.data;
});
});
homeApp.filter('toTrusted', ['$sce',
function($sce) {
return function(text) {
return $sce.trustAsHtml(text);
};
}
]);
HTML:
<section class="characters" ng-app="homeCharacters" ng-controller="characters as myData">
<div class="char_copy">
<h3>Meet the Characters</h3>
<div ng-repeat="item in myData.data" ng-bind-html="item.content | toTrusted" ng-show="myData.tab === item.menu_order">
<!--this is the section that will not display-->
<h3>{{ item.acf.team }}</h3>
<h2>{{ item.acf.characters_name }} <span>[{{item.acf.real_name}}]</span></h2>
<p class="hero_type">{{ item.acf.hero_type }}</p>
{{ item.acf.description }}
Learn More
</div>
</div>
<div class="char_tabs">
<!--if I put the identical {{}} in this section it WILL display-->
<nav>
<ul ng-init="myData.tab = 0" ng-model='clicked'>
<li class="tab" ng-repeat="item in myData.data" ng-class="{'active' : item.menu_order == myData.tab}">
<a href ng-click="myData.tab = item.menu_order">
<img src="{{ item.featured_image.source }}" />
<h3>{{ item.title }}</h3>
</a>
</li>
</ul>
</nav>
</div>
</section>
I should also mention that I use Ng-inspector, and it does show the data being pulled in. I can confirm this via the console. I have checked to ensure no css is in play; the div is totally empty in the DOM.
I appreciate all the help the GREAT angular community has shown!

Angular Js ng-init is not working properly inside ng-repeat while setting a variable

I have a ng-repeat where i am checking a if condition and at the end of each repeation of loop i am setting a variable to a value from ng-repeat
Here is my variable which i want to set inside the ng-repeat
$scope.prvSid = "";
Here is my ng-repeat code
<ul class="chats" ng-repeat="chatData in chatboxData">
<li ng-class="{ 'out': chatData.sender_id == user_id , 'in': chatData.sender_id != user_id }">
{{ prvSid }}
{{ chatData.sender_id }}
<span ng-if=" prvSid != chatData.sender_id ">
<img class="avatar" alt="" src="{{ url('default/img/user_default_logo.jpg') }}" />
</span>
<div class="message">
<span class="body"> {{ chatData.message }} </span>
</div>
<span ng-init="prvSid = chatData.sender_id"></span>
{{ prvSid }}
</li>
</ul>
The problem which i am facing here is that whenever i print these values inside the ng-repeat then prvSid and chatData.sender_id is printing the same id the value of chatData.sender_id even for the first iteration and that's why this
<span ng-if=" prvSid != chatData.sender_id ">
condition is not working and my image is not displaying because for the first iteration the condition should be true because prvSid is "" and chatData.sender_id has some id in it
The purpose of this is to
Henry Zou the purpose is to not show profile picture for two or more messages submitted by same user (if the sender_id is same)Then dont display the profile image.When the new message comes from another sender which means sender_id is different then show the profile image
at first the prvSid will be null so it will show the image because condition will not match at the end of each iteration i will set the prvSid to the current iteration sender_id to match this prv_id with sender_id in the next iteration
i am also getting messages after two seconds and then i am adding the new records in the chatboxdata if they dont exist in it
$scope.getlasttwosecMsgs = function(user_id_chat,chat_id,user,chatboxData,is_new) {
$http.get(url+'/getlasttwosecMsgs/'+chat_id).success(function(data){
angular.forEach(data, function(value, key) {
var exists = $scope.containsObject(value,chatboxData);
if (!exists) {
$scope.chatboxData.push(value);
console.log($scope.chatboxData);
};
});
});
}
$scope.containsObject = function(obj, list) {
var i;
for (i = 0; i < list.length; i++) {
if (angular.equals(list[i], obj)) {
return true;
}
}
return false;
};
This new code snippet will check current userID against a list of users in the chatbox. For all users that's not the current user, show content.
approach #1 (preferred)
angular
.module('app', [])
.controller('myCtrl', function(){
var vm = this;
var prvSid = null;
vm.chatboxData = [{id:1},{id:1},{id:2},{id:3}];
vm.chatboxData.forEach(function(chatbox){
if(prvSid !== chatbox.id){
chatbox.showIcon = true;
}
prvSid = chatbox.id;
});
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.js"></script>
<div ng-app="app">
<div ng-controller="myCtrl as vm">
<ul class="chats" ng-repeat="chatData in vm.chatboxData">
<li>
<span ng-if="chatData.showIcon ">
ICON
</span>
{{::chatData.id}}
</li>
</ul>
</div>
</div>
approach #2
angular
.module('app', [])
.controller('myCtrl', function(){
var vm = this;
vm.prvSid = null;
vm.chatboxData = [{id:1},{id:1},{id:2},{id:3}];
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.js"></script>
<div ng-app="app">
<div ng-controller="myCtrl as vm">
<ul class="chats" ng-repeat="chatData in vm.chatboxData">
<li>
<span ng-if="chatData.showIcon ">
ICON
</span>
{{::vm.prvSid}} {{::chatData.id}}
<span ng-init="chatData.showIcon = (vm.prvSid !== chatData.id)"></span>
<span ng-init=" vm.prvSid = chatData.id"></span>
</li>
</ul>
</div>
</div>
approach #3 (without using controllerAs syntax)
angular
.module('app', [])
.controller('myCtrl', function($scope){
var prvSid = null;
$scope.chatboxData = [{id:1},{id:1},{id:2},{id:3}];
$scope.chatboxData.forEach(function(chatbox){
if(prvSid !== chatbox.id){
chatbox.showIcon = true;
}
prvSid = chatbox.id;
});
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.js"></script>
<div ng-app="app">
<div ng-controller="myCtrl">
<ul class="chats" ng-repeat="chatData in chatboxData">
<li>
<span ng-if="chatData.showIcon ">
ICON
</span>
{{::chatData.id}}
</li>
</ul>
</div>
</div>
OLD ANSWER
In the video AngularJS MTV Meetup: Best Practices (2012/12/11), Miško
explains "..if you use ng-model there has to be a dot somewhere. If
you don't have a dot, you're doing it
wrong.."
ng-repeat creates an inherited scope (think javascript prototype) for each item.
This code below will create $scope.prvSid for each item in the array and the value will always be chatData.sender_id.
<span ng-init="prvSid = chatData.sender_id"></span>
If you meant it to only have 1 instances of prvSid id, then what you'll need to do is initialize prvSid variable at the parent scope first (or use the dot(.) rule)
<div ng-init="prvSid = null"> <!-- initializing prvSid id # parent scope -->
<ul class="chats" ng-repeat="chatData in chatboxData">
<li ng-class="{ 'out': chatData.sender_id == user_id , 'in': chatData.sender_id != user_id }">
{{ prvSid }}
{{ chatData.sender_id }}
<span ng-if=" prvSid != chatData.sender_id ">
<img class="avatar" alt="" src="{{ url('default/img/user_default_logo.jpg') }}" />
</span>
<div class="message">
<span class="body"> {{ chatData.message }} </span>
</div>
<!-- this will now update the parent scope's prvSid instead of creating a new one for each item in the array -->
<span ng-init="prvSid = chatData.sender_id"></span>
{{ prvSid }}
</li>
</ul>
</div>

How to get parent's parent's parent's id in Angularjs

I have a problem. I need to know how to get grandparent's ID in AngularJS.
I need "{{parent}}" to become "grand-parent".
(it should be <div id="me-and-my-grand-parent">)
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
var pid = document.getElementsByClassName("i-am-a-child");
var pid = this.parentNode.id;
if (this.parentNode&&this.parentNode.id)
var pid=this.parentNode.id;
$scope.parent = var pid;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<div id="grand-parent{{$index}}" ng-repeat="item in items">
<div>
<div>
<div id="me-and-my-{{parent}}" class="i-am-a-child">
</div>
</div>
</div>
</div>
</div>
My actual code
<li ng-repeat="project in projects" ng-class="{active: project.childToggle, '': !project.childToggle,hasChild: project.children.length > 0 }" ng-dblclick="childToggleCt(project)" id="project-{{$index}}">
<div class="project-overview">
<header class="clearfix flip-area">
<span ng-if="!project.inCart" class="status dropdown-button warning pull-left" id="id-{{ParentIdShow}}" data-intro="Status bar" data-position="right">Pending</span>
And for now JS was like tis :
$scope.ParentIdShow = function(obj)
{
alert(obj.target.parentNode.parentNode.parentNode.id);
}
The answer to these types of "parent of my parent of my parent of my..." is to use the controller as syntax. Read more about it here. In short, it lets you do stuff like
<div ng-controller="ctrl1 as first">
<div ng-controller="ctrl2 as second">
...
<div ng-controller="ctrlN as Nth">
<div ng-repeat="i in arr">
{{first.property}}
{{second.otherProperty}}
{{Nth.nProperty}}
Note how you dont need any parent calls.

Nested Angular Switches?

I'm building a MEAN SPA and the current page I'm working on displays the users in the database. I'm pretty new to Angular so I'm still trying to wrap my head around it.
I have a parent container of which the content is controlled by an <ng-switch> and switches to show the relevant content depending on whether the user has clicked 'view all' or 'add new'. This works fine.
What I'm aiming to do now is when the user clicks on a user that's displayed in 'view-all', I want the content to switch to a view containing that users details where they can then go and edit the profile etc. What would be the best way to achieve this?
My HTML is set up like so:
Main staff view
<div class="staff" ng-controller="staffController">
<div class="side-menu">
<h2>Staff</h2>
<ul>
<li><a ng-click="tab='view-all'"><i class="fa fa-user"></i> View All</a></li>
<li><a ng-click="tab='add-new'"><i class="fa fa-plus"></i> Add New</a></li>
</ul>
</div>
<div class="page-content" ng-switch on="tab">
<div ng-switch-when="view-all" class="tab-content">
<staff-view-all></staff-view-all>
</div>
<div ng-switch-when="add-new" class="tab-content">
<staff-add-new></staff-add-new>
</div>
</div>
</div>
Directives:
.directive('staffViewAll', function () {
return {
restrict: 'E',
templateUrl: 'partials/staff/view-all.ejs'
}
})
.directive('staffAddNew', function () {
return {
restrict: 'E',
templateUrl: 'partials/staff/add-new.ejs'
}
})
view-all.ejs
<h2>View all staff</h2>
{{ users.length }} users in system
<ul>
<li ng-repeat="user in users"> <!-- click this and you see the singular view -->
<img ng-src="{{user.avatar}}?dim=100x100" />
<h3>{{user.username}}</h3>
<h4>{{user.email}}</h4>
</li>
</ul>
Use another ng-switch to switch to detailed view for the selected user.
Something like this: jsfiddle
<div ng-switch-when="list">
<ul>
<li ng-repeat="fruit in fruits">
{{fruit}}
</li>
</ul>
</div>
<div ng-switch-when="details">
<p>Details for {{ selectedFruit }}</p>
Back to list
</div>
Controller:
$scope.showDetail = function (fruit) {
$scope.selectedFruit = fruit;
$scope.moduleState = 'details';
}
$scope.showList = function()
{
$scope.moduleState = 'list';
};

Categories

Resources