How to render data from javascript to html? - javascript

I am trying to create a forum website. I have created a data1.js file like so:
var threads = [
{
id: 1,
title: "Thread 1",
author: "Alice",
date: Date.now(),
content: "Thread content",
comments: [
{
author: "Bob",
date: Date.now(),
content: "Hello world!"
},
{
author: "Delta",
date: Date.now(),
content: "Hello everyone!"
}
]
},
{
id: 2,
title: "Thread 2",
author: "Ekko",
date: Date.now(),
content: "Thread content",
comments: [
{
author: "Foxtrot",
date: Date.now(),
content: "Hello everybody!"
},
{
author: "Foo",
date: Date.now(),
content: "Hello hello!"
}
]
}
]
In my index.html-file, I rendered data by pasting in a template string for each row (thread), and then rendering the vaiables dynamically, like so:
<body>
<div class="top-bar">
<h1>
My Forum
</h1>
</div>
<div class="main">
<ol> </ol>
</div>
<script src="data1.js"></script> <!--Load script-->
<script>
console.log(threads);
//Create html for a row in string format
var container = document.querySelector('ol');
for (let thread of threads) {
var rowHTML = `
<li class="row">
<a href="/thread.html?key=${thread.id}">
<h4 class="title"> ${thread.title}</h4>
<div class="title-bottom">
<p class ="title-ts">${new Date(thread.date).toLocaleString()}</p>
<p class="comment-count">${thread.comments.length} comments</p>
</div>
</a>
</li>
`
//Paste it into the container
container.insertAdjacentHTML('beforeend', rowHTML);
}
</script>
And that seemed to work when I ran the browser; It shows two threads with two comments each, like it is in the data-file.
When I right-click on the file and click "Open with Edge"->"Open Browser", VS Code opens a URL that looks like this:
file:///C:\Users\PATH\TO\FILE\VSCodeProjects\Website-1\index1.html
and when I click on a thread, I get taken to the URL i defined in <a href="/thread1.html?key=${thread.id}">: file:///C:/thread1.html?key=1, and the whole page is blank. If I try to modify the URL to something like this: <a href="\Users\PATH\TO\FILE\VSCodeProjects\Website-1\index1.html?key=${thread.id}">, I still get a blank page when clicking on a thread.
When I tried to render the same way in my thread.html-file like this:
<body>
<div class="top-bar">
<h1>
My Forum
</h1>
</div>
<div class="main">
<div class="header"></div>
<textarea></textarea>
<button>Add comment</button>
<div class="comments"></div>
</div>
<script defer src="data1.js"></script> <!--Load script-->
<script>
console.log(threads);
//Slice if from url for thread id
var id = new URLSearchParams(window.location.search).get('key'); //window.location.search.slice(1);
var thread = threads.find(t => t.id == id);
//Create html for header in string format
var header = document.querySelector('.header');
var headerHTML = `
<h4 class="title">${thread.title}</h4>
<div class="title-bottom">
<p class ="title-ts">${new Date(thread.date).toLocaleString()}</p>
<p class="comment-count">${thread.comments.length} comments</p>
</div>
`
header.insertAdjacentHTML('beforeend', headerHTML);
//Create html for comments in string format
var comments = document.querySelector('.comments');
for (let comment of thread.comments) {
var commentHTML = `
<div class="comment">
<div class="top-comment">
<p class="user">${comment.author}</p>
<p class="comment-ts">${new Date(comment.date).toLocaleString()}</p>
</div>
<div class="comment-content">${comment.content}</div>
</div>
`
comments.insertAdjacentHTML('beforeend', commentHTML);
}
</script>
</body>
It is not rendering the comments from the .js-file. It only shows <h1>My Forum</h1>, the text-area, and the "Add comment" button. When it should in addition show the thread title and the 2 comments for the thread that I created in the js-file.
I did it exactly the same way in the two files, but I get different results, I have checked that there are no errors in indentation and no added or missing s's, and I have tried to make sure I am looping into the correct place in the threads. I have tried to look through similar questions on here but I cannot find out what's wrong. Am I missing something or am I doing something wrong? Please, all help is appreciated!
EDIT
Added screenshot of console

Related

Is it a good idea to use images local URL in array?

