Angular - Active state gets removed on next and back button - javascript

I've this quiz app, True/False statements in one question and T and F button against each statement. When I select the options and click on next/back button, active class gets removed from the previous one. As I'm a beginner, I can't think what I'm doing wrong. any help please?
Demo: https://stackblitz.com/edit/angular-ivy-4yczgp
<div class="qitem qclose"
[ngClass]="{'qclose-active': selectAnswer[j]?.dirty && (selectAnswer[j].select == 'false')}"
(click)="changeFalse(j , item)">
<i class="qitembox qclose-icon"></i>
</div>
<div class="qitem qtick"
[ngClass]="{'qtick-active': selectAnswer[j]?.dirty && (selectAnswer[j].select == 'true')}"
(click)="changeTrue(j , item)">
<i class="qitembox qtick-icon"></i>
</div>

You need to assign that object to model not as generic Demo
your assigns functions
changeFalse(i, option) {
console.log(option.id);
option.selectAnswer = { select: 'false', dirty: 'true' };
}
changeTrue(i, option) {
console.log(option.id);
option.selectAnswer= { select: 'true', dirty: 'true' };
}
your html
<div class="ccq">
<div class="qitem qclose" [ngClass]="{'qclose-active': item.selectAnswer?.dirty && (item.selectAnswer?.select == 'false')}" (click)="changeFalse(j , item)">
<i class="qitembox qclose-icon">F</i>
</div>
<div class="qitem qtick" [ngClass]="{'qtick-active': item.selectAnswer?.dirty && (item.selectAnswer?.select == 'true')}"(click)="changeTrue(j , item)">
<i class="qitembox qtick-icon">T</i>
</div>
</div>

Related

v-show does not work as I expected in my code

First I get data set from database using API and add a custom field called "isShare" and add value "false" in the initial state, then I iterate these data using v-for. Then I wanted to change the value of the "isShare" depending on the click event of another element.
I tired using v-show and v-if both. But in both cases, I couldn't get the result I wanted.
This is the code I used to get data from database and add a custom field called "isShare"
loadLessons() {
axios.get('/api/find/non_auth_get_lessons')
.then(({data}) => {
(this.lessons = data)
this.lesson_images = []
this.lessons.forEach(lesson => {
this.lesson_images.push(`${window.location.protocol + '//'
+ window.location.hostname}/lesson_covers/${lesson.lesson_cover_image}`)
lesson.isShare = false
});
},
(error) => {
this.loadLessons()
}
)
},
This is how I iterate data.
<carousel-3d class="slider_container lesson_container" :disable3d="true"
:space="320" :clickable="false" :controls-visible="true" style="height: 100%">
<slide class="card m-1 lma_view bg-white" v-for="(lesson, index) in lessons" :index="index" :key="index" >
<div class="card_img_wrapper" >
<img :src="lesson_images[index]" class="card-img-top" alt="..." v-
if="lesson.lesson_cover_image !== null" draggable="false">
</div>
<div class="card-body" >
<h5 class="card-title welcome_page_card_title" v-
html="$options.filters.truncate(lesson.lesson_title, 25, '...')"></h5>
<div class=" d-flex flex-column welcome_page_lesson_details">
<small class="card-text">Subject : <span class="text-muted">{{
lesson.subject | truncate(30, '...') }}</span></small>
<small class="card-text">Material Type : <span class="text-muted">{{
lesson.meterial_type === '1' ? 'Text based' : (lesson.meterial_type ===
'2' ? 'Video based' : 'Document based') }}</span></small>
<small class="card-text">Language : <span
class="text-muted">{{ lesson.language === '1' ? 'Sinhala' :
(lesson.language === '2' ? 'English' : 'Tamil') }}</span></small>
</div>
<hr/>
<div class="text-center">
View
<span class=""><i class="fas fa-share-alt share_icon"
#click="sharePopup(lesson, index)"></i></span>
<!--<div class="share_options" v-show="lesson.isShare"></div>-->
<div class="share_options">{{lesson.isShare}}</div>
<span class="badge pull-right" :class="lesson.access_type === '1' ?
'badge-success' :
(lesson.access_type === '2' ? 'badge-warning' : 'badge-info')">
{{ lesson.access_type === '1' ? 'Free'
: (lesson.access_type === '2' ? 'Restricted' : 'Paid') }}
</span>
</div>
</div>
</slide>
</carousel-3d>
When I click on the element witch has "share_icon" class I need to change the value of "isShare"
When I clicked on the "share_icon" it trigger the sharePopup function and changes the value of "isShare" but does not render
sharePopup(lesson, index){
lesson.isShare = !lesson.isShare;
}
You should access your array by the given index which is passed as parameter as follows:
sharePopup(lesson, index){
this.lessons[index].isShare = !lesson.isShare;
this.$set(this.lessons,index, this.lessons[index])
}
learn more about $set function here

