New js DDP client (SimpleDDP)

The subscription method uses the spread operator. This means that I can add any kind of second parameter to the api.sub method including an array.

About the find function. Its indeed a non-reactive function. I’m doing this to make it easy to include it as SSR feature. It still needs to be connected to Vue’s reactivity system, but I want to wait with it, because of your next release.

The reactive part will also become a Vuex feature since that store uses convinient methods to pull this off easily.

That said. Not sure if this is the right approach. I’m always open for feedback :slightly_smiling_face:

About the fetch part. Thanks for that. The example code for find should not have had the await part. I will remove this :joy:

I made a PR :slight_smile: You can check it out.

I see your point, I am not familiar with Vue.js, but I am going to make reactivity by mutating the returned object. So it will be a plain js array or object anyway, just like now. Is it ok for SSR?

@gregivy Congratulations and thank you very much for this amazing work! I am developing a chrome extension connected to a Meteor back-end and was upset with the DDP clients currently available.

One question I have open is: Since it is not possible to require modules on chrome extensions without a huge workaround, is there a way I can use your DDP directly on my project? (I mean, instead of installing through NPM and then requiring)

Thanks!

It sounds like you need to build it from npm and put it on a cdn?

Unfortunately CDN’s are not accepted at all by chrome extensions anymore…

Thank you, @patrickcneuhaus! You can use webpack or a much simpler solution like browserfy.

Just install it and install simpleDDP

npm install -g browserify
npm install simpleddp

Then include simpleDDP in one of your js files like below and use it as you wish

var simpleDDP = require("simpleddp").default;

And run

browserify your_js_file.js -o bundle.js

The bundle.js is the file you want to use in you extensions (it will contain both your code and simpleDDP)!

If you will have any problems with this approach, let me know, I have not tried this approach with google chrome extensions myself.

2 Likes

@gregivy Thanks for the instructions!

I followed them, but it seems like Chrome Extensions are really not capable of handling “require” without further complications.

I get this error on chrome://extensions right after loading the folder:

Uncaught ReferenceError: require is not defined

Any ideas how to solve that?

@patrickcneuhaus I have no errors, I used this guide https://medium.freecodecamp.org/how-to-create-and-publish-a-chrome-extension-in-20-minutes-6dc8395d7153 and included simpleDDP.

Check out my repo.

Inside index.js I have my code related to simpleDDP. Then I run inside the project folder

browserify index.js -o ./extension/bundle.js

And inside extension/newtab.html I connect my bundle with <script> tag.

<!doctype html>
<html>
  <head>
    <script src="bundle.js"></script>
    <title>Test</title>
  </head>
  <body>
    <h1>Hello World!</h1>
  </body>
</html>

Now folder extension is ready to be used as a google chrome extension!

2 Likes

It worked! Thank you very much for this! :slight_smile:

What do I need to do to call methods through simpleDDP on my chrome extension’s popup?
Should I import { simpleDDP } from bundle.js on my popup.js file?

You have few options:

  • Polute window scope inside index.js compile it to bundle.js and use it inside popup.js:
//index.js
window.simpleDDP = require("simpleddp").default;
<script src="bundle.js"></script>
<script src="popup.js"></script>
// popup.js
// now simpleDDP is global variable on window object

var server = new simpleDDP(/*parameters*/);
  • Use only bundle.js, put you popup.js code inside index.js, compile it to bundle.js and that is it.
1 Like

The above approach works fine, except for the callback of the call…
When I run the code below, the method is called (because the console log appears), but its callback is not:

chrome-extension/index.js

server.call('testMethod',function(error,result){
       $("#test").text(result);
})

meteor/imports/api/methods.js

Meteor.methods({
       'testMethod': function( ){
            console.log('Test');
            return 'Testing the function';
       }
});

What I know from other DDPs is that sometimes calls don’t return callbacks, but rather promises or something different. I also tried the code below, without success:

const testCall = server.call('testMethod')
testCall.done(function(result){
        $("#test").text(result);
});

How does simpleDDP support callbacks?

call returns a promise, so

