storing user's dropdown value in vue - javascript

I have the code below that creates a dropdown menu (ignore the single quotes). The dropdown looks like this on my end.
undefined the logging output here. What can I do to make this work?

You have your #click event on a wrong element..
your #ckick events should be added to <a> tags, so you could pass a team_member.email directly to your method.
<div v-if="active" class="dropdown-content">
<a v-for="team_member in org_team_members"
:key="team_member.email"
#click="selectUsersInOrganization(team_member.email)>
{{ team_member.name }}
</a>
</div>
and the method should be:
selectUsersInOrganization(memberEmail) {
this.email = memberEmail
console.log(memberEmail)
}
p.s. if you would need more then just email, you could pass an entire team_member object like:
#click="selectUsersInOrganization(team_member)
so your method could have access to other members's properties
selectUsersInOrganization(member) {
this.email = member.email
this.name = member.name
console.log(member)
}

Related

Problem accessing values globally due to event listener's function scope

Hello guys and thanks for the help.
compileDescription here is a function I called in order to create the description page of an ecommerce. I received the products passed as an argument from an api, deconstructed them and they work.
The main problem is that I passed the details of the matching product into the description and inserted them into the HTML DOM within an event listener. However, I need to manipulate the data I inserted into the DOM within the event listener within another function. For that, the description variable needs to be accessible outside the event listener, but even logging it into the console within the compileDescription function but outside the event listener returns an empty string.
How do I make the values of description accessible both outside the event listener and outside the compileDescription function or in the global scope?
compileDescription(products){
const eyeView = [...document.querySelectorAll('.view')]; //returns 16 buttons
let description = '';
eyeView.forEach(viewBtn=>{
viewBtn.addEventListener('click', (event)=>{
const btnId = event.target.dataset.class; //works and returns the ID
const productFind = products.find(product=> product.id == btnId); //returns an object with different properties and values
description += `
<article class="overDescription">
<div class="descrContainer">
<div class="thisImage"> <img src="${productFind.image}" alt="">
</div>
<div class="productdetails">
<p class="prodescription">
${productFind.description}
</p>
<h5 class="price"> #${productFind.price}</h5>
<button class="bag-btn" data-id= "${productFind.id}">
<img src="./images/icons8_add_shopping_cart_100px.png"
width="16px" max-width= "18px" alt="add to cart"/>
Add to cart
</button>
</div>
</div>
</article>
`
showDescription.innerHTML = description; //the page displays correctly with all its necessary features
})
})
console.log(description); //returns an empty string
}

How to use template literals in a constructor in JavaScript

I'm building a To Do list app and have a question regarding OOP and JavaScript. I want to create a value in the Constructor that holds my taskBody which contains the HTML and template literal that will be assigned by either the input value or an eventual population from local storage. My goal is to re-use this HTML in two separate functions, but I'm stuck with the template literal.
class Task {
constructor() {
let taskValue //Initializing a variable
this.taskBody = `<div class="task">
<span>${taskValue}</span> //Template Literal
<span class="actions">
<a class="edit-button" title="Edit Task">Edit</a>
<button class="complete-button" title="Complete Task"><i class="fas fa-check"></i></button>
</span>
</div>`;
}
addTask = () => {
//Prevent empty task
if (this.input.value == "") {
this.setPlaceholder("Please Enter A Task");
return;
}
this.taskValue = this.input.value; //Trying to reassign taskValue to the input value
this.results.innerHTML += this.taskBody; //Trying to grab the HTML from the constructor and populate with taskValue able
ls.setLS(this.taskValue); //setting the Local Storage the Task Value, which works
};
}
I expect if I type "Stack Overflow" in the to-do list, "Stack Overflow" populates in the HTML and the Local Storage, however, it only populates in the Local Storage. The todo item is either undefined, null, or empty.
I've tried using this.taskValue, let taskValue = "", and let taskValue = null, but I get the results described above. Where am I going wrong, and more specifically, how can I reuse the HTML in different functions?
Here's a CodePen where you can see the issue:
Codepen
When you first instantiate the Task, the value of the this.taskBody is set as below:
<div class="task">
<span>undefined</span>
<span class="actions">
<a class="edit-button" title="Edit Task">Edit</a>
<button class="complete-button" title="Complete Task"><i class="fas fa-check"></i></button>
</span>
</div>
with undefined value, because at the moment of instantiation, the taskValue is undefined.
If your todo list items are added dynamically (which is the case), consider having a function which will enable dynamic replacement, like:
getTaskBody = item => `<div class="task">
<span>${item}</span>
<span class="actions">
<a class="edit-button" title="Edit Task">Edit</a>
<button class="complete-button" title="Complete Task"><i class="fas fa-check"></i></button>
</span>
</div>`;
and use it later in line 123, instead of:
this.results.innerHTML += this.taskBody;
do:
this.results.innerHTML += getTaskBody(this.taskValue);

