How to have selected class in update page in vue js - javascript

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.

Related

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

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!

Generating dynamic html cards from a javascript array

I would like to display some card elements in HTML. I would like to get the variables of the card element from a javascript array.(Such as title etc..).
The card element number will also depend on the Javascript array size. I have looked at other questions such as generating dynamic tables but as I have a long customized html code block, it doesn't work.
This is for a website that I am building. I already got the variables from an api end point with a HTTP get request but am unable to display as I wish to. I have looked at many similar questions but can't find the answer that I am looking for.
This is the script for getting the variables with the HTTP get request
<script>
const request = new XMLHttpRequest();
request.open('GET', 'api/seminars', true);
request.onload = function() {
// Begin accessing JSON data here
const data = JSON.parse(this.response);
if (request.status >= 200 && request.status < 400) {
data.forEach(resultArray => {
document.getElementById('output').innerHTML = resultArray.name;
document.getElementById('description').innerHTML =
resultArray.description;
document.getElementById('date').innerHTML = resultArray.date;
});
} else {
console.log('error');
}
};
request.send();
</script>
HTML CODE :
<div id="accordion">
<div class="card">
<div class="card-header" id="headingOne">
<h5 class="mb-0">
<button class="btn btn-link" data-toggle="collapse" data- target="#collapseOne" aria-expanded="true"
aria-controls="collapseOne">
</button>
</h5>
</div>
<div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#accordion">
<div class="card-body">
<h5 id="name"></h5>
<p id="description"></p>
<p id="description"></p>
...
</div>
</div>
</div>
</div>
And I have continued html code from here on not relevant ..
If there are 3 objects in my array, I would like to create 3 different cards and display name,description.. attributes. But my code only creates one card and displays last object's attributes.
You code never really "creates" elements based on your API call - It just updates (ie, overwrites) the existing dom elements by updating the innerHTML of fixed elements referenced by their IDs.
If my interpretation of your code is correct, you should be only seeing the LAST item in your API result. There are also some other weird stuff going on like duplicate IDs which Im guessing are typos
To fix this, create a new div.card-body for each item your API returns and append it to your container
const apiResult = [{
title: "title1",
description: "desc1",
output: "out1"
}, {
title: "title2",
description: "desc2",
output: "out2"
}, {
title: "title3",
description: "desc3",
output: "out3"
}];
const container = document.getElementById('accordion');
apiResult.forEach((result, idx) => {
// Create card element
const card = document.createElement('div');
card.classList = 'card-body';
// Construct card content
const content = `
<div class="card">
<div class="card-header" id="heading-${idx}">
<h5 class="mb-0">
<button class="btn btn-link" data-toggle="collapse" data-target="#collapse-${idx}" aria-expanded="true" aria-controls="collapse-${idx}">
</button>
</h5>
</div>
<div id="collapse-${idx}" class="collapse show" aria-labelledby="heading-${idx}" data-parent="#accordion">
<div class="card-body">
<h5>${result.title}</h5>
<p>${result.description}</p>
<p>${result.output}</p>
...
</div>
</div>
</div>
`;
// Append newyly created card element to the container
container.innerHTML += content;
})
.card {
padding: 1rem;
border: 1px solid black;
margin: 1rem;
}
<div id="accordion">
</div>
Note:
While this works, it's not very scalable. There are some great templating libraries out there with much more advanced interpolation features that you can consider if you have to do something like this in many places (and maintain it)
jsRender
Underscore Templates
Handlebars
UI frameworks like Vue, React, Angular wrap templating with binding to data models and handle auto updating the DOM for you to make things like this easier. Worth investigating if you have a lot of dynamically updating parts on your webpage
Suppose you have already queried the API, a way of doing it could be:
// Supposing you already have queried the API and have your data
let data = [
{name: 'name0', description: 'description', date: 'XX/XX/XXXX'},
{name: 'name1', description: 'description', date: 'XX/XX/XXXX'},
{name: 'name2', description: 'description', date: 'XX/XX/XXXX'},
]
data.forEach(res => {
let card = document.createElement("div");
let name = document.createTextNode('Name:' + res.name + ', ');
card.appendChild(name);
let description = document.createTextNode('Description:' + res.description + ', ');
card.appendChild(description);
let date = document.createTextNode('date:' + res.date);
card.appendChild(date);
let container = document.querySelector("#container");
container.appendChild(card);
});
<!-- At one point where you want to generate the cards you put this container -->
<div id="container"></div>
Yours was not working since you updated always the same elements instead of appending new ones :)
You are overwriting the description's innerHTML with every repetition of your for loop.
Change this
document.getElementById('output').innerHTML = resultArray.name;
document.getElementById('description').innerHTML = resultArray.description;
document.getElementById('date').innerHTML = resultArray.date;
to this
var element = document.createElement('div'); // create new div
var name = document.createElement('h4');
name.innerHTML = resultArray.name;
element.appendChild(name);
var description = document.createElement('p');
description.innerHTML = resultArray.description;
element.appendChild(description);
var date = document.createElement('span');
date.innerHTML = resultArray.date;
element.appendChild(date);
document.body.appendChild(element);

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>`;
}

Double click to edit element in Meteor app

I am making a school project in Meteor.js for a hospital - the prototype of the app is up on http://lrh.meteor.com . In the view doctors section in the table, I want to double click on the record and be able to edit the Name and the email id, but along with this I also want to update in record in MongoDB collection. Any ideas about how I can implement this feature?
I think this can help you.
Lets create this helper.
Template.example.helpers({
'editValue' : function(){
return Session.get("TargetValue" + this._id);
}
})
And this 2 events.
Template.example.events({
'dbclick #spanIdOnDom' : function(e,t){
return Session.set("TargetValue" + t.data._id,true)//hide the span and we set the input
},
'click #buttonToSaveNewValue': function(e, t) {
//here you can take the emailId and the name based on this._id like this Collection.find({_id:this._id}).fetch(); and do the updates you want to do
var newValueFromInput = document.getElementById('newValueFromInput').value;
var idCurrentDocument = this._id;
var Bebida = Collection.findOne(t.data._id);
Collection.update({_id: idCurrentDocument}, {$set:{fieldToUpdate: newValueFromInput}});
return Session.set("TargetValue" + t.data._id,false); //we hide the input and we put the span again
}
})
HTML
<template name="example">
{{#each collectionFind}}
{{#if editValue}}
<input type="text" id="newValueFromInput" value="{{currentValue}} " />
<button class="btn btn-sm btn-primary" id="buttonToSaveNewValue" type="submit">Save new Value</button>
{{else}}
<td>
<p>
<span class="content col-md-10" id="spanIdOnDom" ><h4>Descripcion Bebida:</h4><br>{{currentValue}} </span>
</p>
</td>
{{/if}}
{{/each}}
</template>
Of course you need to set your Allow/deny Permission and publish/subscribe methods to make it work more efficient.
How it works?
in resume you have an <span> tag with the current value, when you dobleClick on the <span> tag , we set the Session to true, the <span> tag go away and a new <input> appears with a new button then we take the value from the <input> and she update ($set) into the collection, and its done.
NOTE: this is a mini-repo from Simple Crud app in Meteor from Raj Anand, but the code on the blogs is on coffee and i don't use coffee Script.

Categories

Resources