VueJS Select only one element in a v-for

I'm using Vue v2
I'm trying to change only the properties of the selected element. See, when the response is marked after the click, it should change to a red color with a text that says 'Unmark'. And vice versa: if the button is clicked again (which now would say 'Unmark'), it should change to a green color and the text would be 'Mark'. Alas, it works.... Nevertheless, my code applies the change to every element inside the v-for; I only want that to happen to the selected element.
I've thought about using a Component to check if somethings changes, but first I'd like to see if there's a solutions for this. ANy help will be appreciated
Here's my code:
<div class="search-results">
<div class="activity-box-w" v-for="user in users">
<div class="box">
<div class="avatar" :style="{ 'background-image': 'url(' + user.customer.profile_picture + ')' }">
</div>
<div class="info">
<div class="role">
#{{ '#' + user.username }}
</div>
<div>
<div>
<p class="title">#{{ user.customer.name }}
#{{user.customer.lastname}}
</p>
</div>
</div>
</div>
<div class="time">
<input type="button" class="btn btn-sm btn-primary" v-on:click.prevent="markUser(user)" v-model="text"
v-bind:class="[{'green-border':notMarked}, {'red-border':marked}]" v-cloak v-if="!action"
:disabled="action"></input>
</div>
</div>
</div>
Now for the script:
new Vue({
el: '#engage-panel',
data: {
users: [],
mark: {'id' : '', 'marks' : ''},
text: 'Mark', //Migth change to Unmark later on
action: false,
marked: null,
notMarked: null,
},
methods:
{
markUser: function(user){
this.mark.id = user.id;
this.action= true;
Vue.http.headers.common['X-CSRF-TOKEN'] = $('meta[name="csrf-token"]').attr('content');
this.$http.put('/mark/user', this.mark).then(response => {
if(response.body === "marked")
{
this.mark.marks="true";
this.text = 'Unmark';
this.marked= true;
this.notMarked= false;
this.action= false;
}else{
this.mark.marks="false";
this.text = 'Mark';
this.marked= false;
this.notMarked= true;
this.action= false;
}
}).catch(e => {
this.action= false;
});
}
}
You can use $event.target on click if you just need to toggle css class.
fiddle here
But it's true that it's easier if a user has a status like marked = true/false for example, you just need to bind class like :
<input :class="{ 'green-border': user.marked, 'red-border': !user.marked }">
The issue my code applies the change to every element you met is caused by every user in v-for="user in users" uses one same object to indicates it is marked or not.
If your users data has one property like status to save current status (like unmark, mark etc), it is very simple, just change to next status when click mark button.
If your users data doesn't have that property, you need to create one dictionary, then save the users already clicked as key, the status for the user will be the value.
Below is one demo:
app = new Vue({
el: "#app",
data: {
users1: [{'name':'abc', 'status':'none'},
{'name':'xyz', 'status':'none'}],
users2: [{'name':'abc'}, {'name':'xyz'}],
selectedUsers: {}
},
methods: {
getNextStatusBaseOnRoute: function (status) {
if(status ==='marked') return 'marked'
let routes = {'none':'unmark', 'unmark':'marked'}
return routes[status]
},
markUser1: function (item) {
item.status = this.getNextStatusBaseOnRoute(item.status)
},
markUser2: function (item) {
let status = item.name in this.selectedUsers ? this.selectedUsers[item.name] : 'none'
// remember to use vue.$set when adding new property to one object
this.$set(this.selectedUsers, item.name, this.getNextStatusBaseOnRoute(status))
}
}
})
.marked {
background-color:green;
}
.unmark {
background-color:yellow;
}
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<h2>Case 1: </h2>
<div v-for="(item, index1) in users1" :key="'1'+index1">
<span>{{item.name}}:</span><span :class="[item.status]">{{item.status}}</span><button #click="markUser1(item)">Mark</button>
</div>
<h2>Case 2: </h2>
<div v-for="(item, index2) in users2" :key="'2'+index2">
<span>{{item.name}}:</span><span :class="[item.name in selectedUsers ? selectedUsers[item.name] : 'none']">{{item.name in selectedUsers ? selectedUsers[item.name] : 'none'}}</span><button #click="markUser2(item)">Mark</button>
</div>
</div>
For Vue3, you can also store the index of the selected element
<ul role="list" class="">
<li class="relative" v-for="(image, index) of images" :class="selectedImage == index? 'border-indigo-500 border-2': 'border-transparent'" >
<div #click="selectedImage = index" class="">
<img :src="image" alt="" class="object-cover pointer-events-none group-hover:opacity-75">
</div>
</li>
</ul>

onClick and onBlur events not working properly for draft-js div in react

I am passing onClick and onBlur to div but it doesn't work properly. onBlur event doesn't fires everytime. Here i am using react package Draft-js.
<div className="form-group" id="task-comments-frm">
<div>
<div onClick={this.CommentBoxClick.bind(this,'click')} onBlur={this.CommentBoxClick.bind(this,'blur')}>
<DraftEditor comment={this.state.newComment} handleCommentChange={this.handleCommentChange.bind(this)} />
</div>
<div className="pull-right text-right send-comments">
<button className="btn btn-primary" disabled={(this.state.newComment) ? "" : "disabled"} onClick={this.handleAddComment.bind(this)}>Send</button>
</div>
</div>
</div>
This is my function. I am trying to change classes with states.
CommentBoxClick(index){
if(index == 'click'){
this.setState({ addCommentBox : 'task-comments animated', CommentBoxHeight : '125px'})
}else if(index == 'blur'){
setTimeout(function() { this.setState({addCommentBox :'task-comments', CommentBoxHeight : '40px'}); }.bind(this), 200);
}
}

Disaible open uib-tooltip when any other tooltip is clicked

I am trying to create 3 tooltips in a page, Its not working , only one tooltip should be open on click anyother tooltip should be closed.
angular.module('ui.bootstrap.demo', ['ngAnimate', 'ui.bootstrap']);
angular.module('ui.bootstrap.demo').controller('TooltipDemoCtrl', function ($scope, $sce) {
$scope.tooltip = {
1: false,
2: false,
3: false
};
$scope.tooltipCloseActive = function(activeTooltip) {
console.log($scope.tooltip);
_.set($scope.tooltip, activeTooltip, true);
_.forOwn($scope.tooltip, function(value, key) {
console.log(value+'*************'+key);
if(activeTooltip !== key && value === true){
console.log('#'+key);
_.set($scope.tooltip, key, false);
$scope.triggerClickOnTooltip(key);
console.log(key);
}
});
};
$scope.triggerClickOnTooltip = function(id) {
setTimeout( function(){
angular.element(document.querySelectorAll('#'+id)).trigger('click');
}, 100);
};
});
HTML:
<div class="col-xs-10 col-sm-4">
<p id="1" tooltip-trigger="click"
uib-tooltip="I am a tooltip 1." ng-click="tooltipCloseActive('1');">
<span>tooltip 1:</span>
</p>
<br>
<p id="2" tooltip-trigger="click"
uib-tooltip="I am a tooltip 2." ng-click="tooltipCloseActive('2')">
<span >tooltip 2:</span>
</p>
<br>
<p id="3" tooltip-trigger="click"
uib-tooltip="I am a tooltip 3." ng-click="tooltipCloseActive('3')">
<span>tooltip 3:</span>
</p>
<br>
</div>
I have written code for multiple ui tooltips and want to close other tooltips when one of the tooltip is clicked.
It is going into infinite loop so when I am triggering click .
Just want to know how to make it work :
Possible Solution :
1) Differentiating the user click and trigger click :
I was not able to differentiate (I know how we can do in jquery but not in angular)
2) Use tooltip flags ( which I did it didn't work)
3) Upgrading ui bootstrap and use outsideClick (Which I cant afford doing in our application right now)
Is there any way I can solve this mystery of enable only one tooltip at a time.
Any help would be greatly appreciated.
PLUNKER : http://plnkr.co/edit/mjqGa26auDSY2G8ThAk4?p=preview
I think I was over thinking on trigger :
I have used is-open with wrong format due to which I was not able to get the
Working plunker using mutliple tooltips and enabling using tooltip-is-open http://plnkr.co/edit/gKqdoA3qK74XD1r9b5PR?p=info
HTML:
Tooltip Text
<br/> Toggle value: {{ tooltip.isTTOpen }}
</div>
<br/>
<br/>
<br/>
<br/>
<span tooltip-template="'myTooltipTemplate.html'" tooltip-trigger="none" tooltip-is-open="tooltip.isTTOpen2" tooltip-placement="top" ng-click="closeTooltip('isTTOpen2')">Tooltip Text</span>
<br/> Toggle value: {{ tooltip.isTTOpen2 }}
<br/>
<br/>
<br/>
<br/>
<span tooltip-template="'myTooltipTemplate.html'" tooltip-trigger="none" tooltip-is-open="tooltip.isTTOpen3" tooltip-placement="top" ng-click="closeTooltip('isTTOpen3')">Tooltip Text</span>
<br/> Toggle value: {{ tooltip.isTTOpen3 }}
JS:
$scope.tooltip = {
isTTOpen: false,
isTTOpen2: false,
isTTOpen3: false
};
$scope.closeTooltip = function(tooltip) {
console.log(tooltip);
for (var key in $scope.tooltip) {
if ($scope.tooltip.hasOwnProperty(key)) {
console.log(key + " -> " + $scope.tooltip[key]);
if (tooltip === key) {
$scope.tooltip[key] = true;
} else {
$scope.tooltip[key] = false;
}
}
}
};

