Rendering Meteor Templates in React Components - meteor

Lets stay I have a loginbutton.html file with template {{loginButton}}.
<template name="loginButton">
//rest of code
</template>
How could I render this in a react component. I've looked around and found a couple of answers but to be honest I need more guidance bc I'm fairly new to Meteor. Below is a link to a possible answer I found. I just don't know how to implement this or if this is what i'm actually looking for.
https://gist.github.com/emdagon/944472f39b58875045b6

I finally solved the issue. If you're trying to use a blaze template in a react component you have to make sure your meteor app is using "templating" on the client side (and in a package which is what I was using). Afterwards just follow the directions from the link above when creating your component passing the template in as a prop:
Base.components.photoButton = React.createClass({
componentDidMount: function() {
var componentRoot = React.findDOMNode(this);
var parentNode = componentRoot.parentNode;
parentNode.removeChild(componentRoot);
this.view = Blaze.render(this.props.template, parentNode);
},
componentWillUnmount: function() {
// Clean up Blaze view
Blaze.remove(this.view);
},
render: function() {
return (<div/>
)
},
}));

There is a nice atmosphere package blazetoreact.
It's very simple. If you have this Blaze template:
<template name="loginButton">
<!-- rest of code -->
</template>
Then you can use it as React component:
const LoginButton = BlazeToReact('loginButton');
class SomeComponent extends React.Component {
render() {
return (
<div>
<LoginButton />
</div>
);
}
}

Related

Passing data to props after asynchronous call in Vue

I have set up a bare bones vue project to show the problem. The only thing I added was the axios package. The problem is when I try to set the property of child component after an asynchronous call I cant read that property in the component. If you look at the code you can see I console log several times to show when I can get the data and when I cant. Please help me figure out what im missing here.
Parent
<template>
<div id="app">
<HelloWorld :test_prop="testData" :test_prop2="testData2" :test_prop3="testData3" test_prop4="I work also"/>
<div>{{testData5}}</div>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
import axios from 'axios';
export default {
name: 'app',
components: {
HelloWorld
},
data() {
return {
testData: '',
testData2: 'I work just fine',
testData3: '',
testData5: ''
}
},
created: function(){
var self = this;
this.testDate3 = 'I dont work';
axios.get('https://jsonplaceholder.typicode.com/posts/42').then(function(response){
//I need this one to work
self.testData = 'I dont work either';
self.testData5 = 'I work also';
});
}
}
</script>
Child
<template>
</template>
<script>
export default {
name: 'HelloWorld',
props: ['test_prop', 'test_prop2', 'test_prop3', 'test_prop4'],
data() {
return {
comp_data: this.test_prop,
comp_data2: this.test_prop2,
comp_data3: this.test_prop3,
comp_data4: this.test_prop4
}
},
created: function(){
console.log(this.test_prop);
console.log(this.test_prop2);
console.log(this.test_prop3);
console.log(this.test_prop4);
}
}
</script>
Your console.log inside created hook will show you the initial state of this variables in Parent component. That's because Parent's created hook and Child's created hook will run at the same time.
So, when you solve your promise, Child component was already created. To understand this behavior, put your props in your template using {{ this.test_prop }}.
To solve it, depending on what you want, you can either define some default value to your props (see) or render your child component with a v-if condition. That's it, hope it helps!
On Vue created hook only the initial values of properties passed from main component. Therefore later updates (like your example "after ajax call") in main component will not effect to child component data variables because of that already child created hook take place.
If you want to update data later one way you can do like this:
watch: {
test_prop: function(newOne){
this.comp_data = newOne;
}
}
Adding watcher to property changes will update the last value of property from main component.
And also edit the typo this.testDate3. I guess it must be this.testData3

Filter Collections using iron-router

