How to use template literals in a constructor in JavaScript - 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);

Related

storing user's dropdown value in vue

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

How can i access each element in a ngFor with a function?

I created a simple note app which for loops through an array of objects which holds note data. I have a button which opens up the note for edit by returning true when clicked returning false when clicked again.
The problem is when clicked all the notes open up in edit mode because the boolean variable is shared.
Question is: "How can i access that specific note where i clicked the edit button?"
HTML:
<div class="view-notes" *ngFor="let note of notes; index as i">
<div class="tools">
<i class="fa fa-edit" (click)="openNote(i)"></i>
<i (click)="deleteNote(i)" class="fa fa-trash"></i>
</div>
<input [disabled]="!onEdit()" type="text" [(ngModel)]="note.title" #title>
<p *ngIf="!onEdit()">{{note.date | noteDate}}</p>
<textarea type="text" *ngIf="onEdit()" name="body" id="" [(ngModel)]="note.note"></textarea>
</div>
<h1 class="error" *ngIf="noNotes()">No notes to be displayed.</h1>
The functions:
openNote(i: number) {
if (this.count == 0) {
// open
this.edit = true; this.count++;
} else {
//save
this._noteService.updateNote(i, this.notes[i].title, this.notes[i].note);
//close
this.count--;
this.edit = false;
}
}
onEdit() {
return this.edit;
}
In your title, and in your own words, you're asking:
How can i access each element in a ngFor with a function?
and
"How can i access that specific note where i clicked the edit button?"
To answer this question directly -- you can access the scoped variable that's created implicitly within the loop.
This is to say, the *ngFor="let note of notes" creates a note variable scoped to your to each iteration of the loop.
You're already doing this where your ngModel binding is in your <input> and <textarea> for the note title/text respectively.
You can also pass that variable to functions, just as you do with bindings.
So, you could use the note variable to pass to a function, which will be called using whichever note is clicked. e.g. openNote(note)
// HTML
<div class="view-notes" *ngFor="let note of notes">
<div class="tools">
<i class="fa fa-edit" (click)="openNote(note)"></i> // passes the current note you clicked
...
// TS
openNote(note: any) {
// do something with this note, here
}
That's your question answered (at least what your question is directly asking from the title).
However, your question appears to be asking more than one thing, namely to do with showing/hiding specific notes (i.e. the ones that were clicked). Please try and keep your questions focused, or at least asking the same question in your post as what the title says :)
I'll answer what I think you're asking, looking at the problem you've described in your question; which I think would be:
"How can I show just the note I wish to edit; and save/close it when I edit click again, or edit a different note?"
Regarding the show/hide of specific notes; as already pointed out, you're just showing/hiding all notes based on a single boolean (this.edit returned by onEdit()) variable, which will have the same effect on all your notes (showing/hiding them all at the same time).
Seeing as you have access to each note inside your notes array within your *ngFor loop, you could keep a record of which note is currently displayed, using a property on your component:
export class SomeComponent {
currentlyShownNote: any; // use this to store the reference of the note currently open
// rest of component code...
}
Then, you can simply check in your HTML if the currentlyShownNote is this particular one, and show/hide based on this check:
<textarea type="text" *ngIf="currentlyShownNote === note" ...>
Then, create a showHideNote function in your component to set which note is being shown when you click it:
// HTML
<div class="view-notes" *ngFor="let note of notes; index as i">
<div class="tools">
<i class="fa fa-edit" (click)="showHideNote(note)"></i>
...
// TS
showHideNote(note: any) {
// if there is a currently shown note, save it
if (this.currentlyShownNote) {
const currentNote = this.currentlyShownNote;
this._noteService.updateNote(this.notes.indexOf(currentNote), currentNote.title, currentNote.note);
}
if (this.currentlyShownNote == note) {
this.currentlyShownNote = null;
} else {
this.currentlyShownNote = note;
}
}
Or, rather than using the reference to each note variable, you could simply use the index (index as i) in the array to track which note is shown (similar to how you're deleting the notes):
// HTML
<div class="view-notes" *ngFor="let note of notes; index as i">
<div class="tools">
<i class="fa fa-edit" (click)="showHideNote(i)"></i>
<i (click)="deleteNote(i)" class="fa fa-trash"></i>
</div>
<input [disabled]="shownNoteIndex !== i" type="text" [(ngModel)]="note.title" #title>
<p *ngIf="shownNoteIndex !== i">{{note.date | noteDate}}</p>
<textarea type="text" *ngIf="shownNoteIndex === i" name="body" id="" [(ngModel)]="note.note"></textarea>
</div>
// TS
shownNoteIndex?: number; // property to hold the currently shown note index
showHideNote(noteIndex: number) {
// if there is a currently shown note, save it
if (this.shownNoteIndex >= 0) {
const i = this.shownNoteIndex;
this._noteService.updateNote(i, notes[i].title, notes[i].note);
}
if (this.shownNoteIndex == noteIndex) {
this.shownNoteIndex = null;
} else {
this.shownNoteIndex = noteIndex;
}
}
BONUS: For coming back round full circle, you can create another function in your component to make your shownNoteIndex === i and shownNoteIndex !== i (or even your currentlyShowNote === note) checks even more succinct:
// HTML
<div class="view-notes" *ngFor="let note of notes; index as i">
<div class="tools">
<i class="fa fa-edit" (click)="showHideNote(i)"></i>
<i (click)="deleteNote(i)" class="fa fa-trash"></i>
</div>
<input [disabled]="!isNoteShown(i)" type="text" [(ngModel)]="note.title" #title>
<p *ngIf="!isNoteShown(i)">{{note.date | noteDate}}</p>
<textarea type="text" *ngIf="isNoteShown(i)" name="body" id="" [(ngModel)]="note.note"></textarea>
</div>
// TS
// if you're using the note index
isNoteShown(noteIndex: number) {
return this.shownNoteIndex === noteIndex;
}
// or
// if you're using the note reference
isNoteShown(note: any) {
return this.currentlyShownNote === note;
}
Your edit flag should be a number representing which note should be edited. Then you can check if the item in the list matches the edit number and display edit for just that one.
try calling the function with doSomething($event) in the html
and define that function in ts file as doSomething(event){}

How to get the ID of a button that is clicked and then use it as a Foreign Key for something else? (ASP.NET MVC)

Currently i have a simple program that receives user text input. The inputted text is then used as a heading for bootstrap collapsible's, these headings are referred to as 'Categories' within the context of my program. The user is able to click on any one of these categories and assign sub-categories (items) to them. However, i need to be able to record the ID of the category clicked (the category to which the user wishes to add sub-categorie(s) to), and then record this ID in a text file, as the foreign key of the sub-categorie(s) added.
It will work something like this:
1.) User clicks category 'Homework' (Has an ID (Primary Key) of 0)
2.) User adds several sub-categories, namely;
'Math Homework' (Has an ID (Primary Key) of 0, and Foreign key of 0)
'Biology' (Has an ID (Primary Key) of 1, and Foreign key of 0)
'French' (Has an ID (Primary Key) of 2, and Foreign key of 0)
3.) The text file in which the sub-categories are recorded will have the following format:
Math Homework,0,0
Biology,1,0
French,2,0
Note: As it stands, my text file is currently recording the first two parts of the above example, eg: it currently records: Math Homework,0 ... However i am struggling to link a Foreign Key.
Below is the view i am using to display the collapsibles:
#Model.CategoryList.result
#if (Model.CategoryList.result == "")
{
int count = 0;
foreach (String dataLine in Model.CategoryList.userData)
{
string countString = count.ToString();
string target = "dataLine" + countString;
string trigger = "#" + target;
<p>
<a data-toggle="collapse" href="#trigger" role="button" aria-expanded="false" aria-controls="collapseExample">
#dataLine.Split(Model.CategoryList.delimiterChar)[0]
</a>
<button class="btn" onclick="location.href='#Url.Action("Items", "Items")'" id="#dataLine.Split(Model.CategoryList.delimiterChar)[1]"><i class="fas fa-plus secondaryPlusIcon" id="#dataLine.Split(Model.CategoryList.delimiterChar)[1]"></i></button>
<button class="btn" id="#dataLine.Split(Model.CategoryList.delimiterChar)[1]"><i class="far fa-edit secondaryEditIcon" id="#dataLine.Split(Model.CategoryList.delimiterChar)[1]"></i></button>
<button class="btn" id="#dataLine.Split(Model.CategoryList.delimiterChar)[1]"><i class="far fa-trash-alt secondaryDeleteIcon" id="#dataLine.Split(Model.CategoryList.delimiterChar)[1]"></i></button>
</p>
foreach (String dataLineItem in Model.ItemList.userDataItems)
{
<div class="collapse" id="#target">
<div class="card card-body w-25 p-3 collapsible" id="#dataLine.Split(Model.CategoryList.delimiterChar)[1]">
#dataLineItem.Split(Model.CategoryList.delimiterChar)[0]
</div>
</div>
}
count++;
}
}
As an additional side note; onclick="location.href='#Url.Action("Items", "Items")'" is responsible for redirecting the user to a new view where he/she is able to enter the desired sub-categories.
And id="#dataLine.Split(Model.CategoryList.delimiterChar)[1]" assigns a unique accumulating integer ID, starting from 0, to each new category that is added. This would be the Primary Key for the category headers.
Lastly, here is the controller i have used to initialize the values seen within the view:
public ActionResult Index()
{
CategoryItemViewModel CIVM = new CategoryItemViewModel();
CIVM.ItemList = GetItemModel();
CIVM.CategoryList = GetCategoryModel();
return View(CIVM);
}
public Category GetCategoryModel()
{
var dataFile = Server.MapPath("~/App_Data/Category.txt");
Category cModel = new Category()
{
result = "",
delimiterChar = new[] { ',' },
userData = System.IO.File.ReadAllLines(dataFile) //Category Text File
};
return cModel;
}
public Item GetItemModel()
{
var dataFileItems = Server.MapPath("~/App_Data/Item.txt");
Item iModel = new Item()
{
userDataItems = System.IO.File.ReadAllLines(dataFileItems) //Items Text File
};
return iModel;
}
Hopefully i have provided sufficient information with the code and context mentioned above. I imagine one would use jQuery/AJAX/Json/JS etc to perform such a function, however i am quite lost on where to begin.
Any help is greatly appreciated. Thank you so much!