Angular 5 w/Angular Material - Extracting an object property from ngFor to use in component

<div *ngFor="let player of players">
<h4 mat-line>{{player.firstName}} {{player.lastName}} - {{player.id}}</h4>
</div>
I'm doing a HTTP get call from my player.service.ts file, and then looping through the player object that gets returned, printing out the firstName, lastName and id properties in a massive player list.
I need to extract a specific player ID at a given point in the loop so that I can pass that down to a child Edit Player component that opens a modal with that specific player's information pre-filled in the form (using NgModel and a getbyId call to the API to get the player object). How would I go about doing this?
It looks like you're using #angular/material. If so, you should be able to use a click handler that loads the player data and opens up a dialog with their provided dialog service.
eg:
Template:
<div *ngFor="let player of players">
<h4 (click)="handlePlayerClick(player.id)"
mat-line>
{{player.firstName}} {{player.lastName}} - {{player.id}}
</h4>
</div>
Component:
constructor(private dialogService: MatDialog, private playerApi: PlayerApiService) { }
handlePlayerClick(playerId: string): void {
// potentially open a MatDialog here
this.playerApi.getById(playerId).subscribe((playerData: PlayerInterface) => {
const dialogConfig = {
data: {
playerData: playerData
}
} as MatDialogConfig;
this.dialogService.open(EditPlayerComponent, dialogConfig);
});
}
Documentation: https://material.angular.io/components/dialog/api
You'd want your child component to have a property like #Input() playerId: any; and then simply pass it in square brackets into the child tag like so:
<div *ngFor="let player of players">
<h4 mat-line>{{player.firstName}} {{player.lastName}} - {{player.id}}</h4>
<edit-player [playerId]="player.id"></edit-player>
</div>

One JavaScript confirm delete function for all foreach loop collection items

