vuejs 3 v-if and v-else - html

hey I am building an spa app with vuejs 3 and I am stuck with v-if and v-else not working as intended
<div v-if="this.isScroll" #click = "toggleIsScroll" class ="spinner-container ">
<i ref = "icon" class = "bi bi-pause-fill icon"> </i>
<div ref="spinner" class ="spinner-border text-dark loader "> </div>
</div>
<div v-else #click = "toggleIsScroll" class ="spinner-container ">
<i ref = "icon" class = "bi bi-play-fill icon"> </i>
<div ref="spinner" class ="spinner-border text-dark loader visually-hidden"> </div>
</div>
the docs says that v-else needs to come right after v-if but I don't know if it works if v-if has children inside it
i have tried to create the v-if section and v-else section in separate components and use v-if and v-else on those as follows
<CustomComponent v-if="this.IsScroll"/>
<SecondCustomComponent v-else/>
but it just renders both components always so how should I go about this?
can you use v-else on followed v-if element even if the v-if one has child elements before the v-else?

When it comes to displaying components conditionally, it would be best to go with v-show since it won't re-render every time when you make it hide/show
<CustomComponent v-show="this.IsScroll"/>
<SecondCustomComponent v-show="!this.IsScroll"/>

v-if and v-else should work with children inside the element. Also you don't need to do v-if="this.isScroll" but just v-if="isScroll".

Assuming that IsScroll is set in the data, the this is not required.
Use v-show if you need to toggle something often, and use v-if if the condition is unlikely to change at runtime.
data: {
IsScroll: true
},
methods:{
toggleIsScroll(){
.....
}
}
<div v-if="isScroll" #click= "toggleIsScroll" class ="spinner-container ">
<i ref = "icon" class = "bi bi-pause-fill icon"> </i>
<div ref="spinner" class ="spinner-border text-dark loader "> </div>
</div>
<div v-else #click= "toggleIsScroll" class ="spinner-container ">
<i ref = "icon" class = "bi bi-play-fill icon"> </i>
<div ref="spinner" class ="spinner-border text-dark loader visually-hidden"> </div>
</div>

Related

How to make picklist editable in the target component in angular?

I am using p-picklist and I want to edit target list span data used for displaying the value.
I am using the source code: https://primefaces.org/primeng/showcase/#/picklist
How can I make product name editable in the target list.
<p-pickList
[source]="sourceProducts"
[target]="targetProducts"
sourceHeader="Available"
targetHeader="Selected"
[dragdrop]="true"
[responsive]="true"
[sourceStyle]="{'height':'30rem'}"
[targetStyle]="{'height':'30rem'}"
filterBy="name"
sourceFilterPlaceholder="Search by name"
targetFilterPlaceholder="Search by name"
>
<ng-template let-product pTemplate="item">
<div class="product-item">
<div class="image-container">
<img
src="assets/showcase/images/demo/product/{{product.image}}"
[alt]="product.name"
class="product-image"
/>
</div>
<div class="product-list-detail">
<h5 class="p-mb-2">{{product.name}}</h5>
<i class="pi pi-tag product-category-icon"></i>
<span class="product-category">{{product.category}}</span>
</div>
<div class="product-list-action">
<h6 class="p-mb-2">${{product.price}}</h6>
<span
[class]="'product-badge status-' + product.inventoryStatus.toLowerCase()"
>{{product.inventoryStatus}}</span
>
</div>
</div>
</ng-template>
</p-pickList>
Turning the <h5 class="p-mb-2">{{product.name}}</h5> into an input with [(ngModel)]="product.name" seem to work.
In order to check if the templated item is listed under target and not under source, you can check the element's position on the DOM; if it is inside .p-picklist-target, show the input, else show h5.
StackBlitz

how to show the div when clicked on the link and how to get value from input?

i have a django template that uses for loop to print comments i want to show the input field and a button when the edit link is clicked how do i do that.
and also when the edit button is pressed i wanna get the value from that specific input field. how do i do that?
{% for comment in comments %}
<div class="comment mdl-color-text--grey-700">
<header class="comment__header">
<img src="images/co1.jpg" class="comment__avatar">
<div class="comment__author">
<strong>{{comment.User_name}}</strong>
<span>{{comment.date}}</span>
</div>
</header>
<div class="comment__text">
{{comment.text}}
</div>
<!-- FAB button , class "mdl-button--fab"-->
<a href="javascript:delete_comment('{{comment.text}}','{{comment.blogslug}}')">
<button class="mdl-button mdl-js-button mdl-button--fab">
<i class="material-icons">delete</i>
</button>
</a>
<!--when this link is clicked bellow edit_comment div should appear -->
<a href="">
<button class="mdl-button mdl-js-button mdl-button--fab">
<i class="material-icons">edit</i>
</button>
</a>
<!-- and also when i click the edit button how can i get value from the input -->
<div id="edit_comment" style="visibility: hidden;">
<input type="text" name="edit_comment">
<button>edit</button>
</div>
</div>
{% endfor %}
so the problem is there are going to many other comments of this type because they are printed using a loop.
Firstly, why are you wrapping your button in an <a> tag? Unnecessary.
Get rid of visibility: hidden. Use display: none or a class like Bootstrap's d-none.
I suggest d-none because it allows you to add the class and remove it without worrying about the inherited display property, i.e. display: flex or display: block.
https://getbootstrap.com/docs/4.0/utilities/display/
Write a custom function with an event listener.
Inside your template loop:
<button class="mdl-button mdl-js-button mdl-button--fab" onclick="handleClick(this)">
<i class="material-icons">edit</i>
</button>
ASSUMING YOU HAVE REMOVED THE <a> TAG
JavaScript:
let handleClick = function(param) {
let editCommentDiv = param.parentNode.lastChild;
editCommentDiv.style.display = "none"
};
This is by no means the best way to do it. I highly suggest you use BootStrap's d-none class. Also, what you should actually do is assign an ID based on the for loop to both the button and to the div, i.e. id=editcommentdiv_{{ forloop.counter }} this way you won't need to use DOM to navigate to the last element to get the edit div, and can just target the div directly via ID.