Content Editable = true, but only takes one value at a time

I'm working on a simple project where I need to make an item in a list editable and then update a JS item to store this.
I'm using Content Editable = True, and it works when I comment out my handleItemEdit function, but when I turn it on, I can only insert one character at a time, forcing me to keep clicking to edit.
Clearly this problem stems from my function, but I can't seem to figure out why.
//Responsible for listening for an edit and updating my object with the new text.
function handleEditItem() {
$('.js-shopping-item').on('input', function(event) {
const itemIndex = getItemIndexFromElement(event.currentTarget); //assigning the index of the the editted item to itemIndex
const updatedItem = STORE.items[itemIndex];
updatedItem.name = event.currentTarget.innerHTML;
renderShoppingList();
});
}
//Returns the index of an Item in the Store
function getItemIndexFromElement(item) {
const itemIndexString = $(item)
.closest('.js-item-index-element')
.attr('data-item-index');
return parseInt(itemIndexString, 10);
}
//Function responsible for returning the template HTHML to insert into the html.
function generateItemElement(item) {
let itemIndex = STORE.items.indexOf(item);
return `
<li class="js-item-index-element" data-item-index="${itemIndex}">
<span contentEditable='true' class="shopping-item js-shopping-item ${item.checked ? 'shopping-item__checked' : ''}">${item.name}</span>
<div class="shopping-item-controls">
<button class="shopping-item-toggle js-item-toggle">
<span class="button-label">check</span>
</button>
<button class="shopping-item-delete js-item-delete">
<span class="button-label">delete</span>
</button>
</div>
</li>`;
}

