Script tag in ng repeat - javascript

I'm trying to set a an elements that are intially hidden but the script tag inside my ng-repeat is only executing once.
<h2 class="primary-header"> Get Settings </h2>
<div ng-repeat='feature in features'>
<p></p>
<button type="button" class="btn btn-default"
ng-click='genericGet(feature.func)'>
{{feature.tag}}
</button>
<div ng-hide={{feature.hide}}>
<pre>
{{feature.status | json}}
</pre>
</div>
</div>
$scope.features =[];
$scope.features[0] = {};
$scope.features[0].tag = 'pop';
$scope.features[0].hide = true;
$scope.features[0].status = '';
$scope.features[0].func = 0;
$scope.features[0].url = 'pop';
$scope.genericGet = function(number){
$scope.features[number].hide = false;
}

Related

Why is my .empty() not emptying the parent element?

I cannot get the jQuery empty method to work on my appended HTML elements.
It's quite a simple problem seemingly, but it has me beat.
I've tried moving the empty method around in the code but I still cannot get it to empty.
Edit: It will not let me edit this unless there is more text so here is some more text.
My jQuery/JavaScript:
// Declares blank arrays
let monthHolidayName = [];
let monthHolidayDescription = [];
let monthHolidayDay = [];
let monthHolidayMonth = [];
let monthHolidayIsoDate = [];
// On change pushes the arrays with the current months data
$('#monthSelect').change(function(){
var selectedMonth = $('#monthSelect').val();
var numSelectedMonth = parseInt(selectedMonth) + 1;
for(i = 0; i < result['data']['holidays'].length; i++){
var holidayMonth = result['data']['holidays'][i]['date']['datetime']['month'];
if(holidayMonth === numSelectedMonth){
// console.log((result['data']['holidays'][i]));
monthHolidayName.push(result['data']['holidays'][i]['name']);
monthHolidayDescription.push(result['data']['holidays'][i]['description']);
monthHolidayDay.push(result['data']['holidays'][i]['date']['datetime']['day']);
monthHolidayMonth.push(result['data']['holidays'][i]['date']['datetime']['month']);
monthHolidayIsoDate.push(result['data']['holidays'][i]['date']['iso']);
}
}
// Empties the #holidays element <--------------------
$("#holidays").empty();
// Appends the data to the modal
for(i = 0; i < monthHolidayName.length; i++){
var holidayName = monthHolidayName[i];
var holidayDescription = monthHolidayDescription[i];
var holidayDay = monthHolidayDay[i];
var holidayDayMonth = monthHolidayMonth[i];
var holidayIsoDate = monthHolidayIsoDate[i];
var dateParsed = Date.parse(`${holidayDay} ${holidayDayMonth}`).toString("MMMM dS");
// Appends elements to #holidays with the data
$("#holidays").append(`<div class="list-group-item list-group-item-action"><div style="text-decoration: underline; text-align: center;">${holidayName}</div><div style="text-align: center">${holidayDescription}</div><small class="text-muted">${holidayIsoDate}</small></div>`);
}
});
My HTML code:
<!-- Calendar Modal -->
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="modal fade " id="calendar-modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 id="modalTitle">Holidays</h1>
<button type="button" class="close btn btn-secondary" data-bs-dismiss="modal" >×</button>
</div>
<!-- This is the body section of our modal overlay -->
<div class="modal-body" id="modalBody">
<div class="btn-group dropright">
<select class="form-select form-select-sm mb-3" id="monthSelect">
</select>
</div>
<div class="list-group">
<button type="button" class="list-group-item list-group-item-action active" id="holidayTitle">
Holidays in <span id="currentMonth"></span>
</button>
<span id="holidays">
</span>
</div>
</div>
<!-- This is the footer section of our modal overlay -->
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" >Close</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
It seems that you want to both append new data to your existing data while also deleting the old data. Your code is constantly referencing append, which implies that you want to keep the previous data, but your question asks about how to clear the holidays div, and then add the new holiday.
Answering your question, we don't need an array if we're always deleting the previous holiday information, instead we can use a JavaScript Object to hold the information. The other portion that you'll notice I changed is I took out the for loop you had. Since we have a single holiday, we don't need to iterate through an array. The following code should show how to work with a single holiday in an Object. Note that I did not change the HTML
// Declare an object with our empty parameters:
let holiday = {
name: "",
description: "",
day: "",
month: "",
isoDate: ""
};
// On change pushes the arrays with the current months data
$('#monthSelect').change(function(){
var selectedMonth = $('#monthSelect').val();
var numSelectedMonth = parseInt(selectedMonth) + 1;
for(i = 0; i < result['data']['holidays'].length; i++){
var holidayMonth = result['data']['holidays'][i]['date']['datetime']['month'];
if(holidayMonth === numSelectedMonth){
// console.log((result['data']['holidays'][i]));
// Using object setter notation to change each key:
holiday.name = result['data']['holidays'][i]['name'];
holiday.description = result['data']['holidays'][i]['description'];
holiday.day = result['data']['holidays'][i]['date']['datetime']['day'];
holiday.month = result['data']['holidays'][i]['date']['datetime']['month'];
holiday.isoDate = result['data']['holidays'][i]['date']['iso'];
}
}
// Empties the #holidays element <--------------------
$("#holidays").empty();
var dateParsed = Date.parse(`${holiday.day} ${holiday.month}`).toString("MMMM dS");
// Appends elements to #holidays with the data
$("#holidays").append(`<div class="list-group-item list-group-item-action"><div style="text-decoration: underline; text-align: center;">${holiday.name}</div><div style="text-align: center">${holiday.description}</div><small class="text-muted">${holiday.isoDate}</small></div>`);
});
You can manage this
$("#yourDiv").html(""); // jQuery