Setting the div in angular to different color on click

So though my question might sound familiar the case is a bit different. I have a screen with multiple tasks. To show the tasks I am iterating via the data and my code looks something like
<div *ngFor="let task of tasks" class="scheduleContainer inline-block shadow">
<div id="myHeader" #myHeader class="activeHeader">
{{task.title}}
</div>
<div class="detailsBox">
<div class="row">
<div class="offset-md-1 col-md-auto">
Last Date:
</div>
<div class="col-md-auto">
{{task.lastDate}}
</div>
</div>
<div class="row">
<div class="offset-md-1 col-md-auto">
Duration:
</div>
<div class="col-md-auto">
{{task.duration}}
</div>
</div>
<div class="row">
<div class="offset-md-1 col-md-auto">
Total Runs:
</div>
<div class="col-md-auto">
{{task.totalRun}}
</div>
</div>
</div>
<div class="footer">
<a [routerLink]="['edit-scheduled-tasks']">edit schedule</a>
<a [routerLink]="['view-history-scheduled-tasks']">view history</a>
<a (click)="onClick()">enable task</a>
run now
</div>
</div>
<router-outlet></router-outlet>
Now when I click on the enabled task, I would like the color of that particular div to be changed. In my component, I tried something like
onClick() {
this.myHeader.nativeElement.style.background = 'red';
}
So this did change the color but it did not change the current task but rather some other task. Suggestions?
you can access myHeader from template so you can change the color something like this
<div id="myHeader" #myHeader class="activeHeader">
Change the color by myHeader variable
</div>
<button (click)="myHeader.style.background='red'">click</button>
or you can use a property with ngStyle like this
<div [ngStyle]="{'background-color':color}" >
Another way by ngStyle
</div>
<button (click)="color='red'">click</button>
or you can use a property to toggle class with ngClass
<div [ngClass]="{'red':isClicked}" >
Set class
</div>
<button (click)="isClicked=!isClicked">Toggle class</button>
Example toggle color of taskList by useing ngClass
template
<div *ngFor="let task of taskList"
[ngClass]="{'red':selectedTasks[task.id]}"
(click)="selectedTasks[task.id]= !selectedTasks[task.id]" class="task">
{{task.name}}
</div>
or you can use button to toggle the state
<div *ngFor="let task of taskList"
[ngClass]="{'red':selectedTasks[task.id]}"
class="task">
{{task.name}}
<button (click)="selectedTasks[task.id]= !selectedTasks[task.id]">toggle {{task.name}}</button>
</div>
if you want to set the state without toggle on click event just set
the state to true like this selectedTasks[task.id] =true
component
taskList =[
{id:1 , name:'Task 01'},
{id:2 , name:'Task 02'},
{id:3 , name:'Task 03'},
{id:4 , name:'Task 04'},
{id:5 , name:'Task 05'},
];
selectedTasks = {};
stackblitz demo
Not a clean way to do, but it still works. Send an index of selected element to onClick(i) and add the color to selected div. So that you don't mess with template reference.
html
<div *ngFor="let task of tasks; let i=index;" class="scheduleContainer inline-block shadow">
<div id="myHeader" #myHeader class="activeHeader">
{{task}}
</div>
<div class="footer">
<a (click)="onClick(i)">enable task</a>
</div>
</div>
component.ts
onClick(index: number) {
document.querySelectorAll('#myHeader')[index]
.style.background = 'red';
}
DEMO
It's not a good practice to manipulate DOM directly.
Angular: Stop manipulating DOM with ElementRef!
As an alternate, It's easy to bind inline style in your Angular templates using style binding.
Since you would like the color of that particular div to be changed. Use A boolean array:
component:
export class AppComponent {
name = 'Angular';
public styleArray=new Array<boolean>(10);
onClick(i)
{
this.styleArray[i]=true;
}
}
While Iterating pass index to onClick(i) to set particular index of array true and apply style dynamically
[style.color]="styleArray[i] ? 'green':'black'"
<div *ngFor="let task of tasks; let i=index" class="scheduleContainer inline-block shadow">
<div id="myHeader" [style.color]="styleArray[i] ? 'green':'black'" class="activeHeader">
{{task.title}}
</div>
........rest of the code
<a (click)="onClick(i)">enable task</a>
Live Demo