I want to make a list of projects and I made an array and using .map() method to display them, and I'm adding that list in the HTML element using .insertAdjacentHTML() method.
While inspecting the elements I can see the image path is coming fine but image is not showing in UI. What would be the reason of it?
const projectData = [
{
id: 01,
title: "Project 1",
url: "https://react-projects-1/",
imageUrl: "src/img/projects/project-1.png", //is it wrong using like this
description: "This is project 1 desc",
},
{
id: 02,
title: "Project 2",
url: "https://react-projects-2/",
imageUrl: "src/img/projects/project-2.png", //is it wrong using like this
description: "This is project 2 desc",
},
];
Mapping them and using the HTML:-
const projectHTML = projectData.map((el) => {
return `<div class="row align-items-center project_row">
<div class="col-md-5 col-12 order-md-2">
<a href="${el.url}" class="thumb">
<img
src="${el.imageUrl}"
class="w-100"
alt="${el.title}"
target="_blank"
/>
</a>
</div>
<div class="col-md-7 col-12 order-md-1 mt-4 mt-md-0">
<h3><span>${el.id}.</span>${el.title}</h3>
<p>
${el.description}
</p>
<ul class="buildTech">
<li><i class="fa-brands fa-react"></i> React</li>
</ul>
View
</div>
</div>`;
});
workWrapper.insertAdjacentHTML("afterbegin", projectHTML);
Inspect:-

How vue know which image to display and being reactive from method

Recently I have signed up for VueMstery and I am lost. In order to understand code I have a habit of visualizing code and can't move if I cannot make sense of code. What I do not understand in the following code is
how function named updateProduct is updating the DOM with the passed in image and
how it know which image to display. (this point confuses me a lot)
Following is the code
<body>
<div class="nav-bar"></div>
<div id="app">
<div class="product">
<div class="product-image">
<img :src="image" alt="">
</div>
<div class="product-info">
<h1>{{products}}</h1>
<p v-if='inStock'>In Stock</p>
<p v-else>Out of stock</p>
<p v-if='inventory <= 10'>Low on stock</p>
<ul>
<li v-for='detail in details'>{{detail}}</li>
</ul>
<div v-for='variant in variants'
class='color-box'
:style="{backgroundColor: variant.variantColor}"
#mouseover='updateProduct(variant.variantImage)'>
</div>
cart {{cart}}
<button #click='addToCart'>
Add to cart
</button>
</div>
</div>
</div>
<!-- development version, includes helpful console warnings -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="main.js"></script>
and main.js
var app = new Vue({
el: '#app',
data: {
products: 'socks',
image: './assets/vmSocks-green.jpg',
inStock: true,
inventory: 10,
details: ["80% cotton", "20% polyester", "Gender-Neutral"],
variants: [
{
variantId: 2234,
variantColor: "green",
variantImage: "./assets/vmSocks-green.jpg"
},
{
variantId: 2235,
variantColor: "blue",
variantImage: "./assets/vmSocks-blue.jpg"
},
],
cart: 0
},
methods: {
addToCart: function(){
this.cart +=1
},
updateProduct: function(image){
this.image = image;
}
}
})
Look at the event handler which you've registered in the template:
#mouseover='updateProduct(variant.variantImage)'
Vue will run the function automatically when events fire. The function updateProduct just updates the value image in your data object. Vue detects any changes for that data and updates DOM when nessesary.
Also because your img tag depends on image property of data object:
<img :src="image" alt="">
you will see that image changes.

str.split() is splitting at every letter and not at the delimiter