My PHP code contains a foreach loop that creates arrays (called a quotation) from a collection (called quotations). Each quotation contains a delete button with an id that is then fed (as a parameter) into a Confirm Delete JavaScript function.
The challenge is that the first quotation has the specified id so the JavaScript function works correctly. However, for the quotations that come after, there is no specified id so they are deleted immediately when their delete buttons are clicked with no Confirmation prompt.
I tried to vary it using getElementsByClassName but that didn't work.
Here is a snapshot of the foreach loop creating the variables:
#foreach($quotations as $quotation)
<article style="padding-bottom: 30px">
<div class="body w3-container">Service Request: - {{ $quotation->service_requests->service_name }}</div>
<h5>
<p><strong>Days to complete service: </strong>{{ $quotation->days_to_complete }}<br><strong>Fees to be charged for service (KES): {{ $quotation->fees_for_service }}</strong> <br><strong>Additional terms: </strong>{{ $quotation->additional_terms }}</p>
Edit Quotation
{{ Form::model($quotation, ["route" => ["service_requests.quotations.destroy", $quotation->service_requests->id, $quotation->id], "method" => "delete"]) }}
{{ Form::button("Delete Quotation!",["type"=> "submit", "class" => "pure-button button-xsmall", "id" => "revDel", "style"=>"background-color: #ff4747; color: #ffffff"]) }}
{{ Form::close() }}
</h5>
</article>
#endforeach
Here is a snapshot of the Confirm Deleted JavaScript function:
<script>
document.getElementById("revDel").onclick = function(f) {my2ndFunction(f)};
function my2ndFunction(f) {
if (confirm("Confirm delete!")) {
}
else {
f.preventDefault();
}
}
</script>
Here is a snapshot of how the variables look on my view
How can I change the JavaScript function or the foreach loop (or whatever needs to be changed) so that I get the confirm delete prompt for each listed quotation and not just the first one?
You have given all buttons the same id. id are supposed to be unique. the DOM will only recognise the first one. Either give them all names attributes and use the getElementsByName method to get a list of button object and itterate through the node list and set onclick or do this.
{{ Form::button("Delete Quotation!",["type"=> "submit", "class" => "pure-button button-xsmall", "onclick" => "my2ndFunction(event)", "style"=>"background-color: #ff4747; color: #ffffff"]) }}
make sure to remove this line
document.getElementById("revDel").onclick = function(f) {my2ndFunction(f)};

How to access nested object info for nested functions

I'm writing an app in Angular and have something like this:
$scope.items = [
{'name':'someName',
'title': 'someTitle',
'filter': function(item){
Filters.setTableTitle(this.title); //cannot get title
...
}
},
{'name':'someName',
'title': 'someTitle',
'filter': function(item){
Filters.setTableTitle(this.title);
...
}
}
];
An array of objects. Part of each object is a function, and inside the function I would like to call a function that grabs the title of that object itself in order to pass it into a greater scope for the rest of the app.
However, I can't grab the title for each object.
How would I access the title in order to use it here?
Thanks.
Update
Here is my HTML that uses (something very similar to) the code above. I'm using the code to create buttons.
<p ng-repeat="link in items">
<block class="button" href="{{link.URL}}" title="{{link.title}}">
<a class="hrefLink" href="{{link.URL}}" ng-click="Filters.setFilter(link.filter, link.title)">
{{link.name}}
</a>
</block>
</p>
The function held within the filter part of each object returns information that is then passed into Filters.setFilter() which updates the DOM.
Filters.setFilter()
service.setFilter = function(filter, title){
service.searchTerm = '';
$spMenu.hide(); //close nav if open
service.selectedFilter = filter;
service.setTableTitle(title); //this does the job
};
I've rearranged the ways these functions work, and now simply pass in the title to the different functions. This gets the job done for what I want, but still could never solve the initial question at hand--how would I access part of an object from inside the object?
I don't know Angular wery well so there could definitely be a much better soultion.
If you call the function yourself, just write it like this:
$scope.items[x].filter(item);
If it's called somewhere else and this doesn't contain the right object, you can enumerate all objects and bind the function to it:
for (var i = 0; i < $scope.items.length; ++i) {
var item = $scope.items[i];
item.filter = item.filter.bind(item);
}
Edit:
You have two choices. First you can just bind the function in HTML:
<p ng-repeat="link in items">
<block class="button" href="{{link.URL}}" title="{{link.title}}">
<a class="hrefLink" href="{{link.URL}}" ng-click="Filters.setFilter(link.filter.bind(link))">
{{link.name}}
</a>
</block>
</p>
Or you can edit the function to do it for you:
<p ng-repeat="link in items">
<block class="button" href="{{link.URL}}" title="{{link.title}}">
<a class="hrefLink" href="{{link.URL}}" ng-click="Filters.setFilter(link)">
{{link.name}}
</a>
</block>
</p>
service.setFilter = function(link){
service.searchTerm = '';
$spMenu.hide(); //close nav if open
service.selectedFilter = link.filter.bind(link);
};

Categories

Resources