Cannot read property '0' of undefined in angular 6

I'm getting this error in console, Cannot read property '0' of undefined but still I'm getting the result as expected.
This is my HTML code
<div class="col-md-3">
<div class="slider-product">
<a href="#">
<img src="{{featureProducts[0].img_path}}" alt="Image" style="max-width:100%;">
<span class="tag">{{featureProducts[0].cat_name}}</span>
<div>
<a class="title">{{featureProducts[0].name}}</a>
</div>
<div class="price">
<i class="fa fa-inr"></i> {{featureProducts[0].min_price}}
</div>
</a>
</div>
</div>
Here is the function in typescript file
getFeatureProducts(){
this.httpClient.get(this.baseUrl+`/getFeatureProducts`)
.subscribe(
(data:any[]) => {
if(data.length) {
this.featureProducts = data;
}else{
this.featureProducts = null;
}
}
)}
featureProducts is declared inside the class as
featureProducts: any;
I know there is work around to this problem, like I can use multiple variables as below
in typescript
imgpath0 = this.featureProducts[0].imgPath;
And using this variable in html directly as
{{imgPath0}}
But this is not a better approach as I have lot of properties to be displayed in html and I cannot declare as many variables in ts.
Note: I don't want to loop using 'for' in html. Instead I need to fetch the properties as we usually do in JSON.
You can use *ngIf="featureProducts && featureProducts[0]" and prevent rendering the div until featureProducts object gets populated.
<div class="slider-product" *ngIf="featureProducts && featureProducts[0]">
<a href="#">
<img src="{{featureProducts[0].img_path}}" alt="Image" style="max-width:100%;">
<span class="tag">{{featureProducts[0].cat_name}}</span>
<div>
<a class="title">{{featureProducts[0].name}}</a>
</div>
<div class="price">
<i class="fa fa-inr"></i> {{featureProducts[0].min_price}}
</div>
</a>
</div>

routerLink from Object in Angular2

I'm trying to build a "Team Member" page from JSON data.
I can create the page with basic things like firstName, lastName, position.
Each team member has their own page with a little more information
What I cant figure out is how to include the team members url to my [routerLink].
My router link would look like this which I have setup in my routes
<a [routerLink]="['./glenn']">
And this is how I'm attempting to use it
<div class="team-members" *ngFor="let teammember of teammembers" >
<div class="clearfix">
<div class="col-xs-12 col-sm-8 col-md-8 col-lg-6 team-member member-item" style="cursor: pointer;">
<a [routerLink]="['./"{{teammember.firstName}}"']">
<div class="member-pic-holder">
<img alt="" src='{{teammember.photo}}' />
<div class="member-overlay"></div>
</div>
<h4>{{teammember.firstName}}<br/>{{teammember.lastName}} <span class="fa fa-arrow-right"></span></h4>
<p class="company-position">{{teammember.position}}</p>
</a>
</div>
</div>
</div>
Any thoughts on this one please?
It's also breaking when I'm trying to include the team-members photo
<img alt="" src='{{teammember.photo}}' />
However one thing at a time!
Thanks
GWS
You are using the {{ foo.bar }} binding incorrectly, the {{ }} syntax allows you to do one way binding, what you want is to use regular js expressions when binding to your objects properties.
When binding to an html element attribute, you can use the [attr.{id|href|etc}] binding, in your case, for the href of the image you can use:
<img alt="" [attr.href] = 'teammember.photo' />
And for the router, simply use [routerLink] = "[teammember.firstName]" (not sure why you need the ./, if you do need it, you could append it using a getter on your team member class, as shown bellow.
For your routes, you could do something along the lines of:
Team Member Class
export class TeamMember {
// ...properties and constructor
private memberUrl: string = "foobar"
get MemberRoute(){
return `./${this.memberUrl}`;
}
}
Template:
<div class="team-members" *ngFor="let teammember of teammembers" >
<div class="clearfix">
<div class="col-xs-12 col-sm-8 col-md-8 col-lg-6 team-member member-item" style="cursor: pointer;">
<a [routerLink]="[teammember.MemberRoute']">
<div class="member-pic-holder">
<img [attr.href] = 'teammember.photo' />
<div class="member-overlay"></div>
</div>
<h4>{{teammember.firstName}}<br/>{{teammember.lastName}} <span class="fa fa-arrow-right"></span></h4>
<p class="company-position">{{teammember.position}}</p>
</a>
</div>
</div>
</div>
Hope this helps!
can you try like this:
['./',teammember.firstName]
for img use
<img alt="" [src]="teammember.photo" />