How do I toggle an ng-show in AngularJS based on a boolean?

I have a form for replying to messages that I want to show only when isReplyFormOpen is true, and everytime I click the reply button I want to toggle whether the form is shown or not. How can I do this?
You just need to toggle the value of "isReplyFormOpen" on ng-click event
<a ng-click="isReplyFormOpen = !isReplyFormOpen">Reply</a>
<div ng-show="isReplyFormOpen" id="replyForm">
</div>
Basically I solved it by NOT-ing the isReplyFormOpen value whenever it is clicked:
<a ng-click="isReplyFormOpen = !isReplyFormOpen">Reply</a>
<div ng-init="isReplyFormOpen = false" ng-show="isReplyFormOpen" id="replyForm">
<!-- Form -->
</div>
If based on click here it is:
ng-click="orderReverse = orderReverse ? false : true"
It's worth noting that if you have a button in Controller A and the element you want to show in Controller B, you may need to use dot notation to access the scope variable across controllers.
For example, this will not work:
<div ng-controller="ControllerA">
<a ng-click="isReplyFormOpen = !isReplyFormOpen">Reply</a>
<div ng-controller="ControllerB">
<div ng-show="isReplyFormOpen" id="replyForm">
</div>
</div>
</div>
To solve this, create a global variable (ie. in Controller A or your main Controller):
.controller('ControllerA', function ($scope) {
$scope.global = {};
}
Then add a 'global' prefix to your click and show variables:
<div ng-controller="ControllerA">
<a ng-click="global.isReplyFormOpen = !global.isReplyFormOpen">Reply</a>
<div ng-controller="ControllerB">
<div ng-show="global.isReplyFormOpen" id="replyForm">
</div>
</div>
</div>
For more detail, check out the Nested States & Nested Views in the Angular-UI documentation, watch a video, or read understanding scopes.
Here's an example to use ngclick & ng-if directives.
Note: that ng-if removes the element from the DOM, but ng-hide just hides the display of the element.
<!-- <input type="checkbox" ng-model="hideShow" ng-init="hideShow = false"></input> -->
<input type = "button" value = "Add Book"ng-click="hideShow=(hideShow ? false : true)"> </input>
<div ng-app = "mainApp" ng-controller = "bookController" ng-if="hideShow">
Enter book name: <input type = "text" ng-model = "book.name"><br>
Enter book category: <input type = "text" ng-model = "book.category"><br>
Enter book price: <input type = "text" ng-model = "book.price"><br>
Enter book author: <input type = "text" ng-model = "book.author"><br>
You are entering book: {{book.bookDetails()}}
</div>
<script>
var mainApp = angular.module("mainApp", []);
mainApp.controller('bookController', function($scope) {
$scope.book = {
name: "",
category: "",
price:"",
author: "",
bookDetails: function() {
var bookObject;
bookObject = $scope.book;
return "Book name: " + bookObject.name + '\n' + "Book category: " + bookObject.category + " \n" + "Book price: " + bookObject.price + " \n" + "Book Author: " + bookObject.author;
}
};
});
</script>
If you have multiple Menus with Submenus, then you can go with the below solution.
HTML
<ul class="sidebar-menu" id="nav-accordion">
<li class="sub-menu">
<a href="" ng-click="hasSubMenu('dashboard')">
<i class="fa fa-book"></i>
<span>Dashboard</span>
<i class="fa fa-angle-right pull-right"></i>
</a>
<ul class="sub" ng-show="showDash">
<li><a ng-class="{ active: isActive('/dashboard/loan')}" href="#/dashboard/loan">Loan</a></li>
<li><a ng-class="{ active: isActive('/dashboard/recovery')}" href="#/dashboard/recovery">Recovery</a></li>
</ul>
</li>
<li class="sub-menu">
<a href="" ng-click="hasSubMenu('customerCare')">
<i class="fa fa-book"></i>
<span>Customer Care</span>
<i class="fa fa-angle-right pull-right"></i>
</a>
<ul class="sub" ng-show="showCC">
<li><a ng-class="{ active: isActive('/customerCare/eligibility')}" href="#/CC/eligibility">Eligibility</a></li>
<li><a ng-class="{ active: isActive('/customerCare/transaction')}" href="#/CC/transaction">Transaction</a></li>
</ul>
</li>
</ul>
There are two functions i have called first is ng-click = hasSubMenu('dashboard'). This function will be used to toggle the menu and it is explained in the code below. The ng-class="{ active: isActive('/customerCare/transaction')} it will add a class active to the current menu item.
Now i have defined some functions in my app:
First, add a dependency $rootScope which is used to declare variables and functions. To learn more about $roootScope refer to the link : https://docs.angularjs.org/api/ng/service/$rootScope
Here is my app file:
$rootScope.isActive = function (viewLocation) {
return viewLocation === $location.path();
};
The above function is used to add active class to the current menu item.
$rootScope.showDash = false;
$rootScope.showCC = false;
var location = $location.url().split('/');
if(location[1] == 'customerCare'){
$rootScope.showCC = true;
}
else if(location[1]=='dashboard'){
$rootScope.showDash = true;
}
$rootScope.hasSubMenu = function(menuType){
if(menuType=='dashboard'){
$rootScope.showCC = false;
$rootScope.showDash = $rootScope.showDash === false ? true: false;
}
else if(menuType=='customerCare'){
$rootScope.showDash = false;
$rootScope.showCC = $rootScope.showCC === false ? true: false;
}
}
By default $rootScope.showDash and $rootScope.showCC are set to false. It will set the menus to closed when page is initially loaded. If you have more than two submenus add accordingly.
hasSubMenu() function will work for toggling between the menus. I have added a small condition
if(location[1] == 'customerCare'){
$rootScope.showCC = true;
}
else if(location[1]=='dashboard'){
$rootScope.showDash = true;
}
it will remain the submenu open after reloading the page according to selected menu item.
I have defined my pages like:
$routeProvider
.when('/dasboard/loan', {
controller: 'LoanController',
templateUrl: './views/loan/view.html',
controllerAs: 'vm'
})
You can use isActive() function only if you have a single menu without submenu.
You can modify the code according to your requirement.
Hope this will help. Have a great day :)

Categories