Meteor: Error: {{#each}} currently only accepts arrays, cursors or falsey values - debugging

I keep getting this error message when I click on the send button. Im trying to create a Instant Messenger app where online users can chat one on one. I am a beginner and I would really appreciate any help. Here is my error message, again it appears in the console once I click the Send button.
Exception from Tracker recompute function: meteor.js:862 Error:
{{#each}} currently only accepts arrays, cursors or falsey values.
at badSequenceError (observe-sequence.js:148)
at observe-sequence.js:113
at Object.Tracker.nonreactive (tracker.js:597)
at observe-sequence.js:90
at Tracker.Computation._compute (tracker.js:331)
at Tracker.Computation._recompute (tracker.js:350)
at Object.Tracker._runFlush (tracker.js:489)
at onGlobalMessage (meteor.js:347)
Here is my HTML
<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...">
<input type="submit" value="Send">
</form>
</div>
</div>
</template>
<!-- simple template that displays a message -->
<template name="chat_message">
<div class = "container">
<div class = "row">
<img src="/{{profile.avatar}}" class="avatar_img">
{{username}} said: {{text}}
</div>
</div>
<br>
</template>
Client Side
Template.chat_page.helpers({
messages: function () {
var chat = Chats.findOne({ _id: Session.get("chatId") });
return chat.messages;
},
other_user: function () {
return "";
}
});
Template.chat_page.events({
'submit .js-send-chat': function (event) {
console.log(event);
event.preventDefault();
var chat = Chats.findOne({ _id: Session.get("chatId") });
if (chat) {
var msgs = chat.messages;
if (! msgs) {
msgs = [];
}
msgs.push({ text: event.target.chat.value });
event.target.chat.value = "";
chat.messages = msgs;
Chats.update({ _id: chat._id }, { $set : { messages: chat } });
Meteor.call("sendMessage", chat);
}
}
});
Parts of the server side
Meteor.publish("chats", function () {
return Chats.find();
});
Meteor.publish("userStatus", function () {
return Meteor.users.find({ "status.online": true });
});
Meteor.publish("userData", function () {
if (this.userId) {
return Meteor.users.find({ _id: this.userId },{ fields: { 'other': 1, 'things': 1 } });
} else {
this.ready();
}
return Meteor.users.find({ "status.online": true });
});
Meteor.publish("users", function () {
return Meteor.users.find({ "status.online": true });
});
Chats.allow({
insert: function () { return true; },
update: function () { return true; },
remove: function () { return true; }
});
Meteor.methods({
sendMessage: function (chat) {
Chats.insert({
chat: chat,
createdAt: new Date(),
username: Meteor.user().profile.username,
avatar: Meteor.user().profile.avatar,
});
}
});

Chances are your subscriptions aren't ready. This means that Chats.findOne() will return nothing, meaning that Chats.findOne().messages will be undefined.
Try the following:
{{ #if Template.subscriptionsReady }}
{{#each messages}}
{{/each}}
{{/else}}
Alternatively, use a find() on chats, then {{#each}} on the messages within that chat. For example:
Template['Chat'].helpers({
chats: function () {
return Chats.find(Session.get('chatId')); // _id is unique, so this should only ever have one result.
}
});
Then in template:
{{#each chats}}
{{#each messages}}
{{>chat_message}}
{{/each}}
{{/each}}

I think there might be a logical error in this line
Chats.update({ _id : chat._id }, { $set : { messages : chat } });
You are setting the value of the field messages to chat. But chat is an object. So in your helper when you are returning Chats.findOne().messages to the {{#each}} block, you are actually returning an object which is not a valid value to be sent to an {{#each}} block and hence the error.
I think what you mean to do is
Chats.update({ _id : chat._id }, { $set : { messages : msgs } });

Related

Ajax how change value id to text database?

I have a problem that is the value displayed in the database is the id value not the value of the text that appears in the input label. then my question is how to change the id value to a text value?
route web.php
`Route::get('/selectProvinces', [App\Http\Controllers\Admin\ManagementController::class, 'provinces'])->name('province.index'); `
ajax script
<script>
$(document).ready(function () {
$("#selectProvinces").select2({
placeholder: 'Pilih Provinsi',
ajax: {
url: "{{route('province.index')}}",
processResults: function ({
data
}) {
return {
results: $.map(data, function (item) {
return {
id: item.id,
text: item.name
}
})
}
}
}
});
</script>
blade.php boostrap
<div class="row">
<label for="formGroupExampleInput2">Provinsi</label>
<div class="col">
<select id="selectProvinces" class="form-control selectpicker" name="provinces">
</select>
</div>
</div>
controller
public function management_create(Request $request) {
Member::create([
'provinces' => $request->provinces,
]);
Alert::success('Success', 'Member berhasil dibuat');
return redirect()->route('management');
}
i hope the value changes text value like "East Java" not the number

How do I display instant messages, when insecure is removed, with Meteor.methods and Meteor.call?

I am taking coding courses online, so I can build my app sometime next year...
Can you help me with this instant message code please?
a. I am supposed to display an alert message when the user is not logged in.
b. Display the usename in the header.
c. Display the username with his instant message.
Since insecure is removed, I have to use Meteor.methods and meteor.call. I cannot use Sessions. I keep getting weird errors...
Here is the javascript code I have tried based on the course module, but I get errors that don't make sense to me...
Messages = new Mongo.Collection("messages");
if (Meteor.isClient) {
// this will configure the sign up field so it
// they only need a username
Accounts.ui.config({
passwordSignupFields: 'USERNAME_ONLY',
});
Template.messageForm.events({
// this event listener is triggered when they click on
// the post! button on the message form template
'click .js-save-message': function (event) {
var messageText = $('#message-text-input').val();
// notice how tihs has changed since the lsat time
// now we read the username from the Meteor.user()
var messageNickname = "Anon";
if (Meteor.user()) {
messageNickname = Meteor.user().username;
}
var message = {
messageText: messageText,
nickname: messageNickname,
createdOn: new Date()
};
// HERE is where you come in ....
// call a meteor method
// on the server here instead of
if (Meteor.isServer) {
Meteor.methods({ // defines a method, adds extra security layer to app
insertMessage: function () {
var doc, user, euser;
doc = Message.findOne();
if (!doc) {
return;
} // no logged in user, give up
// now I have a doc and possibly a user
user = Meteor.user().profile;
eusers = insertMessage.findOne({ docid: doc._id });
if (!eusers) {
eusers = {
docid: doc._id,
users: {},
};
}
user.lastEdit = new Date();
eusers.users[this.userId] = user;
insertMessage.upsert({ _id: eusers._id }, eusers);
}
}
)
}
// comment out this code, which won't work as we removed insecure...
//Messages.insert(message); // the insecure way of doing it
// ... put code here that calls the
Meteor.call('insertMesage', message, function (err, res) {
if (!res) {
alert('You need to log in!');
}
});
Template.header.helpers({
// HERE is another one for you - can you
// complete the template helper for the 'header' template
// called 'nickname' that
// returns the nickname from the Session variable?, if they have set it
nickname: function () {
if (Meteor.user()) {
return Meteor.user().username;
}
},
});
Template.messageList.helpers({
// this helper provides the list of messages for the
// messageList template
messages: function () {
return Messages.find({}, { sort: { createdOn: -1 } })
}
});
},
});
}
Here is the html file
<body>
{{>header}}
{{>nicknameForm}}
{{>messageList}}
{{>messageForm}}
</body>
<template name="header">
<h1>Welcome to M-Instant {{nickname}}</h1>
</template>
<template name="messageList">
{{#each messages}}
{{>messageItem}}
{{/each}}
</template>
<template name="messageItem">
<h3>{{nickname}} - {{messageText}}</h3>
</template>
<template name="nicknameForm">
<div class="form-group">
<label for="nickname-input">Nickname:</label>
<input type="text" class="form-control" id="nickname-input"
placeholder="Type message here...">
<button type="submit" class="btn btn-default js-set-nickname">Set my
nickname!</button>
</div>
</template>
<template name="messageForm">
<div class="form-group">
<label for="message-text-input">Message:</label>
<input type="text" class="form-control" id="message-text-input"
placeholder="Type message here...">
<button type="submit" class="btn btn-primary js-save-message">Post!
</button>
</div>
</template>
Here is the Methods file
Meteor.methods({
'insertMessage':function(message){
console.log("If you manage to call the method, you'll see this
message in the server console");
if (!Meteor.user()){
return;
}
else {
return Messages.insert(message);
}
}
})

Search functionality with rest api prevent DDOSing the server

The Problem
I have a search component and component which implements the search component. When I type something in the search bar after 1/2 second of not typing (debounce) the server should be hit and the results should be returned.
The solution i am trying to implement comes from this post on Stackoverflow
The code
This leads me to the following code.
I have search.vue
<template>
<label for="search">
<input
id="search"
class="w-full py-2 px-1 border-gray-900 border"
type="text"
name=":searchTitle"
v-model="searchFilter"
:placeholder="searchPlaceholder"
autocomplete="off"
v-on:keydown="filteredDataset"
/>
</label>
</template>
<script>
import {debounce} from 'lodash';
export default {
props: {
searchPlaceholder: {
type: String,
required: false,
default: ''
},
searchName: {
type: String,
required: false,
default: 'search'
}
},
data() {
return {
searchFilter: '',
}
},
methods: {
filteredDataset() {
console.log('event fired');
this.$emit('searchValue', this.searchFilter);
}
},
}
</script>
And product.vue
<template>
<div>
<div class="my-4">
<search
search-placeholder=""
search-name=""
v-on:searchValue="filterValue = $event"
v-model="productsFiltered"
>
</search>
<div class="flex w-full py-1 border px-2 my-2" v-for="product in productsFiltered"> (...)
</div>
</div>
</div>
</div>
</template>
<script>
import {debounce} from 'lodash';
export default {
data() {
return {
products: [],
filterValue: '',
filteredProducts: ''
}
},
computed: {
productsFiltered: {
get(){
console.log('getter called');
return this.filteredProducts;
},
set: _.debounce(function(){
console.log('setter called');
if (this.filterValue.length < 1) {
this.filteredProducts = [];
}
axios.get(`${apiUrl}search/` + this.filterValue)
.then(response => {
this.products = response.data.products;
const filtered = [];
const regOption = new RegExp(this.filterValue, 'ig');
for (const product of this.products) {
if (this.filterValue.length < 1 || product.productname.match(regOption)) {
filtered.push(product);
}
}
this.filteredProducts = filtered;
});
}, 500)
}
},
}
</script>
The result
The result is that the setter in the computed property in product.vue does not get called and no data is fetched from the server. Any ideas on how to solve this?
Your first code block imports debounce but does not use it. It also declares a prop, searchName, that isn't used. These aren't central issues, but clutter makes it harder to figure out what's going on.
Your second code block uses v-model but does not follow the required conventions for getting v-model to work with components:
the component must take a prop named value
the component must emit input events to signal changes to value
You have the component emit searchValue events, and handle them with a v-on that sets a data item. You seem to expect the v-model to call the setter, but as I noted, you haven't hooked it up to do so.
From what's here, you don't even really need to store the input value. You just want to emit it when it changes. Here's a demo:
const searchComponent = {
template: '#search-template',
props: {
searchPlaceholder: {
type: String,
required: false,
default: ''
}
},
methods: {
filteredDataset(searchFilter) {
console.log('event fired');
this.$emit('input', searchFilter);
}
}
};
new Vue({
el: '#app',
data() {
return {
products: [],
filterValue: '',
filteredProducts: ''
}
},
components: {
searchComponent
},
computed: {
productsFiltered: {
get() {
console.log('getter called');
return this.filteredProducts;
},
set: _.debounce(function() {
console.log('setter called');
if (this.filterValue.length < 1) {
this.filteredProducts = [];
}
setTimeout(() => {
console.log("This is the axios call");
this.filteredProducts = ['one','two','three'];
}, 200);
}, 500)
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
<template id="search-template">
<label for="search">
<input
id="search"
class="w-full py-2 px-1 border-gray-900 border"
type="text"
name=":searchTitle"
:placeholder="searchPlaceholder"
autocomplete="off"
#input="filteredDataset"
/>
</label>
</template>
<div id="app">
<div class="my-4">
<search-component search-placeholder="enter something" v-model="productsFiltered">
</search-component>
<div class="flex w-full py-1 border px-2 my-2" v-for="product in productsFiltered"> (...)
</div>
</div>
</div>

Vue Object is Empty

I'm using Vuejs to display data from an API to a template. I'm trying to figure out why I am not returning data for the team so I can display in for the template. Right now when I use the VueJS Chrome Extention it shows that the team is an empty object.
<div v-if="team">
<div class="row">
<div class="col-12 col-sm-12">
<div class="fw-700 pb3 mb5" style="border-bottom: 1px solid #333;">Name:</div>
<div class="mb10" style="font-size: 20px;">{{ team.name }}</div>
</div>
</div>
<script>
module.exports = {
http: {
headers: {
'X-CSRF-TOKEN': window.Laravel.csrfToken
}
},
props: [ 'id', 'editable' ],
data: function(){
return {
modalName: 'additionalInfo',
team:{
}
}
};
},
methods: {
fetchInfo: function(){
this.$http.get('/api/teams/info', { params: { id: this.id } }).then((response) => {
this.team = response.data;
});
},
},
}
}
</script>
It is empty because your method fetchInfo isn't being called, so you need to do something like this:
created () {
this.fetchInfo()
}
This will call the function fetchInfo which in turn will fetch and fill this.team

Laravel Echo isn't listening to the Broadcast event

I am trying to broadcast the event when a user creates a post to all his friends. Laravel Echo isn't listening to the events. I can see the logs on the Pusher site. So the event is being sent to the pusher.
Here's my timeline code:
<template>
<div>
<div class="row">
<status-box :authUser="authUser"></status-box>
</div>
<div class="row">
<div class="col-lg-8">
<div v-if="statuses.length">
<status v-for="status in statuses" :status="status"></status>
</div>
<div v-else>
<p>No posts to show yet.</p>
</div>
</div>
</div>
</div>
<script>
import EventHub from './../eventhub.js';
export default {
data() {
return {
statuses: [],
authUser: null,
};
},
methods: {
pushNewStatus(status) {
status.parent_id = null;
this.statuses.unshift(status);
// Broadcasting the event to so that user's friends can see the post in realtime
Echo.private('status.' + status.id)
.listen('StatusWasPosted', (e) => {
this.pushNewStatus(e.status);
});
}
},
mounted() {
EventHub.$on('status-posted', this.pushNewStatus)
axios.get('/chatty/getStatuses/')
.then(({data}) => {
this.statuses = data.statuses;
this.authUser = data.user_name;
})
.catch(error => console.log(error));
}
}
Here's the channel authorization logic:
Broadcast::channel('status.{statusId}', function ($user, $statusId) {
$status = Status::find($statusId);
$status_owner = User::find($status->user_id);
return $user->isFriendsWith($status_owner);
});
Here's the channel name in the StatusWasPosted event class
public function broadcastOn() {
return new PrivateChannel('status.' . $this->status->id);
}
I am emitting status-posted in the status compoenet when a new status is created.
Where am I going wrong??

Resources