server.call('testMethod').then(function(result) {
  $("#test").text(result);
}

check DOCS

2 Likes

@gregivy You’re on fire, man. I’m well impressed with this work.

2 Likes

@gregivy Thanks, it worked!

1 Like

Thanks :smiley:, @babrahams

I think I have to hold over a bit publishing version 1.2.0 because right now I am deeply testing it with different mobile web views and have some problems with scenarios like the lose of the internet connection and reconnection some time after etc.

Hello!

News

  1. I found some really disturbing bug!
    If you connect to meteor server, subscribe for some publication, disconnect for long enough (>10s), make some changes in the publishing data and then reconnect again - simpleDDP local collection will be messed up. Instead of updating changed documents to the latest state, simpleDDP will make a duplicate with the same id inside its local collection. This happens because the newly created subscription does not know anything about it previous state and will send added ddp messages.
    This behavior is fixed in 1.2.0.
    However there are some questions left with removed ddp sync in the same scenario.
  1. I decided to push my 1.2.x branch on github, so you can check it out, but there is no documentation yet.
    https://github.com/Gregivy/simpleddp/tree/1.2.x
  2. I have replaced ddp.js with simpleddp-core. It is the same old ddp.js but with PRs included and few bugs excluded :slight_smile:
  3. Right now I am making tests, fixing some errors, improving API and reading Meteor repo about DDP. There are a lof of things I need to consider in 1.2.0.
  4. I am planning to make a good guide and push it to github pages, so if someone is willing to help and knows good constuctor or something like this you’re welcome :wink:
1 Like

Thanks for the heads up! I’m looking for a way to make this pluggable since React, Vue and Angular all have slightly different ways of connecting their Reactivity system. Any thoughts on it? right now I’m actually considering mapping onChange, etc to the Vue Store and create a local DB’ish storage there.

1 Like

I am successfully using in my Ionic v3 application (so in other words it is Angular 4 app) this approach (P.S. This is current 1.2.0 API):

// somewhere in the constructor
this.userId = localStorage.getItem('userid');

this.server = new simpleDDP({
      endpoint: 'ws://someserver.com/websocket',
      SocketConstructor: WebSocket,
      reconnectInterval: 1000
});

this.server.sub('usersub');
this.userCursor = server.collection('users')
                  .filter(user=>user.id==this.userId)
                  .reactiveOne({preserve:true});
this.user = this.userCursor.data;
<!--some class template -->
<div *ngIf="user.id && user.badges?.length > 0">
  <h1>My Badges</h1>
  <div *ngFor="let badge of user.badges">
    <img [src]="badge.picurl">
    <span>{{badge.title}}</span>
  </div>
</div>

This means that this.user is a reactive data source, when there is no data it is an empty object, when some changes happen it is being mutated to the right state. The key word is mutated, so the object remains the same until you remove this.userCursor.

The code reactiveOne({preserve:true}) means that we want a reactive data source as a first object passing previous conditions (filter in my example), and {preserve:true} means that if object is being removed by the subscription it stays untouched in simpleDDP collection storage.

If we use reactiveOne() or reactiveOne({preserve:false}) then there will be empty object {} in case of publication removes it. But it will be the same object, not some new {}.

If you need more complex behavior you can use onChange:

this.userObserver = this.userCursor.onChange({prev,next,fields}=>{
   // prev is previous state of the object or false if none
   // next is new state of the object or false if none
   // fields is an associative array of changed fields inside the document 
   // (value 1 means property was changed, 0 means removed)
  if ( prev && next && prev.score > next.score) {
     alert('You lost some points!');
  }
});

You can read more about onChange here.

And there is also reactive() for reactive collection or filtered/sorted collection.

Hello!

SimpleDDP 1.2.0 is 75% ready!

You can watch project status by opening the link below.

2 Likes

Nice work, I have two questions:

  • Is it possible to make a Meteor.call()? Don’t find it
  • Implement collection cache import/export. => like Minimongo? Will it be possible to save a Collection in AsyncStorage//localStorage?

Keep it up

1 Like