I'm trying to split a string of tags that I'm getting back from firebase by ',' and then store them in my data() for rendering. When I render out the firebase snapshot data after I split they tags in the console they are correctly formatted like so:
"tag1" "tag2"
but when I render them in my vue app, they are split at every letter (or at least a div is generated and is displaying for every letter) like so:
<div>"t"</div>
<div>"a"</div>
<div>"g"</div>
<div>"1"</div>
Can someone let me know if this is an issue with my vue app or my firebase call?
I have included some dummy data that I'm using to show the final result of how the tags array in data should look.
Here's my app for reference :)
<div id="container">
<div class="topbar">
<h3 id="header">DevDeep</h3>
<div id="searchDiv">
<b-form-input id="search" v-model="search" placeholder="search articles"></b-form-input>
<font-awesome-icon id="searchBtn" #click="searchResults()" icon="search" />
</div>
</div>
<!--main part of page-->
<div class="bod">
<div class="sideContainer">
<h4 id="category">categories</h4>
<ul id="listoflinks" v-for="cat in categories" :key="cat.toString()">
<div #click="searchResultsByCat(cat)">{{cat}}</div>
</ul>
</div>
<div id="centerContainer">
<div>
<h5> Tag list </h5>
<div class="flexContainer">
<div id="selectedTags" v-for="tag in tagList" :key="tag.toString()">
<span id="tag" #click="removeTag(tag)">{{tag}}</span>
</div>
<font-awesome-icon id="searchBtn" #click="searchbyTags()" icon="search" />
</div>
</div>
<div id="artDiv" v-for="art in articles" :key="art.title">
<div #click="gotoArticle(art)" id="thumbnail">
<h5 >{{art.title}}</h5>
<img :src=art.image height="100px" width="100px" alt="article thumbnail">
</div>
<!--TAGS-->/////////////////////////////////////////
<div class="flexContainer">
<div id="tags" v-for="tag in art.tags" :key="tag.toString()">
<span id="tag" #click="addTagToSearch(tag)">{{tag}}</span>
</div>
</div>
<!--TAGS-->//////////////////////////////////////////
</div>
</div>
<div class="addContainer">adds</div>
</div>
<!--main part of page-->
</div>
</template>
<script>
const fb = require('../../fireconfig.js')
export default {
name: 'Home',
data:function() {
return{
articles: [
{
title: 'modern web app security',
body: 'some content here about web app security',
image: 'dd',
tags: ['cyber security','web apps', 'web development']
},
{
title: 'intro to ArcGIS',
body: 'an article to show users how to do GIS stuff',
image: 'dwwd',
tags: ['arcgis','node js','gps services']
},
{
title: 'Vue.js injecting props',
body: 'this is how to inject vue props into a component',
image: 'dwwd',
tags: ['vue','props','components','web development','web apps']
}
],
categories:['web development', 'arcgis','cyber security','partnerships'],
search: '',
tagList: []
}
},
props: {
post: Object
},
created(){
console.log('db post will go here later')
let ref = fb.db.collection('articles')
ref.get()
.then(snapshot => {
if (snapshot.empty) {
console.log('No matching documents.');
return;
}
snapshot.forEach(doc => { //this works for each doc
console.log(doc.id, '=>', doc.data());
doc.data().tags = doc.data().tags.split(",") // its splitting each letter we need to only split at the comma
console.log(doc.data().tags)
this.articles.push(doc.data()) //push object into state array
})
})
.catch(err => {
console.log('Error getting documents', err);
});
},
}
</script>
doc.data() will produce a new object each time you call the method.
doc.data().tags = doc.data().tags.split(",") // its splitting each letter we need to only split at the comma
The above line is pointless you create a new object, but never store it in a variable. Which means that you can't use it later. Instead assign the resulting object to variable and use that.
const data = doc.data(); // create the data object only once
data.tags = data.tags.split(",");
this.articles.push(data);
Since the split of the tags didn't persist in the question code. tag in:
<div id="tags" v-for="tag in art.tags" :key="tag.toString()">
Is set to a character, because art.tags is a string and not an array.

Angular 1.6 - ng-repeat orderby and groupby items

So what I want to achieve is:
loop through array of messages (ng-repeat)
sort messages by date
then group current message with previous one, if author is matching (store in the same div element)
create new div if current messages's author is not matching previous one
continue loop
I'm stuck here:
<div class="message" ng-repeat="msg in chatDetails.msgList">
<p>{{ msg.text }}</p>
</div>
I need to keep exact order - simply, if previous element doesn't match with current - new box should be created.
Is that even possible in angular? If so, could you show me how, please?
Thank you!
edit
Heres sample result of chatDetails:
{
msgList: [
{ author: 0, text: 'hi', date: 1493050181799 },
{ author: 1, text: 'hola!', date: 1493050181801 },
{ author: 1, text: 'wilkomen', date: 1493050181802 },
{ author: 0, text: 'czesc', date: 1493050181803 }
{ author: 0, text: 'ciao', date: 1493050181804 }
{ author: 1, text: 'bonjour', date: 1493050181805 }
]
}
Somehow desired result:
<div class="message-list">
<div class="message-group" data-author="1">
<div class="message">
<p>hola</p>
</div>
<div class="message">
<p>ciao</p>
</div>
</div>
<div class="message-group" data-author="0">
<div class="message">
<p>hola</p>
</div>
</div>
<div class="message-group" data-author="1">
<div class="message">
<p>hola</p>
</div>
<div class="message">
<p>hola</p>
</div>
</div>
</div>
I believe your problem can be solved with "orderBy".
https://docs.angularjs.org/api/ng/filter/orderBy
It would look something like:
<div class="message" ng-repeat="msg in chatDetails.msgList | orderBy: 'date'">
<p>{{ msg.text }}</p>
</div>
If your msg objects have some date or index value, you can sort by that number.