How to have selected class in update page in vue js

In Vuejs 2.0 I've a following set of data:
const tags = {
Investor:[
{display:"Mutual Fund", value:"Investor - Mutual Funds"},
{display:"Insurance", value:"Investor - Insurance"},
{display:"FII", value:"Investor - FII"},
],
Research:[
{display:"Research - Tier I", value:"Research - Tier I"},
{display:"Research - Tier II", value:"Research - Tier II"},
]
}
I'm having following set of buttons which shows these tags:
<div class="col-sm-4 border-right">
<div>
<button v-for="(obj, key) in tags"
:key="key"
#click.prevent="currentTag = key"
class="btn btn-primary btn-xs">
{{key}}
</button>
</div>
</div>
<div class="col-sm-6">
<div>
<button v-for="tag in tags[currentTag]"
:key="tag"
class="btn btn-xs"
:class="tagClass(tag)"
#click.prevent="selectedTag = tag">
{{tag.display}}
</button>
</div>
</div>
To get the selected tags I'm having a variable named:
currentTag: '',
selectedTag: '',
Now i'm having tagClass(tag) to toggle the class:
tagClass(tag){
return {
'btn-warning': this.selectedTag === tag,
'btn-primary': !(this.selectedTag === tag)
}
},
Now while getting the update page I'm putting values in current tag and selectedTag like this:
this.currentTag = response.data.contact.parentTag
this.selectedTag = response.data.contact.selectedTag
Now I'm able to view the child tags selected from the parent but I'm unable to have selected class in it. I want the data which is being set to seletedTag should have class btn-warning
In backend PHP I'm calculating and passing the value as
$selectedTag['value'] = $contact->tag;
$tags = explode('-', $contact->tag);
$contact->parentTag = $tags[0];
$selectedTag['display'] = $tags[1];
$contact->selectedTag = $selectedTag;
The issue here is that the tagClass method is checking to see if the tag is equal to the selectedTag. Since tag and selectedTag are objects, when you set it manually, selectedTag is never going to be equal to any of your tags. Instead, look for the tag that you have locally that matches the tag that you received from the server.
Wherever you are doing this
this.currentTag = response.data.contact.parentTag
this.selectedTag = response.data.contact.selectedTag
Change it to this
this.currentTag = response.data.contact.parentTag
const selectedTag = response.data.contact.selectedTag
this.selectedTag = this.tags[this.currentTag].find(t => t.value === selectedTag.value)
Example.

Categories

Resources