Handlebars page rendering as a empty bracket - javascript

When i run my webapp using handlebars as the template engine and want to go to the /dashboard page it renders an empty bracket instead of the actual code.
http://localhost:3001/dashboard
what is rendered
{}
Here is the route
router.get('/dashboard', withAuth, async (req, res) => {
try{
const dbUserData = await User.findByPk(req.session.user_id, {
attributes: { exclude: ['password'] },
include: [{ model: Post }],
});
const user = dbUserData.get({plain: true});
res.render('dashboard', {
...user,
loggedIn: req.session.loggedIn,
})
} catch(err){
console.log(err);
res.status(500).json(err);
}
})
Here is the dashboard.handlebars code
<section>
<div class="row">
<div class="col-auto">
<h2>Welcome, {{username}}!</h2>
</div>
</div>
<h2>Create New Post</h2>
<form id="new-form" class=" margin-10 new-post-form">
<div id="new-title">
<label for="post-title">Title</label>
<input type="text" id="post-title" name="post-title" />
</div>
<div id="new-text" class="margin-10">
<label for="post-text">Text</label>
<input id="post-text" name="post-text" />
</div>
<button type="submit" class="black-button">Create</button>
</form>
</section>
{{#if posts.length}}
<section>
<h2>Your Posts</h2>
<ol>
{{#each posts as |post|}}
<li>
{{> post-info post}}
Edit post
</li>
{{/each}}
</ol>
</section>
{{/if}}
<script src="/js/add-post.js"></script>

Related

Vue 3 Composition Api - Find object and render

I have a problem. It's my first Vue.js project and I need help to solve the following problem. First I get a response from my API, then I get a list of projects and I want to find the project with the same ID as the url parameter. When I try to open the view my console logs the following error:
TypeError: Cannot read property 'title' of undefined
However, it then renders the right project into the template.
Code:
<template>
<div id="wrapper">
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css"
integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA=="
crossorigin="anonymous"
/>
<Sidebar></Sidebar>
<div id="content">
<Navbar></Navbar>
<div id="headline">
<ul>
<li>
<h1>Projekt Details</h1>
<Popup></Popup>
</li>
</ul>
</div>
<div id="grid" class="module-grid module-grid-2">
<div class="card">
<div class="card-head">
<div>
<h3>Meta Daten</h3>
</div>
<div></div>
</div>
<div class="card-body">
<ul v-if="filtered_projects != null">
<li>
<div class="list-info">
<p>Projektnummer: {{ filtered_projects.id }}</p>
</div>
</li>
<li>
<div class="list-info">
<p>Autor: {{ filtered_projects.author }}</p>
</div>
</li>
<li>
<div class="list-info">
<p>Firma: {{ filtered_projects.company }}</p>
</div>
</li>
<li>
<div class="list-info">
<p>
Erstellt am:
{{
new Date(filtered_projects.created_at)
.toLocaleString()
.split(",")[0]
}}
</p>
</div>
</li>
<li>
<div class="list-info">
<p>
Letzte Änderung am:
{{
new Date(filtered_projects.updated_at)
.toLocaleString()
.split(",")[0]
}}
</p>
</div>
</li>
</ul>
</div>
</div>
<div class="card">
<div class="card-head">
<div>
<h3>Projekt Übersicht</h3>
</div>
<div></div>
</div>
<div class="card-body">
<form class="edit-form" #submit.prevent="submitProject()">
<label for="title">Überschrift*</label>
<input
v-model="filtered_projects.title"
name="title"
id="title"
class="input"
type="text"
required
maxlength="16"
/>
<label for="text">Text*</label>
<textarea
v-model="filtered_projects.text"
name="text"
id="text"
class="input"
type="text"
required
rows="6"
/>
<label for="finish">Abgeschlossen</label>
<input
v-model="filtered_projects.finish"
name="finish"
id="finish"
class="input w-auto"
type="checkbox"
/>
<button v-if="state.user_info.id === filtered_projects.author || state.user_info.admin === true" type="submit" class="second-btn btn">
Aktualisieren
</button>
</form>
<button v-on:click="deleteProject()" v-if="state.user_info.id === filtered_projects.author || state.user_info.admin === true" class="btn delete-btn">Löschen</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { computed, reactive } from "vue";
import { useStore } from "vuex";
import { useRoute, useRouter } from "vue-router";
import Sidebar from "../components/Sidebar.vue";
import Popup from "../components/Popup.vue";
import Navbar from "../components/Navbar.vue";
export default {
name: "ProjectDetails",
components: {
Sidebar,
Popup,
Navbar,
},
setup() {
const store = useStore();
const route = useRoute();
const router = useRouter();
store.dispatch("company_projects/getProjectList");
store.dispatch("user_info/getUserInfo");
store.dispatch("companies/getCompaniesList");
const state = reactive({
query: route.params.id,
company_projects: computed(
() => store.getters["company_projects/getProjectlist"]
),
user_info: computed(
() => store.getters["user_info/getUserInfo"]
),
companies: computed(
() => store.getters["companies/getCompanieslist"]
),
user_auth_data: computed(
() => store.getters["auth/getAuthData"]
),
});
const filtered_projects = computed(() => state.company_projects.find(obj => {
return obj.id == parseInt(state.query)
}))
async function submitProject() {
await store
.dispatch("company_projects/submitProject", {
id: state.company_project.id,
company: state.company_project.company,
author: state.company_project.author,
title: state.company_project.title,
text: state.company_project.text,
finish: state.company_project.finish,
})
.catch((err) => {
console.log(err);
});
}
async function deleteProject() {
await store
.dispatch("company_projects/deleteProject", {
id: state.company_project.id,
})
.catch((err) => {
console.log(err);
});
router.push("/project");
}
return {
state,
submitProject,
deleteProject,
filtered_projects,
route,
};
},
};
</script>
Do you know a way to solve it better than me?
Thank you very much.
Since you are dispatching the company_projects/getProjectList action (which I suppose is responsible for fetching data from the server) directly in the setup, it is very likely that at the moment your component will render for the 1st time, the company_projects/getProjectList getter (and consequently the state.company_projects computed) will return an empty array and thus filtered_projects is undefined (find)
Which means the part of your template which needs filtered_projects to have a value should be protected with v-if
<form class="edit-form" #submit.prevent="submitProject()" v-if="filtered_projects">

Vue : Accessing Nested Object Component's Values

I have problems accessing this "name" property on the component. I can only access it statically.
<template>
<div class="col-md-12">
<p
v-for="channel in channels"
:key="channel.id"
class="channel"
:class="{ 'active': channel.id == activeChannel }"
#click="setChannel(channel.id)">
{{ channel.users[0].name }}
</p>
</div>
</template>
Here is an Image of my Vue Devtools
So I have an v-for loop over channels, and I want to: Access the Usernames for each channel (if it is not my own preferably as "username" is set on my own i think its easy to exclude it right?) So that in the end In Channel 1 when there are 2 Users , I want to show the corresponding username, so the "other username", the one i am chatting with, and he should see my name that is the initial goal.
I thought of doing something like this:
<template>
<div class="col-md-12">
<p
v-for="channel in channels"
:key="channel.id"
class="channel"
:class="{ 'active': channel.id == activeChannel }"
#click="setChannel(channel.id)">
<!-- {{ channel.users[0].name }} -->
<span v-for="user,key in channel">{{key}}</span>
</p>
</div>
it at least displays the content of the channels object for each channel, but something like this isnt gonna work: key.user.name , unfortunately im stuck here. please help :)
edit: here is a dd() of the view
click
EDIT 2: Parent Data Provided:
//chat-app.blade.php
<div id="app">
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Chats</div>
<vue-chat :channels="{{ $channels }}" ></vue-chat>
</div>
</div>
</div>
</div>
</div>
</div>
//<vue-chat> component
<template>
<div class="chat">
<div class="container">
<div class="row">
<div class="col-md-3">
<vue-chat-channels
:channels="channels"
:active-channel="activeChannel"
#channelChanged="onChannelChanged"
:username="sername"
></vue-chat-channels>
</div>
<div class="col-md-3">
<vue-chat-messages :messages="messages"></vue-chat-messages>
</div>
<div class="col-md-3">participants</div>
</div>
<div class="message-input-wrapper col-md-12"><vue-chat-new-message :active-channel="activeChannel"
:username="username"></vue-chat-new-message></div>
</div>
</div>
</template>
<script>
export default {
props: ["channels"],
data() {
return {
activeChannel: this.channels[0].id,
messages: [],
username: ''
};
},
methods: {
fetchMessages() {
let endpoint = `/channels/${this.activeChannel}/messages`;
axios.get(endpoint).then(({ data }) => {
this.messages = data;
});
},
onChannelChanged(id) {
this.activeChannel = id;
this.fetchMessages();
}
},
created() {
this.fetchMessages();
axios.get('/userfetch').then( ({data}) => {
console.log("Current User: "+data.name);
this.username = data.name;
});
console.log(this.channels[0].name);
// for (let channel of this.channels) {
this.channels.forEach(channel => {
// Channelname
window.Echo.channel('presence-'+channel.name)
.listen('MessageSent', (channel) => {
console.log(channel.data.message);
this.messages.push({ message: channel.data.message, author_username: channel.data.author_username});
if (this.activeChannel == channel.id) {
console.log("received message");
}
});
});
}
};
</script>
<style>
</style>
//ChatController.php
public function index()
{
$channels = Channel::with('users')->whereHas('users', function($q) {
$q->where('user_id',Auth::id());
})->get();
$user = Auth::user()->name;
return view('chat-app' , compact('channels','user'));
}
Short Explanation: ChatController returns the blade view, which has the data channels and user (my username) , and then vue comes into play which should pass down the prop of my username but i couldnt get it to work just yet
So you need to access users in every channel.
You can try like this:
<div class="col-md-12">
<p
v-for="channel in channels"
:key="channel.id"
class="channel"
:class="{ 'active': channel.id == activeChannel }"
#click="setChannel(channel.id)">
<span v-for="user in channel.users">
{{ user.name }}
</span>
</p>
</div>
This should work. If you have errors provide it here.
If you need to compare every user you can do it simply with v-if:
<span v-for="user in channel.users">
<span v-if="user.name === parentdata">
{{ user.name }}
</span>
</span>

How to load Html layout into vuejs component?

I am using Vuejs cdn. In which I have a Navigation menu. I want to load different layout when a user clicks on Nav menu.
for example
<div class="row" id="mytemplate">
<div class="col-sm-3 sidebar-col">
<div>
<nav class="settings-sidebar">
<h4>Profile settings</h4>
<ul>
<li class="active" v-on:click="Profile()">
<a >Profile picture</a>
</li>
<li class="" v-on:click="Business()">
<a >Business details</a>
</li>
<li class="" v-on:click="Equipments()">
<a >Equipments details</a>
</li>
</ul>
</nav>
</div>
</div>
<div class="col-sm-9 col-xs-12 settings-body">
{{ load_layout_here }}
</div>
Now when user click on "Business Details" option it should run function Business() and load layout from business.html or business.php
the sample code in business.php
<div class="form">
<div class="input-field">
<h5>Business name</h5>
<input type="text" class="textbox" name="primary-phone" value="Perfect choice movers" placeholder="Your business name" maxlength="15">
</div>
<div class="input-field inline-input">
<h5>City</h5>
<input type="text" class="textbox " name="business-city" value="myCity" placeholder="Business address" maxlength="15">
</div>
<button class="btn btn-orange" type="submit">Save</button>
My vue.js code is
var app2 = new Vue({
el: '#mytemplate',
data : {
message : 'hi',
},
methods: {
Profile : function () {
this.message = 'Profile';
},
Business : function () {
// Here I want to call Html Template to load seprate layout
this.message = 'Business';
},
Equipments : function () {
this.message = 'Equipments';
}
}
})
I have seen lots of answers on the internet but they all using vue template engine or .vue files. Which I do not want to use
Note : I am using CodeIgniter as PHP framework. and Vue for frontend.
You can use axios to render the html. What ever data come in response data variable. That should to show.
Profile : function () {
axios.get('profile.php')
.then(function (response) {
console.log(response.data);
// response
this.message = response.data.json;
})
.catch(function (error) {
// handle error
console.log(error);
});
}
and to use axios, you have to include cdn file.
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

Vue js template rendering issue

First time with vue. I am learning it playing around with some examples from Laracasts. I cannot get external template to render and the console shows cannot find element: #toolbar-chat.
My template is:
<template>
<div id="toolbar-chat">
<div class="toolbar-chat">
<ul v-for="chat in messages">
<li><b>#{{ chat.nickname }} says:</b> #{{ chat.message }}</li>
</ul>
</div>
<div class="input-group input-group-sm">
<input class="form-control" value="" placeholder="Type message..." required="required" maxlength="140" v-model="newMsg">
<div class="input-group-btn">
<button class="btn btn-primary" type="button" #click="press">
<i class="fa fa-paper-plane"></i>
</button>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
console.log('Component mounted.')
},
data() {
return {
nickname: [],
message: ''
}
},
ready() {
Echo.channel(chat_channel)
.listen('ChatMessageWasReceived', (data) => {
// Push data to messages list.
this.messages.push({
message: data.chat.message,
nickname: data.player.nickname
});
});
},
methods: {
press() {
// Send message to backend.
this.$http.post(chat_send_route, {message: this.newMsg})
.then((response) => {
// Clear input field.
this.newMsg = '';
});
}
}
};
</script>
My HTML contains the following tag:
<div class="col-xs-12 col-md-4" id="toolbarChat">
<my-chat></my-chat>
</div>
My vue component call is inside a document ready function like this:
require('./../app/bootstrap');
$(document).ready(function()
{
....
// Set up chat
Vue.component('my-chat', require('./../generic/chat.vue'));
const app = new Vue({
el: '#toolbar-chat'
});
});
And I include vue in my bootstrap file like this, then compile with webpack and no errors.
window.Vue = require('vue');
Why is my HTML template not rendering?
In your HTML you have the following div:
<div class="col-xs-12 col-md-4" id="toolbarChat">
<my-chat></my-chat>
</div>
Change it to
<div class="col-xs-12 col-md-4" id="toolbar-chat">
<my-chat></my-chat>
</div>
Because that is the id that new Vue({el: "#toolbar-chat",...}) is looking for.

Setting the data context for a template defined by a package

Here's the relevant code template code:
<!-- display a list of users -->
<template name="available_user_list">
<h2 class="cha_heading">Choose someone to chat with:</h2>
<div class="row">
{{#each users}}
{{> available_user}}
{{/each}}
</div>
</template>
<!-- display an individual user -->
<template name="available_user">
<div class="col-md-2">
<div class="user_avatar">
{{#if isMyUser _id}}
<div class="bg-success">
{{> avatar user=this shape="circle"}}
<div class="user_name">{{getUsername _id}} (YOU)</div>
</div>
{{else}}
<a href="/chat/{{_id}}">
{{> avatar user=this shape="circle"}}
<div class="user_name">{{getUsername _id}}</div>
</a>
{{/if}}
</div>
</div>
</template>
<template name="chat_page">
<h2>Type in the box below to send a message!</h2>
<div class="row">
<div class="col-md-12">
<div class="well well-lg">
{{#each messages}}
{{> chat_message}}
{{/each}}
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<form class="js-send-chat">
<input class="input" type="text" name="chat" placeholder="type a message here...">
<button class="btn btn-default">send</button>
</form>
</div>
</div>
</template>
<!-- simple template that displays a message -->
<template name="chat_message">
<div class="chat-message">
{{> avatar user=user size="small" shape="circle"}} <div class="chat-center">{{user}}: {{text}}</div>
</div>
</template>
and the template helpers:
Template.available_user_list.helpers({
users:function(){
return Meteor.users.find();
}
})
Template.available_user.helpers({
getUsername:function(userId){
user = Meteor.users.findOne({_id:userId});
return user.username;
},
isMyUser:function(userId){
if (userId == Meteor.userId()){
return true;
}
else {
return false;
}
}
})
Template.chat_page.helpers({
messages:function(){
var chat = Chats.findOne({_id:Session.get("chatId")});
return chat.messages;
},
other_user:function(){
return ""
},
})
Template.chat_message.helpers({
user: Meteor.user()
})
I'm making an website where users can log in and chat with each other. I've downloaded the utilities:avatar package to show the avatars of the users. The avatar image is based on the first initial of the username of the user. When I render the avatar template in the template available_user with the code {{> avatar user=this shape="circle"}}, it shows the initials in the avatars fine because it's with the context of the users collection.
I also want to show the avatar when a user sends a message but the template chat_message is within the data context of an array inside the chats collection. So it only shows the default avatar without the initial.
the documentation for the package doesn't quite specify how I can set the template parameter for user or userId. Can someone please help with this issue?
I figured it out. I included the user id into each object in the messages array under the field userId and in the template I set it as {{> avatar userId=userId size="small" shape="circle"}}

Categories

Resources