I have a template rendering my Collection using {{#each Collection}}, I'm using iron-router to route this template like this:
Router.route('/home', function () {
this.render('home');
this.layout('header');
});
how can i use a "books" filter button so iron-router apply that filter to the subscription so i can only see the filtered version of my Collection like the following:
Collection.find({category: "books"});
There are a number of ways to approach this but one simple way would be to define a sub-route that takes a category as a parameter:
Router.route('/collections/:category',{
name: 'collections',
layoutTemplate: 'header',
data(){
return Collections.find({category: this.params.category});
}
});
Then your button code can just do Router.go('/collections/books') but you can now have multiple buttons each tied to different categories.
As suggested here https://guide.meteor.com/data-loading.html#organizing-subscriptions you can manage your subscription in Template and not in the route. So you can do:
import {ReactiveDict} from 'meteor/reactive-dict';
import {Template} from 'meteor/templating';
Template.home.onCreated(function(){
var that = this;
that.state = new ReactiveDict();
//that.state.set('selected-category',YOUR_DEFAULT_CATEGORY);
that.autorun(function(){
that.subscribe('subscription-name',that.state.get('selected-category'));
});
});
Template.home.events({
'click .js-category':function(e,tpl){
let category = tpl.$(e.currentTarget).data('category');
tpl.state.set('selected-category',category);
}
});
And in your Template:
<button type="button" data-category="books" class="js-category">Books</button>
Hope this will help you to find the right solution for your

How to share data between components in VueJS

I have a fairly simple VueJS app, 3 components (Login, SelectSomething, DoStuff)
Login component is just a form for user and pass input while the second component needs to display some data obtained in the login progress.
How can I share data from one component to the others? So that when I route to second component I still have the data obtained in the Login one?
You can either use props or an event bus, where you'll be able to emit an event from a component and listen on another
vm.$on('test', function (msg) {
console.log(msg)
})
vm.$emit('test', 'hi')
// -> "hi"
In Vue.js components can communicate with each other using props or events. It all depends on the relation between your components.
Let's take this small example:
<template>
<h2>Parent Component</h2>
<child-component></child-component>
</template>
To send information from the parent to Child, you will need to use props:
<template>
<h2>Parent Component</h2>
<child-component :propsName="example"></child-component>
</template>
<script>
export default {
data(){
return{
example: 'Send this variable to the child'
}
}
}
</script>
To send information from the child to the parent, you will need to use events:
Child Component
<script>
...
this.$emit('example', this.variable);
</script>
Parent Component
<template>
<h2>Parent Component</h2>
<child-component #example="methodName"></child-component>
</template>
<script>
export default {
methods: {
methodName(variable){
...
}
}
}
</script>
Check the documentation of vue.js for more information about this subject. This is a very brief introduction.
Use this small plugin if you have a lot of nested components:
Vue.use(VueGlobalVariable, {
globals: {
user: new User('user1'),
obj:{},
config:Config,
....
},
});
Now you can use $user in any component without using props or other

Simple button not showing

I'm trying to just get off the ground with Meteor 1.2.1 and am failing miserably.
I've simply used the code from this question but always receive a blank page. If I remove the Button class, there's no problem with getting the div to appear or text inside it.
I receive no console errors.
My added packages:
twbs:bootstrap 3.3.6
universe:react-bootstrap 0.24.0
react 0.14.3*
Code:
if (Meteor.isClient) {
Meteor.startup(function () {
let App=React.createClass({
render: function () {
return (
<div>
<Button>Default</Button>
</div>
);
}
});
React.render(<App/>, document.getElementById("container"));
});
}
I expect that whatever I'm missing is very simple, but can't narrow it down other then reac-bootstrap being the cause.
Did you require/import the Button component anywhere in your code? Maybe that's what was missing.
In my ignorance, I simply did not follow the universe:react-bootstrap documentation.
As a global
This package additionally export ReactBootstrap as a global, so you
can write inside any .jsx file:
let { Button } = ReactBootstrap;
<Button /> // React component
or
<ReactBootstrap.Button /> // React component
let { Button } = ReactBootstrap;
if (Meteor.isClient) {
Meteor.startup(function () {
let App=React.createClass({
render: function () {
return (
<div>
<Button>Default</Button>
</div>
);
}
});
React.render(<App/>, document.getElementById("container"));
});
}

Configuring Iron Router in Meteor - React

Using Meteor 1.2.0.1 and React. My simple app works great but now I needed iron router.
app layout:
client\
app.jsx
lib\
router.jsx
server
views\
home.jsx
layout.jsx
home.jsx:
Home = React.createClass({
render() {
return (
<div>
<h3>Hello World</h3>
<p>from home</p>
</div>
);
}
});
layout.jsx:
Layout = React.createClass({
render() {
return (
<div>
{this.props.children}
</div>
);
}
});
routes.jsx:
Router.route('/', () => {
let page = (
<Layout>
<Home/>
</Layout>
);
React.render(page, document.body);
});
Surely enough, in my app.jsx, works great as it displays to the body of the html but the setup would not work for a multi-page app so this is the need for routes. Inside is:
Meteor.startup(() => {
let app = (
<Layout>
<Home/>
</Layout>
);
React.render(app, document.body);
});
The question is, how to get iron router (routes.jsx) to show the contents?
I would strongly recommend using Flow Router instead of Iron Router. Add Flow Router to your app, then add kadira:react-layout as well. Follow this format and it should work:
FlowRouter.route('/', {
name: 'home',
action() {
ReactLayout.render(Layout, {content: <Home />});
}
});
FlowRouter.route('/login', {
name: 'loginPage',
action() {
ReactLayout.render(Layout, {content: <Login />});
}
});
And your Layout component should look like:
Layout = React.createClass({
render() {
return (
<div>
<Header />
{this.props.content}
</div>
);
}
});
To route to a page that takes a parameter:
FlowRouter.route('/detail/:id', {
name: 'prodDetail',
action() {
ReactLayout.render(Layout, {content: <ProdDetail />});
}
});
And then in your ProdDetail component, you can refer to FlowRouter.getParam('id'). Check out the full FlowRouter documentation.
The solution was to add the ejson package which solved the issue, thanks to Chris. But I can easily follow Flow Router so I'll mark (since I'll be using it) that the answer but for anyone that has this issue, use the ejson package. However then my resolver over time.

Resources