Meteor publish and subscribe not working - javascript

I have a fairly novice question and am hoping someone can help me.
I have the following code in which subscribe/publish is not working:
if (Meteor.isServer) {
Meteor.publish('items', function () {
return Items.find({});
});
}
if (Meteor.isClient) {
Meteor.subscribe("items");
}
In my template:
<template name="items">
<div class="ui segment">
<div class="ui relaxed list">
{{#each item in items}}
<div class="item">
<img class="ui avatar image" src="http://placehold.it/20x20">
<div class="content">
<a class="header">{{item.text}}
</div>
<button class="delete">×</button>
</div>
{{/each}}
</div>
</div>
</template>
But nothing gets output. However when I add:
Template.items.helpers({
items: function () {
return Items.find({});
}
});
I do see the list properly. Why is this so? Also I am clearly confused as to why someone would want to use subscribe/publish along with the template helpers.

I suggest you read Data flow from the database to the UI: Three layers of Meteor
You are creating a publication labeled: items. Then you are subscribing to a publication labeled deals. This will not work as the labels must match for the subscription to work.
If adding that template helper then shows the data in the UI, you must have the autopublish package in your app. It will be autopublish, not your pub/sub, that is sending the client the data the client puts in it's mini-mongo Items collection.
So pub/sub gets the data from the server to the client, but doesn't display it. That is why you need the template helper, to get the data from the client's mini-mongo collection into the format the templates require.

You must subscribe() using the same name as used in the publish(). In your case (pay attention to 'items'):
/server:
Meteor.publish('items', function () {
return Items.find({});
});
/client:
if (Meteor.isClient) {
Meteor.subscribe('items');
}
The publish/subscribe link is telling meteor to send all docs in the 'Items' collection to minimongo on the client. Now, the purpose of the template helper is to tell meteor that you want to reactively update the template any time a document changes in Items.
A good reference on the subject: https://www.discovermeteor.com/blog/understanding-meteor-publications-and-subscriptions/

Related

Why does my routing not work in Ember Js?

I'm working on a todoApp with a rails API and frontend as Ember.
I've followed this tutorial Ember todo App
However, it a bit old and I kinda lost in my routing.
I've a todos.hbs which should be rendered localhost:4200/, but it is a clear page.
Here's what my router.jslooks like :
import EmberRouter from '#ember/routing/router';
import config from './config/environment';
const Router = EmberRouter.extend({
location: config.locationType,
rootURL: config.rootURL
});
Router.map(function() {
this.route('todos', { path: '/' });
});
export default Router;
And my routes/todos.js:
import Route from '#ember/routing/route';
export default Route.extend({
model: function() {
return this.store.find('todo');
}
});
On my application.hbs, there's only an ```{{outlet}}
and my todos.hbs looks like this :
<section id="todoapp">
<header id="header">
<h1>todos</h1>
{{input type="text" id="new-todo" placeholder="What needs to be done?" value=newTitle action="createTodo"}}
</header>
<section id="main">
<ul id="todo-list">
{{#each itemController="todo"}}
<li {{bind-attr class="isCompleted:completed isEditing:editing"}}>
{{#if isEditing}}
{{input insert-newline=(action "acceptChanges")}}
{{else}}
{{input type="checkbox" checked=isCompleted class="toggle"}}
<label {{action "editTodo" on="doubleClick"}}>{{title}}</label>
<button {{action "removeTodo"}} class="destroy"></button>
{{/if}}
</li>
{{/each}}
</ul>
<input type="checkbox" id="toggle-all">
</section>
</section>
<footer id="info">
<p>Double-click to edit a todo</p>
</footer>
{{outlet}}
So now, I dont know where the mistake is, it only rendered me a blank page. If anyone can explain what is wrong into my work, I would appreciate.
UPDATE
I've found that a troubles appear in my application.js, here's what it looks like now, if that can help :
import DS from 'ember-data';
export default DS.RESTAdapter.extend({
host: 'http://localhost:3000/api'
});
As Joe has already mentioned, store.find('todo') has been removed and from what I can see it was removed in 2015 but I am not 100% sure about that 🤔 You are likely to find other large issues following such an old tutorial and using the latest Ember. Have you seen the official tutorial on the Ember Guides site? https://guides.emberjs.com/release/tutorial/ember-cli/ there is always a lot of people making sure that it is as up-to-date as possible!
I will continue to try to answer your question but I do strongly recommend that you try a slightly more modern tutorial in the meantime 😂
Joe is indeed correct that you need to update the call in your route to use the updated method this.store.findAll('todo') and you should be able to see that recommended in your console output if you bring it up (you can usually right-click anywhere on the page and select 'Inspect Element' and then click the console tab)
As you can see the error says:
Using store.find(type) has been removed. Use store.findAll(modelName) to retrieve all records for a given type. Error: Assertion Failed: Using store.find(type) has been removed. Use store.findAll(modelName) to retrieve all records for a given type.
Once I update that method so we're using findAll() it gives me a new error:
Error while processing route: todos No model was found for 'todo' Error: No model was found for 'todo'
Which means I need to create the file models/todo.js. If you do look at a more modern tutorial it will probably recommend that you use a generator to generate this file such as: ember generate model todo.
I then added your adapter and this is where I need to stop because I don't have the backend server running on my local machine so it will always fail for me 😄
You can see the code I was using to test this here: https://ember-twiddle.com/14bab300c47493c10a9de69efd591092
If you haven't used Ember Twiddle before I would highly recommend it if you are trying to ask a question here on Stack Overflow, on the Ember Discord server or on the Ember forums. If you are able to recreate your issue on an Ember Twiddle then you are very likely to be able to get a helpful response 🎉
Let me know if you have any questions and I hope this has been helpful 👍
I noticed that your routes/todos.js calls this.store.find('todo');. Using store.find(type) has been removed. I think you have to call this.store.findAll('todo');

How to make Laravel/Vue implementation show individual entry

I'm trying to integrate Laravel with Vue, and further down the line Nuxt, in the hope that I can integrate snazzy page transitions like the ones shown on http://page-transitions.com into my websites.
I've been reading a tutorial about using Vue with Laravel; https://scotch.io/tutorials/build-a-guestbook-with-laravel-and-vuejs, and I was pleased to find that Laravel ships with a Vue implementation, so I thought there'd be quite a lot of info on how to use the two in combination, but there doesn't seem to be.
I completed the tutorial and made the guestbook as it was described. I'm now trying to build upon that.
Specifically, Im trying to create individual pages for each of the guestbook entries.
I do have quite a bit of experience using Laravel, but only what I've described above with Vue.
So, in order to create the individual pages, I've created a new route in the routes/web.php file;
Route::get('signature/{id}','SignaturesController#show')->name('signature');
I've then created a new code block in app/Http/Controllers/SignaturesController.php to deal with this request;
public function show()
{
return view('signatures.signature');
}
I've created the specified view in resources/views/signatures/signature.php;
#extends('master')
#section('content')
<div class="container">
<div class="row">
<div class="col-md-12">
<signature></signature>
</div>
</div>
</div>
#endsection`
And I've created the vue file that should integrate with this view in resources/assets/js/components/Signature.vue;
<template>
<h1>Signature</h1>
</template>
<script>
export default {
}
</script>
Finally, I've registered the component in resources/assets/js/app.js and reran npm run dev.
This has worked to an extenet, I can view the file at the expected url; http://transitions.localhost/signature/1.
My question is, how do I get the data related to the signature with the ID of 1 into the page? I can't even echo out {{ id }} or {{ signature }}.
Any other resources that you've found helpful regarding this subject would also be greatly appreciated. Thanks for taking the time to read through all of that, does anyone know where I go from here?
You will need to pass the data to your vue component
Maybe something like this?
In your view:
#section('content')
<div class="container">
<div class="row">
<div class="col-md-12">
<signature :signature="{{ $signature }}"></signature>
</div>
</div>
</div>
#endsection
In your vue component:
<template>
<h1>This signature has the ID of: {{ signature.id }}</h1>
</template>
<script>
export default {
props: ['signature']
}
</script>

Is it possible to switch collection focus in Meteor spacebars templates?

I am trying to display some relational data in a Meteor spacebars template. Specifically I two collections, Location and Weather. They look something like this:
Location {
_id: 'full-name',
title: 'Full name',
lat: '123.456',
long: '123.456',
order: 1
}
Weather {
locName: 'full-name', // this name always matches a location _id
temperature: '41.3'
}
I'd like to display information from both of these collections on a single page. So that I can show the latest weather from each location (there are 4-20 of them per page). To do this, I've published a Mongo request of both collections like so on the server side:
Meteor.publish('allLocations', function() {
return [
Locations.find({}, { sort: { order: 1 } }),
Weather.find({}) // The weather
]
});
I then subscribe to this publication in my router (iron-router):
Router.map(function() {
this.route('locations', {
waitOn: function () {
return Meteor.subscribe('allLocations');
}
}
});
However, I get stuck when I get to my spacebars template. I can't figure out the syntax for switching collection focus in spacebars.
Here's the psuedo-code for the template that I'm trying to parse, but I know that this doesn't currently work.
<template name="locations">
<div class="locations-grid">
{{#each locations}}
<div class="location {{_id}}">
This is the location template
<h1>{{title}}</h1>
{{#each weather}}
<!-- Trying to pass the _id along to the weather template for filtering -->
{{> weather _id}}
{{/each}}
</div>
{{/each}}
</div>
</template>
<template name="weather">
This is the weather template
{{#with weather}}
<!-- Ideally, we've now switched contexts to the weather collection -->
<h2>Temperature: <div class="temp">{{temperature}}</div></h2>
{{/with}}
</template>
So my question is, where do I tell spacebars to switch contexts to the weather collection? How can I pass along the _id variable to the weather template so that I can select the right data from the collection? I know I'm missing a big step here, I just can't figure out which portion of the Meteor space to examine. I know I might need to specify a subscription for the weather template, but I'm not sure where to do that since it's not really a route, since it won't have its own page. It just lives as a sub-template within the locations template.
Thanks for any tips, or possible suggestions on restructuring.
Before we begin, please read A Guide to Meteor Templates & Data Contexts - that will correctly orient you on contexts inside of #each blocks.
Your goal is to join the correct weather documents to their corresponding location documents. This is most easily accomplished by introducing sub-templates for both types. Let's begin with the top-level template:
<template name="locations">
<div class="locations-grid">
{{#each locations}}
{{> location}}
{{/each}}
</div>
</template>
Which has a locations helper like this:
Template.locations.helpers({
locations: function() {
return Locations.find();
}
});
Next, the location template:
<template name="location">
<div class="location">
<h1>{{title}}</h1>
{{#each weathers}}
{{> weather}}
{{/each}}
</div>
</template>
Which has a weather helper like this:
Template.location.helpers({
weathers: function() {
return Weather.find({locName: this._id});
}
});
The key insight here is that the context of a location template is a single location document so weather will return only the weather documents for this location instance. Finally, your weather template can look like:
<template name="weather">
<h2>Temperature: {{temperature}}</h2>
</template>
Note we are now in a weather context so the #with is no longer needed.
Side note - using sort in your publisher has no affect in this case.

Create sub controller without route in Ember.js

I'm building a chat app with ember.js(I'm very new to it) that has the following specifics:
Every user have multiple threads each one with only two users involved
I need to display the thread and the messages in a "facebook" manner with the list of threads aside and the messages right of it
Every message have a read state that I need to work with
The app works with an url pattern like /:thread_id
Giving theese, I've already setted up an app
https://gist.github.com/Fed03/33da4a7c28c792af23cf (I've merged the various js file for sake of your readability) but the problem rises on the specific message state.
From what I understand about ember, evrything "needs" a route in order to proxy things, but if you look at the code and at the specifics a don't need a message route but I'd need a MessageController to manage the single message states.
Obviously I'm doing something very wrong with the architecture of this app, so if someone cuold give me some advices would be great!
Thank you in advance!
You'll want to use a {{render}} helper.
Instead of rendering each model in the threads template, pass the model to a {{render}} and have it handle displaying the message and it'll have it's own controller.
You'll need to create a seperate template for the msg, like this:
<script type="text/x-handlebars" data-template-name="message">
<div class="col-md-12">
<div class="well">
<div class="message-header">
<h5>{{user.fullname}}</h5>
</div>
{{body}}
{{isRead msg}}
</div>
</div>
</script>
And then change your thread template to this:
<script type="text/x-handlebars" data-template-name="thread">
{{#each msg in model.messages}}
{{render 'message' msg}}
{{/each}}
<div class="col-md-12">
<div class="row">
<div class="col-md-5">
{{textarea value=message rows="3" class="form-control" data-thread=model}}
<div>
<button class="btn btn-primary pull-right" {{action 'send' model}}>
Submit
</button>
</div>
</div>
</div>
</div>
</script>
Then each msg will have it's own instance of MessageController and you can use it to handle it's state.

How Do I Properly Make a Meteor Template Reactive?

My app displays a collection of items and I'd like to add an item drilldown view.
I've only been able to get half of the solution working; I find the appropriate document and use that document to render the template:
var item = Items.findOne('my_id');
$('#main').html(
Meteor.render(function () {
return Templates.item(item)
}));
This renders the individual item successfully and the appropriate events are bound.
Here's the rub, the template isn't reactive! If I change its data using the associated event handlers or from the console, the template isn't updated. However, a page refresh will reveal the updated data.
I'm a Meteor noobie, so it's probably something very simple. Any help would be greatly appreciated.
It seems to me that you aren't using the templates in they way they were really intended to be.
A meteor app starts with the main html markup which can only exist once in your app..
<head>
<title>My New Fancy App</title>
</head>
<body>
{{>templateName}}
</body>
Then you add a template..
<template name="templateName">
{{#each items}}
template or relevant html goes here..
{{/each}}
</template>
Now you need a template helper to give you data for your {{#each items}} block helper..
Template.templateName.helpers({
items: function(){ return Items.find({}) }
});
All this gets defined on the client side..
Then you'll need a collection and the collection should be defined on both the client and server.
Items = new Meteor.Collection('items');
This should now work as long as you have records in your collection.
Since you wish to only wish to render a single document you can change the helper and template just slightly..
first the helper becomes:
Template.templateName.helpers({
item: function(){ return Items.findOne() }
});
Then the template can reference the values of the returned document through document, so we change our template to:
<template name="templateName">
{{item.propertyName}}
</template>

Categories