How to add a link in javascript

I have the following Javascript that I am using to make a sort of flowchart where the user clicks through a set of questions. For certain responses i want to link to an external site where more info can be found. How do I add these links?
HTML
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
<div class="wrapper">
<div class="container">
<div class="row">
<div class="col-xs-12 text-right">
<button class="btn btn-default btn-corner" type="submit" data-bind="click: startOver, visible: queryData().id > 0">Start over</button>
</div>
</div>
</div>
<div class="container main">
<div class="row">
<div class="c12 text-center">
<h1 data-bind="text: queryData().text"></h1>
<h3 data-bind="text: queryData().subhead"></h3>
<div class="option-group" data-bind="foreach: queryData().answers">
<button class="btn btn-default btn-lg" type="submit" data-bind="click: $parent.goToTarget, text: text"></button>
</div>
<button class="btn btn-default" type="submit" data-bind="click: stepBack, visible: navHistory().length > 1">Previous Step</button>
</div>
</div>
</div>
<div class="push"></div>
</div>
<script src="http://ajax.aspnetcdn.com/ajax/knockout/knockout-3.3.0.js"></script>
<script src="app.js?v=0.4.0"></script>
<script>
</script>
</body>
</html>
The Javascript is as follows:
JS
var queries = [{
id: 0,
text: "Where to start?",
answers: [{
text: "Let's Begin!",
target: 1
}]
}, {
id: 1,
text: "Which genre do you want to start in?",
answers: [{
text: "Fantasy",
target: 100
}, {
text: "SciFi",
target: 2
}, {
text: "Neither",
target: 59
}]
}, {
id: 2,
text: "It's huge but it's worth it. The Cryptonomicon by Neal Stephenson",
answers: [{
text: "Amazon.co.uk",
target: "_blank"
}, {
text: "Amazon.com"
}]
}];
function QueryViewModel() {
var self = this;
self.querySet = ko.observable();
self.currentStep = ko.observable();
self.queryData = ko.observable();
self.sfw = ko.observable();
self.navHistory = ko.observableArray();
// Operations
self.goToTarget = function(obj) {
self.navHistory.push(self.currentStep());
self.currentStep(obj.target);
self.queryData(self.querySet()[obj.target]);
}
self.startOver = function() {
self.navHistory.removeAll();
self.goToTarget({target: 0});
}
self.stepBack = function() {
var lastStep = self.navHistory().length > 1 ? self.navHistory.pop() : 0;
self.currentStep(lastStep);
self.queryData(self.querySet()[lastStep]);
}
var paramsString = document.location.hash.substring(1);
var params = new Array();
if (paramsString) {
var paramValues = paramsString.split("&");
for (var i = 0; i < paramValues.length; i++) {
var paramValue = paramValues[i].split("=");
params[paramValue[0]] = paramValue[1];
}
}
params ? paramTarget = params['target'] : params = [];
self.sfw() ? self.querySet(queriesSFW) : self.querySet(queries);
if (paramTarget) {
self.navHistory.push(0);
self.currentStep(0);
self.goToTarget({target: paramTarget})
} else {
self.goToTarget({target: 0});
}
}
ko.applyBindings(new QueryViewModel());
In html you can do something like this:
<button type="button" onclick="window.open('https://google.com/', '_self')">Button</button>
You don't have to use a button, different elements can use onclick like text or images. This can also call js functions, just put the function name where "window.open..." is.
Of course the standard way to do it is
<a href='https://www.google.com/'>Link</a>
You can practice using js here: http://www.w3schools.com/js/tryit.asp?filename=tryjs_intro_inner_html
and learn more about it here: http://www.w3schools.com/js/js_intro.asp
I am not sure why you would show us the JSON for open a link to another page. Unless I misunderstood. This kind of basic information can be found by a quick Google search.
Add your link in the object like:
text: "Fantasy",
link: "http://www.stackoverflow.com",
target: 2
Now when you need to go to that link, use this function:
var link = obj.link;
window.open(link, "_blank");

Categories