OK. I am speechless. I encountered this weird behavior like two days ago and I can't really tell what is going on.
In my code I have:
character: Characters.find({
'user._id': Meteor.userId(),
'gameId': this.props.gameId
}).fetch(),
It is inside the getMeteorData function (I use Meteor with React), mixin [ReactMeteorData] is also present.
Now in the componentWillMount() function I have this piece of code. What I want to do is to check if there is a character inside created by this user and in this game.
componentDidMount: function() {
console.log(this.data.character);
}
It returns [Class] with the character I was looking for. Great! So now I add this piece of code and it looks like this:
componentDidMount: function() {
console.log(this.data.character);
if (this.data.character.length > 0) {
console.log('yay!');
} else {
console.log('nay...');
}
}
So that's a normal, unsuspicious if(). Guess what I get from that first console.log(): []. WHY? Why is it that this if is changing what I get from my DB?!
The problem was that subscriptions were not ready when I tried to use them. What I did is rewrite the way subscriptions are made. I moved it from the router (so no subscriptions there) to the component itself. Like this:
data = {}
subs = {}
subs.something = Meteor.subscribe("something", argument);
if (subs.something.ready()) {
data.something = Somethings.find({}).fetch();
// the same query I use in publish method but with .fetch()
// because otherwise Meteor throws a warning
}
This is the code that goes to the getMeteorData function. And then inside the render() I can use those subscriptions like this:
render: function() {
return(
<p>{this.data.something ? this.data.something.property : 'Loading...'}</p>
);
}
And then it works perfectly fine. I had rewritten all of my components to use this way of doing things and now I have NO problems with subscriptions whatsoever. It also feels more "componentish" and "reactish" as everything including subscriptions is included in the parent component and the relevant data is being passed to children via props. No need to look for code and subscription methods in the router.
Related
for clarify next part: payU is the Internet payment operator
I have a serious problem with integration my Angular app with payU payments. I won't store or pass credit card's data (security reasons) so I choose widget.
The first problem is how to place the widget in the code. Documentation says that I should place script in the following way:
<script
src="https://secure.payu.com/front/widget/js/payu-bootstrap.js"
pay-button="#pay-button"
merchant-pos-id="145227"
shop-name="Nazwa sklepu"
total-amount="9.99"
currency-code="USD"
success-callback="test"
sig="250f5f53e465777b6fefb04f171a21b598ccceb2899fc9f229604ad529c69532">
</script>
How you probably know, you can't set script in your code in this way in Angular so I decided use little walkaround:
ngAfterViewInit(): void {
this.script = document.createElement('script');
this.script.setAttribute('src', 'https://secure.payu.com/front/widget/js/payu-bootstrap.js');
this.script.setAttribute('pay-button', '#pay-button');
this.script.setAttribute('merchant-pos-id', '145227');
this.script.setAttribute('total-amount', '9.99');
this.script.setAttribute('currency-code', 'USD');
this.script.setAttribute('success-callback', 'test');
this.script.setAttribute('sig', '4752ce2b163684a9c27cc0923ad46068c04da5d34329f5669ce73dcf96394558');
this.renderer.appendChild(this.el.nativeElement, this.script);
}
I know it's not a perfect solution (if you know better way to do this, please let me know in comment.
But the main problem is pass name of callback function to success-callback attribute. I prepared function in my component, like:
test(arg: any) {
console.log(arg);
}
But I can't get this name. I was trying:
this.script.setAttribute('success-callback', this.test.name);
but property name is empty. Is there a simple way to get real name of method (after typescipt translating) in my component?
BTW.
Adding simple js script to index.html and providing its name works, but i need to call service within my function.
I'm using Angular v7.
Explanation:
Ok, let's start by explaining the script. Since the script is being added in the global namespace, the success callback refers to a global function with the name 'test' in your above code.
So we need a reference to the angular component's 'test' function in the global namespace of your app, so that it can be accessed on success callback.
In your component:
import {NgZone} from '#angular/core';
constructor(private zone:NgZone){
window.callbackComponentRef = {
testFn: (args) => {
this.zone.run(() => { this.test(args); })
}
};
}
test() {
//Whatever code you want to run
}
Then use in script addition code
this.script.setAttribute('success-callback', 'callbackComponentRef.testFn');
I'm new to meteor.js. Still getting used to it.
I get how templates update reactively according to the cursor updates on the server, like this:
{{#if waitingforsomething.length}} Something Happened! {{/if}}
This is good to display elements on the page, updating lists and content. Now, my question is: what if I want to call some javascript or fire some event when something gets updated reactively? What would be the right way to do it with meteor.js?
Anything inside Tracker.autorun or template instance this.autorun runs with changes in reactive data sources inside these autoruns.
Reactive data sources are ReactiveVar instances, db queries, Session variables, etc.
Template.myTemplate.onCreated(function() {
// Let's define some reactive data source
this.reactive = new ReactiveVar(0);
// And put it inside this.autorun
this.autorun(() => console.log(this.reactive.get()));
});
Template.myTemplate.events({
// Now whenever you click we assign new value
// to our reactive var and this fires
// our console.log
'click'(event, template) {
let inc = template.reactive.get() + 1;
template.reactive.set(inc);
}
});
It is a little bit outdated, but Sacha Greif's Reactivity Basics is a very quick and concise introduction to meteor's reactivity model.
Basically, you have what's called reactive computations, code that observes special data objects (sessions, subscriptions, cursors, etc.) and gets executed whenever any of these reactive sources changes.
This is exposed via the Tracker API
Computation works pretty well for me:
Template.myTemplate.onRendered(function() {
this.computation = Deps.autorun(function () {
if (something) {
$(".reactive").html("Something Happened!");
}
});
});
Template.myTemplate.destroyed = function(){
if (this.computation){
this.computation.stop()
}
};
I Hope this helps.
I curious if there is any agreed upon pattern to check if data has been already loaded before hitting the server.
Say I have my action that looks like this:
Actions.loadRequest.preEmit = function () {
$.get('/store/', function (data) {
Actions.loadSuccess(data);
}.bind(this));
}
This is called from a component that is simply saying give me this data:
But I don't want to hit the server if that data is already in the store.
Should I store the logic of checking the store in the component:
render: function () {
var data = this.state.store.data;
if (!data) {
Actions.loadRequest();
}
Is there a better way to go about this?
In my project I use shouldEmit for this (see https://github.com/reflux/refluxjs#action-hooks). An example from my code:
var streamStore = Reflux.createStore({
[...]
});
actions.loadStream.shouldEmit = function(streamId) {
if(streamId in streamStore.data)
return false;
return true;
};
This lives in the same file as the store definition. I think this is conceptually the right approach because the store saves the data, so the store should be responsible for intercepting the request to load more data and saying not to, just as it's responsible for listening to the action saying more data is available and updating itself.
Unfortunately this won't work with your example because you bound the AJAX call to preEmit, which gets called before shouldEmit. I would suggest refactoring to make the API call in a normal listen call, like this:
Actions.loadRequest.listen(function () {
$.get('/store/', function (data) {
Actions.loadSuccess(data);
}.bind(this));
});
This saves preEmit for the rare case of needing to rewrite an action's arguments before emitting it. I do use this pattern in my code, for example when loading a second page of results, which relies on a next token that came with the first page and is thus in the store. But in the general simple case of "action triggered, so make a request", using listen makes more sense because then you can add preEmit and shouldEmit for more advanced behavior, like the caching you want.
Reflux also has a helper function, listenAndPromise, which further simplifies the common use case of "action fired, make AJAX call, then fire another action when it's done". Your example could become:
Actions.loadRequest.listenAndPromise(function () {
return $.get('/store/');
});
See this section of the docs for more info on how to set that up: https://github.com/reflux/refluxjs#asynchronous-actions
I am using iron:router in my app and I have a controller that subscribes to one document in a collection by using a parameter in the path. I can access all of the documents in my collection on the server, so I know that there is stuff in there, but when I try to access the data that I subscribe to in the waitOn method of the controller, the data is undefined. Here is the relevant code for this problem.
Router code:
this.route('unit', { path: 'unit/:unitId', template: 'unit', controller: 'UnitController' });
UnitController = BaseController.extend({
waitOn: function () {
return Meteor.subscribe('getUnit', this.params.unitId);
},
data: function () {
var id = this.params.unitId;
templateData = {
unit: Collections.units.model(Collections.units.getUnit(id))
};
return templateData;
}
});
Publication:
Meteor.publish('getUnit', function(id) {
return Collections.units.data.find({ unitId: id });
});
Here I have created an object for various things to do with my collection(I only included the important parts here):
Collections.units = {
data: new Mongo.Collection("units"),
getUnit: function (id) {
return this.data.findOne({ unitId: id });
},
model: function(unitEntity) {
return {
unitId: unitEntity.unitId,
createdAt: unitId.createdAt,
packets: unitEntity.packets,
getLastPacket: function (id) {
return _.last(this.packets);
}
};
}
};
I have been trying to debug this for quite a while and I can do all the things to the collection I want to on the server and in the publish method, but when it gets to the controller, I can't access any of the info. In the data method this.params.unitId returns exactly what I want so that isn't the issue. Where the exception gets thrown is when I try to read properties of unitEntity when I'm making the model but that is just because it is undefined.
Have any ideas what I am doing wrong? Thanks in advance for any responses.
The main problem that I was trying to solve's solution was to wrap the code inside the data method inside of if (this.ready()){ ... } and add an action hook with if (this.data()) { this.render(); }. After I got the subscription to work, I found that Christian was right in the comments with saying that my controller setup might mess things up. It was causing other strange exceptions which I fixed by just moving the hooks to each route instead of using the controller. As for my Collections setup, it may be unconventional, but all of that is working fine (as of right now). I may want to set them up the standard way at a later point, but as of right now its pretty handy for me to do things with the collections with the methods already written in the object.
// Main class
function App() {
this.task = new Task(this); // pass the instance of this class to Task so
// it has access to doSomething
}
App.prototype.doSomething = function () {
alert("I do something that Task() needs to be able to do!");
};
function Task(app) {
// This class needs access to App()'s doSomething method
this.appInstance = app;
this.appInstance.doSomething(); // Great, now Task can call the method
}
var app = new App();
The aim of the code above is to give Task access to one of App's methods called doSomething. The code is the current way I'd go about it and I'm posting this to see if it's the best way...
To give Task access I simply pass the whole instance of App, is this efficient or is there a better way to go about it? Is the code above general practice in going about doing something like this?
Yes, what you have is fine. It is a circular dependency, however because of JavaScript's dynamic nature there aren't really any issues.
Another way you could reference App from Task would be a Singleton pattern or something similar, but that would probably be harder to test.
jsFiddle Demo
Generally bind would be used in this scenario assuming that the Task "class" didn't also setup other facilities which were not shown here.
Bind allows for the context to be provided for a function. This could be done in app's constructor. At which point only a function task would be required to call "someMethod".
function task(){
return this["someMethod"]();
}
function App(){
task.bind(this)();
}
App.prototype.someMethod = function(){
alert("Task needed access to this");
};
var a = new App();
However, if task must be a "class", and have other responsibilities then the prototype function could be shared.
function Task(){}
function App(){}
App.prototype.someMethod = Task.prototype.someMethod = function(){
alert("Task needed access to this");
};
var a = new App();
a.task();//->"Task needed access to this"
var t = new Task();
t.someMethod();//->"Task needed access to this"
Your app instances and task instances are tightly bound. App instances have tasks and this can be fine.
A design of loosely coupled objects is more flexible and easier to extend but more complicated to initially create. One such pattern is using a mediator/publish subscriber and have app raise an event/publish message any other object function can listen to this and take action on the event.
For example: your app creates an Ajax instance and when that instance is done it raises some event (fetchedData for example). A listener could be DomDependent.updateView function but later you may want to add/remove/change the order of tasks to do after data is fetched. This can all be configured in a app.init function or per procedure in a controller that kicks of certain procedures (like log in, search, ...).
Instead of creating a whole bunch of specific functions in Ajax (fetchUserPrefs, login, search, ...) you can create one general function and have the controller add listeners or pass the next event when fetchData is complete to run the correct next function.
Here is some pseudo code:
var app = {
init:function(){
mediator.add("updateLogin",domDependent.updateView);
mediator.add("updateLogin",app.loadUserPrefs);
mediator.add("failLogin",domDependent.updateView);
},
login: function(){
mediator.trigger("loadingSometing",{type:"login"});
ajax.fetch({
onComplete:"updateLogin",//what listens to updateLogin you decided in init
onFail:"failLogin",
loginDetails:domDependent.getLogin(),
url:settings.loginUrl,
type:"post"
});
}
}
var ajax = {
fetch:function(data){
data = data || {};
//simple check for onComplete, it's mandatory
var complete = data.onComplete || app.raiseError("ajax.fetch needs onComplete");
//other code to validate data and making ajax request
onSuccess:function(resp){
//mutate data object as the mediator will pass it to
// whatever other function is called next
// you don't hard code domDependent.updateView and
// app.loadUserPrefs because fetch can be used generally and
// success may have to do completely different things after its done
// and you want to define procedures in init, not all over your code
data.response=resp;
//trigger event to do whatever needs to be done next
mediator.trigger(complete,data);
}
}
}
As you can see it gets complicated and maybe doesn't look like code you're used to but it's highly configurable.
I may have misunderstood the advantages of the mediator pattern to loose couple and if so please comment. I use it to:
Make methods more general instead of copying a lot of logic only
because what to do after it's done is different. In fetch the ajax
object just fetches, this would be the same for login or getting
user preferences, the only thing different is what function to call
next/on error when it's done.
A procedure like login involves multiple functions in multiple
objects if this function chain hard code what to do next once a
particular function is done your procedure of login is defined all
over your code. When defining it in init/config you can easily change the
order or add/remove functions in the chain.