how to add a pre made div using javascript on button click

I have a webpage that looks like this
I want it like that every time the save note is pressed a new card with updated title and description appears on the right.
This is the html code I wrote
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-4 rightLine">
<h1 class="bottomLine">Note List</h1>
<div class="active-cyan-3 active-cyan-4 mb-4">
<input class="form-control" type="text" placeholder="Search" aria-label="Search">
</div>
<div id ="cards"></div>
</div>
<div class="col-md-8">
<h1 class="bottomLine">Note</h1>
<div class="active-cyan-3 active-cyan-4 mb-4">
<input class="form-control" id="title" type="text" placeholder="Enter title here" aria-label="Search">
</div>
<div class="active-cyan-3 active-cyan-4 mb-4 bottomLine">
<textarea class="form-control" id="description" rows="15" placeholder="Enter descirption here"></textarea>
</div>
<div>
<button type="button" id="removenote" class="btn btn-outline-danger">Remove Note</button>
<button type="button" id="savenote" class="btn btn-outline-success" onclick="x.saveNote()">Save Note</button>
<button type="button" id="addnote" class="btn btn-outline-primary" onclick="x.addNote()">Add Note</button>
</div>
</div>
</div>
</div>
The card is the one with id=card in the code and that is the thing I want new every-time.
This is the javascript I wrote
class Note {
constructor(name, description) {
this.name = name;
this.description = description;
}
}
class NoteComponent {
constructor() {
this.listOfNotes = [];
}
saveNote() {
let title = document.getElementById("title").value;
let description = document.getElementById("description").value;
let currentNote = new Note(title, description);
this.listOfNotes.push(currentNote);
getCardHTML(this.listOfNotes);
this.listOfNotes.forEach((arrayItem) => {
console.log('name is ' + arrayItem.name + ' description is ' + arrayItem.description);
});
}
addNote() {
let title = document.getElementById("title").value = "";
let description = document.getElementById("description").value = "";
}
filterList(noteList, Query) {}
}
/*when the specific note card clicked the title and description places will be populated*/
function showNote(){
console.log('the onclcik worked fine');
}
function getCardHTML(arr) {
let divOfCards = document.getElementById('cards');
while (divOfCards.firstChild) {
divOfCards.removeChild(divOfCards.firstChild);
}
for (let i = 0; i < arr.length; i++) {
let div = document.getElementById("cards");
let anchor = document.createElement("div");
anchor.className = "list-group-item list-group-item-action flex-column align-items-start";
let innerDiv = document.createElement("div");
innerDiv.className = "d-flex w-100 justify-content-between";
let divHeading = document.createElement("h5");
divHeading.className = "mb-1";
divHeading.innerHTML = arr[i].name;
let divPara = document.createElement("p");
divPara.className = "mb-1";
divPara.innerHTML = arr[i].description;
//anchor.href = "#";
anchor.onclick = showNote();
innerDiv.appendChild(divHeading);
anchor.appendChild(innerDiv);
anchor.appendChild(divPara);
div.appendChild(anchor);
}
}
let x = new NoteComponent();
When a new note is saved it appears on the left side. I don't understand how when that card on the left side is clicked that notes title and description occupies the places on the right.
There is a JavaScript function called createElement() that allows you to create and assign a HTML element into a variable.
Once the element is created, just append the content to it and then append the element to a HTML element.
For example:
var body = document.getElementsByTagName('body');
var title = document.getElementById("title").value;
var description = document.getElementById("description").value;
var div = document.createElement("div");
var h1 = document.createElement("h1");
var p = document.createElement("p");
// assign values to elements
h1.textContent = title;
p.textContent = description;
// append elements to div
div.appendChild(h1);
div.appendChild(p);
// append div to body
body.appendChild(div);
You can also use createTextNode instead of textContent.
Traverse the listOfNodes and create card and append it to the div. Whenever you click savenote the card will be appeared. Here is the working demo.
class Note{
constructor(name, description) {
this.name = name;
this.description = description;
}
}
let listOfNotes = [];
class NoteComponent{
constructor(){}
filterList(noteList,Query){}
}
document.getElementById("savenote").addEventListener("click", function(){
let title = document.getElementById("title").value;
let description = document.getElementById("description").value;
let currentNote = new Note(title,description);
listOfNotes.push(currentNote);
var newNode = document.getElementById("card");
listOfNotes.forEach((arrayItem) => {
console.log(arrayItem.name);
var name = document.createElement("h5");
var nameText = document.createTextNode(arrayItem.name);
name.appendChild(nameText);
var description = document.createElement("p");
var desc = document.createTextNode(arrayItem.description);
description.appendChild(desc);
var card_body = document.createElement("div");
card_body.className = "card_body";
card_body.appendChild(name);
card_body.appendChild(description);
var card = document.createElement("div");
card.className = "card";
card.appendChild(card_body);
var aTag = document.createElement("a");
aTag.className="custom-card";
aTag.setAttribute("id", "card");
aTag.appendChild(card);
var cardDiv = document.getElementById("card");
cardDiv.appendChild(aTag);
});
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-4 rightLine">
<h1 class="bottomLine">Note List</h1>
<div class="active-cyan-3 active-cyan-4 mb-4">
<input class="form-control" type="text" placeholder="Search" aria-label="Search">
</div>
<div id="card">
<a href="#" id="card" class="custom-card">
<div class="card">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">With supporting text below as a natural lead-in to additional content.</p>
</div>
</div>
</a>
</div>
</div>
<div class="col-md-8">
<h1 class="bottomLine">Note</h1>
<div class="active-cyan-3 active-cyan-4 mb-4">
<input class="form-control" id="title" type="text" placeholder="Enter title here" aria-label="Search">
</div>
<div class="active-cyan-3 active-cyan-4 mb-4 bottomLine">
<textarea class="form-control" id="description" rows="15" placeholder="Enter descirption here"></textarea>
</div>
<div>
<button type="button" id="removenote" class="btn btn-outline-danger">Remove Note</button>
<button type="button" id="savenote" class="btn btn-outline-success">Save Note</button>
<button type="button" id="addnote" class="btn btn-outline-primary">Add Note</button>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery.min.js"></script>
<link href="https://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css" rel="stylesheet" type="text/css" />
<script src="https://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js"></script>
</div>
</body>
</html>
Replace your anchor template (your "a ...id="card" code) with an unordered list like so:
<ul id="cards"></ul>
Then utilize DOM's appendChild() method:
https://www.w3schools.com/jsref/met_node_appendchild.asp
So your JS code will be something like this
document.getElementById("savenote").addEventListener("click", function(){
let title = document.getElementById("title").value;
let description = document.getElementById("description").value;
let currentNote = new Note(title,description);
let listedCard = document.createElement("LI");
listOfNotes.push(currentNote);
listOfNotes.forEach((arrayItem) => {
console.log('name is ' + arrayItem.name + ' description is ' + arrayItem.description);
});
// New code
let listedCard = document.createElement("LI");
let cardAnchor = document.createElement("A");
//...
listedCard.appendChild(cardAnchor);
//...
// The rest of your HTML code for the <a...id="card"> goes here. Little tedious
// Finally append the listedCard to the UL
let cardList = document.getElementById("cards");
cardList.appendChild(listedCard);
});
The simplest solution IMO would be generate the HTML by looping over the Notes array.
1) This function iterates (using map) over the array and returns HTML using a template literal (what's between the back-ticks):
function getCardHTML(arr) {
// Array.map returns a new array
return arr.map(({ name, description }) => {
// In this case the array has a number of elements containing
// HTML strings
return `<h5 class="card-title">${name}</h5><p class="card-text">${description}</p>`;
// which is joined into one HTML string before it's returned
}).join('');
}
2) And you can add it to the card panel like this:
const cards = document.querySelector('.card-body');
cards.innerHTML = getCardHTML(listOfNotes);
DEMO
Edit
In order to solve the next part of the question move all the node selections outside of the functions, and then add an event listener to the cards container.
class Note {
constructor(name, description) {
this.name = name;
this.description = description;
}
}
class NoteComponent {
constructor() {}
filterList(noteList, Query) {}
}
const listOfNotes = [];
const title = document.getElementById('title');
const description = document.getElementById('description');
const save = document.getElementById("savenote");
save.addEventListener("click", saveNote, false);
const cards = document.querySelector('.cards');
cards.addEventListener('click', showNote, false);
function getCardHTML(arr) {
return arr.map(({ name, description }, i) => {
return `<div data-id="${i}" class="card"><h5>${name}</h5><p>${description}</p></div>`;
}).join('');
}
function saveNote() {
let currentNote = new Note(title.value, description.value);
listOfNotes.push(currentNote);
cards.innerHTML = getCardHTML(listOfNotes);
}
function showNote(e) {
const t = e.target;
const id = t.dataset.id || t.parentNode.dataset.id;
title.value = listOfNotes[id].name;
description.value = listOfNotes[id].description;
}
<div class="cards"></div>
<div>
<input id="title" type="text" placeholder="Enter title here" aria-label="Search">
</div>
<div>
<textarea id="description" rows="15" placeholder="Enter descirption here"></textarea>
</div>
<div>
<button type="button" id="removenote">Remove Note</button>
<button type="button" id="savenote">Save Note</button>
<button type="button" id="addnote">Add Note</button>
</div>
Hope that helps.

Grouping dynamically created elements

Below is my function to create and remove text boxes dynamically, however I am looking for a method by which I can group these items into one text box (preferably through the use of a button located below the text boxes or something..). The jist is that I've four columns, each with these functions to add and remove the text boxes, therefore I also wondered if that after grouping the text boxes would the undo function now be useless? Apologies for my lack of knowledge in this area, and thank you in advance for your help
function addText(e) {
let week = e.target.closest('.week'),
area = week ? week.querySelector('textarea') : null,
text = area ? area.value : '';
if (text.length) {
let item = document.createElement('p');
item.innerText = text;
week.appendChild(item);
area.value = '';
}
}
function removeText(e) {
let week = e.target.closest('.week'),
item = week ? week.querySelector('p:last-of-type') : null;
if (item)
item.remove()
}
document.body.addEventListener('click', event => {
if (event.target.closest('[add-text]'))
addText(event);
if (event.target.closest('[remove-text]'))
removeText(event);
})
<div id="planner">
<div class="week week1">
<div class="add">
<h1>Weeks</h1>
<textarea></textarea>
<button type="button" add-text>Add text</button>
<button type="button" remove-text>Undo</button>
</div>
</div>
<div class="week week2">
<div class="add">
<h1>Topics</h1>
<textarea></textarea>
<button type="button" add-text>Add text</button>
<button type="button" remove-text>Undo</button>
</div>
</div>

JavaScript - getElementsByClassName works for one function but not the other

I am trying to code a simple page where each click will change the pricing on the website using getElementsByClassName.
This is working :
<script>
function monthly() {
var price = document.getElementsByClassName("price");
price[0].innerHTML = "$10";
price[1].innerHTML = "$20";
price[2].innerHTML = "$30";
price[3].innerHTML = "$40";
}
</script>
<button onclick="monthly()">Monthly</button>
<button onclick="1year()">1 year</button>
<button onclick="2year()">2 year</button>
<button onclick="3year()">3 year</button>
<br>
<span class="price">$1</span><br>
<span class="price">$2</span><br>
<span class="price">$3</span><br>
<span class="price">$4</span><br>
Not working after adding 1year():
<script>
function monthly() {
var price = document.getElementsByClassName("price");
price[0].innerHTML = "$10";
price[1].innerHTML = "$20";
price[2].innerHTML = "$30";
price[3].innerHTML = "$40";
}
function 1year() {
var price2 = document.getElementsByClassName("price");
price2[0].innerHTML = "$8";
price2[1].innerHTML = "$16";
price2[2].innerHTML = "$24";
price2[3].innerHTML = "$32";
}
</script>
<button onclick="monthly()">Monthly</button>
<button onclick="1year()">1 year</button>
<button onclick="2year()">2 year</button>
<button onclick="3year()">3 year</button>
<br>
<span class="price">$1</span><br>
<span class="price">$2</span><br>
<span class="price">$3</span><br>
<span class="price">$4</span><br>
Does anyone know why?
TIA.
A function or variable in JavaScript cannot start with a number.
Identifiers must start with either a dollar sign ($), an underscore (_) or a unicode character.
In your case,
<button onclick="1year()">1 year</button>
<button onclick="2year()">2 year</button>
<button onclick="3year()">3 year</button>
All 3 functions are invalid.
Thanks to #Richard Hamilton, this is the full working code snipplet.
<script>
function monthly() {
var price = document.getElementsByClassName("price");
price[0].innerHTML = "$10";
price[1].innerHTML = "$20";
price[2].innerHTML = "$30";
price[3].innerHTML = "$40";
}
function annually() {
var price = document.getElementsByClassName("price");
price[0].innerHTML = "$8";
price[1].innerHTML = "$16";
price[2].innerHTML = "$24";
price[3].innerHTML = "$32";
}
</script>
<button onclick="monthly()">Monthly</button>
<button onclick="annually()">1 year</button>
<br>
<span class="price">$1</span><br>
<span class="price">$2</span><br>
<span class="price">$3</span><br>
<span class="price">$4</span><br>

Is JQuery breaking my functionality?

I am making an app, the user can either select an item or use their camera to get the qr code which translates into an item's ID.
The problem is that I think some JQuery is messing with my scope from working properly.
I have to get the QR code by listening to an innerHtml change, once it changes (DOMSubtreeModified) the following occurs.
var index = 0;
$('#result').one('DOMSubtreeModified', function(e) {
var code = "";
if (e.target.innerHTML.length > 0) {
code = e.target.innerHTML;
$scope.ToRun(code);
}
});
$scope.ToRun = function(code) {
for (i = 0; i < $scope.plantList.length; i++) {
if ($scope.plantList[i].plantcode == code) {
index = i;
break;
}
}
$scope.currentPlant = $scope.plantList[index];
$scope.plantDetails = false;
$scope.searchDetails = true;
}
For some reason the following does not have any effect on my ng-classes. As when an item is selected I hide the input dialogs, and show the result one.
$scope.plantDetails = false;
$scope.searchDetails = true;
But when a user selects the item manually it works just perfectly. (the items have an ng-click on it)
$scope.viewPlant = function(plant) {
$scope.currentPlant = plant
$scope.plantDetails = false;
$scope.searchDetails = true;
};
And the above works fine, with the ng-click. So why won't my new function that listens for an innerHtml change work?
HTML snippet
<div ng-class="{ 'hidden': searchDetails }">
<!-- CHOOSE INPUT TYPE -->
<div class="form-group" style="margin-bottom:0px">
<div class="btn-group btn-group-justified">
<div class="btn-group">
<button type="button" class="btn btn-default" ng-click="digits = false; qr = true">Plant Code</button>
</div>
<div class="btn-group">
<button type="button" class="btn btn-default" ng-click="digits = true; qr = false">QR Code</button>
</div>
</div>
</div>
<br />
<!-- QR CODE INPUT -->
<div ng-class="{ 'hidden': qr }">
<img id="blah" src="./images/placeholder.png" />
<span class="btn btn-default btn-file">
<i class="glyphicon glyphicon-camera"></i>
<input type="file" onchange="readURL(this);handleFiles(this.files)">
</span>
<div id="result">xxxxxx</div>
<canvas id="qr-canvas" width="800" height="600"></canvas>
</div>
<!-- MANUAL SELECTION INPUT -->
<div ng-class="{ 'hidden': digits }">
<input ng-model="search.$" style="width:100%; font-size:30px; text-align:center" placeholder="Plant Code" />
<div style="overflow: auto; max-height:250px">
<table class="table table-striped" style="background-color:lightblue">
<tr ng-repeat="plant in plantList | filter:search" ng-click="viewPlant(plant)" style="cursor:pointer">
<th>{{plant.name}}</th>
<th>{{plant.plantcode}}</th>
</tr>
</table>
</div>
</div>
<div>
</div>
</div>
<div ng-class="{ 'hidden': plantDetails }">
// results - this should appear when one of the above is entered.
// works for manual selection, but not qr code
</div>
Just confused on why my QR input will not fire off the change-class options to hide the searchDetails div and show the plantDetails div
EDIT: Doing a small test, it seems that my class variables are indeed not updating at all. I just put the values at the bottom of my page and they do not update when hitting the block of:
$scope.plantDetails = false;
$scope.searchDetails = true;
You need to let Angular know about the change. Add this at the end of your method and let me know if it works.
$scope.$apply();

Categories

Resources