decorator inside component does is not updated with data - ractivejs

Notwithstanding the advice in this stackoverflow question, I'm still having trouble with decorators being updated with the correct data. Unlike the cited question, now the decorator is inside a component. Here's the code (ref: this jsfiddle):
the html:
<div id="container" />
<script type='template/racive' id='member_template'>
<p id='name_{{name}}' decorator='sayname:{{name}},{{id}}'>
{{id}}:{{name}}
<a href='#'>say my name</a>
</p>
</script>
and the coffeescript:
Sayname = (node,name)->
console.log "new decorator: #{name}"
$(node).find('a').on 'click',->
alert "I'm saying: #{name}"
teardown : ->
console.log "teardown #{name}"
$(node).off('click')
update : ->
console.log "update #{$(node).attr('id')} to #{name}"
Ractive.decorators.sayname = Sayname
Member = Ractive.extend
template: "#member_template"
window.family = new Ractive
el : '#container'
template: "{{#members}}<member name='{{name}}' id='{{id}}' />{{/members}}"
data:
members: [{id:1, name:"Barney"},
{id:2, name:"Fred"},
{id:3, name:"Wilma"},
{id:4, name:"Pebbles"}]
components:
member: Member
console.log "init complete"
family.set('members',[{id:5,name:"Sneezy"},{id:6,name:"Sleepy"},{id:7,name:"Happy"},{id:8,name:"Grumpy"}])
After initialization and after the dataset is updated, clicking the link with decorator-supplied behaviour still returns the original data, not the updated data.
Any suggestions what is wrong here? Thanks in advance for any insight offered.

Two things:
1) The decorator function should return an object with both a teardown method and optionally an update method if you want it to be updatable
2) In the update method, the new params are supplied you need to use them to update the decorator state.
With your example, in my best coffeescript, in would be (see https://jsfiddle.net/964qvry9/1/):
Sayname = (node,name)->
console.log "new decorator: #{name}"
$(node).find('a').on 'click',->
alert "I'm saying: #{name}"
decorator =
teardown : ->
console.log "teardown #{name}"
$(node).off('click')
update : (newName) ->
console.log "update #{$(node).attr('id')} to #{name}"
name = newName
For this use case, there's no need to resubscribe to the click event, but often the pattern is helpful for cases where you need to re-run the teardown and init logic:
function myDecorator( node, param ){
function init(param) {
//do setup work
}
function teardown() {
//do teardown work
}
init(param);
return {
teardown() { teardown(); },
update(param) {
teardown();
init(param);
}
}
}

Related

How do to log a Error Callback in SQLite?

I am trying to connect to a database using the code below:
import SQLite from 'react-native-sqlite-storage'
var db = SQLite.openDatabase({name : "banco.db", createFromLocation : 1}, this.successCB(), this.errorCB());
errorCB() {
this.setState({message: "I NEED SHOW THE ERROR HERE"});
}
successCB() {
this.setState({message: "SQL executed fine"});
}
How to show the error on the errorCB function?
This is in the documentation example. The error callback will get passed an argument containing the error. You are also not providing the correct value to openDatabase. You should be passing in functions, not trying to call the function.
Copy pasting the relevant parts from the documentation with comments to explain:
// Your error callback function that should take an argument that will contain your error.
errorCB(err) {
console.log("SQL Error: " + err);
// Here you can use err in your setState call.
}
openCB() {
console.log("Database OPENED");
}
// openDatabase should be passed in the functions; openCB and errorCB in this example.
var db = SQLite.openDatabase("test.db", "1.0", "Test Database", 200000, openCB, errorCB);
// What you're doing is incorrect as it's akin to doing this which is wrong.
// var db = SQLite.openDatabase("test.db", "1.0", "Test Database", 200000, openCB(), errorCB());
This is really more of a basic JavaScript question where you need to be able to read the documentation and understand how to use a given API. If you're having trouble with this, I suggest reading up about Higher-Order Functions as it's fundamental to JavaScript.
Edit: To be very direct and answer the comments; this is what your code should look like:
import SQLite from 'react-native-sqlite-storage'
var db = SQLite.openDatabase({name : "banco.db", createFromLocation : 1}, this.successCB, this.errorCB);
// Making the assumption that these are in a class,
// otherwise add the const keyword before them.
// Convert these to arrow functions instead
// so they can more easily be passed as variables.
errorCB = (err) => {
this.setState({message: err});
}
successCB = () => {
this.setState({message: "SQL executed fine"});
}
Given your comments, I'll be very direct here. If you don't understand how functions, Higher-Order Functions, and variables/values work in JavaScript, you are going to have a very difficult time with React Native. Especially so if you are unfamiliar with ES6 syntax. Go through the book in that link or one of the many other great resources for learning the fundamentals of JavaScript before tackling React Native.
I used to open SQLite using following statement
database = SQLite.openDatabase({name: 'my.db', location: 'default'}, (db) => {
db.transaction( tx => {
tx.executeSql(`CREATE TABLE IF NOT EXISTS tableName (columnNames)`);
}, error => {
this.setState({message: error});
});
}, error => {
this.setState({message: error});
});
Hope this will help!

Is there a way to update the URL in Flow Router without a refresh/redirect?

Is there a way to update a part of the URL reactively without using FlowRouter.go() while using React and react-layout?
I want to change the value in the document that is used to get the document from the DB. For example, if I have a route like ~/users/:username and update the username field in the document, I then have to user FlowRouter.go('profile', {data}) to direct the user to that new URL. The "old" route is gone.
Below is the working version I have, but there are two issues:
I have to use FlowRouter.go(), which is actually a full page refresh (and going back would be a 404).
I still get errors in the console because for a brief moment the reactive data for the component is actually wrong.
Relevant parts of the component are like this:
...
mixins: [ReactMeteorData],
getMeteorData() {
let data = {};
let users = Meteor.subscribe('user', {this.props.username});
if (user.ready())
data.user = user;
return data;
}
...
updateName(username) {
Users.update({_id:this.data.user._id}, {$set:{username}}, null, (e,r) => {
if (!e)
FlowRouter.go('profile', {username});
});
},
...
The route is like this:
FlowRouter.route('/users/:username', {
name: 'profile',
action(params) {
ReactLayout.render(Main, {content: <UserProfile {...params} />});
}
});
The errors I get in the console are:
Exception from Tracker recompute function:
and
TypeError: Cannot read property '_id' of undefined

Meteor.autorun() not working on client when insert occures

i have been knocking my head for 2 days now in that .
am creating a search engine, am creating queries dynamically using Meteor Framwork, the queries are working fine and when i search i can rebind the UI (Table in My Case) with the dynamic data query output.
however if an insert/update/delete operation occures the data object
and the UI (html Table) is not updating.
which means that the template is not re-rendered when the data object changes.
Template.search.rendered = function () {
Meteor.autorun(function() {
alarmsData = Alarms.find(getSearchSelector($('#searchTxt').val(), $('#startTimeTxt').val(), $('#endTimeTxt').val())).fetch()
console.log("rendered")
//alarmsData = Alarms.find({},{sort: {timestamp: "desc"} }).fetch();
searchControls(alarmsData)
getConsole(alarmsData, ".console")
$('#badge').html(alarmsData.length)
})
}
the get console function is just reading the array from teh search and creating an html table (this is working fine)
as for the begining i am creating a simple query as the default for my search. and then am changing this query whenever user changes the search criteria. i can notice that only the first instance of teh data object is kept and tracked for changes, so if the second search criteria resides within the first one, it's updating the UI, if not nothing happenes
i have used Meteor.autorun(function(){}) function however i traced it's execution with console.log and i can see it's no excuting when i insert data in the database for the same collection.
One, I believe you are trying to use Deps.autorun. Also, there is nothing in your autorun that seems to be dependent on a reactive source. Since alarmsData is taking a snapshot of data it won't care when Alarms has data changing.
Second, I would probably approach this with a redirect. I would compile my data, and redirect to the same page, allowing the server to handle the querying for me. This easily allows you to jump to this page from anywhere else with a prefilled query in the parameters (because the route would then handle it) and also gives a visual change to the navigation bar when a search has happened (just like every other search engine). You would do something like this on a button click:
var query = {},
path;
query.text = encodeURIComponent($('#searchTxt').val()),
query.start = encodeURIComponent($('#startTimeTxt').val()),
query.end = encodeURIComponent($('#endTimeTxt').val()),
// redirect to current path
path = Router.routes[Router.current().route.name].path({}, {
query: query
});
Router.go( path );
In your router you would just pass the query into your server and route as a data object (assuming you are using iron-router):
this.route( "search", {
path: "/search",
waitOn: function() {
return [
Meteor.subscribe( "searchAlarms", _.omit( this.params, "hash" ) ),
]
},
data: function () {
return { "query": _.omit( this.params, "hash" ) };
}
});
This will not only give you the query data that was used for the search (in your template) but the server can now handle the search for you! Your Alarms data now holds all of the documents needed to display to the user and you no longer need to subscribe to all your Alarms. This is also great because it is automatically reactive. So if a new Alarm matches your query filter it will automatically be passed down to the client and displayed to the user without needing to setup any extra dependencies/autoruns.
Note though, that if you are subscribing to Alarms elsewhere you will still need to do filtering client-side.
What a strange meteor code…
The "rendered" code method code is called once you will be rendering the search template
getSearchSelector($('#searchTxt').val() is not reactive, my advise is to use the session variable to put your search criteria inside and use this same session to inject the find parameters inside.
Are you looking for displaying all the alarms Data ?
function getAlarms()
{
var text = Session.get("text");
var from = Session.get("start");
var to = Session.get("end");
var filter = getSearchSelector(text, from, to);
return Alarms.find(filter);
}
Template.search.alarms = function () {
return getAlarms();
}
Template.search.alarmsCount = function () {
return getAlarms().count();
}
Template.search.events({
'keypress input[name=text]' : function(e,o)
{
var val = $("input[name= text]").val()
Session.set("text", val);
},
'keypress input[name=start]' : function(e,o)
{
var val = $("input[name=start]").val()
Session.set("start", val);
},
'keypress input[name=end]' : function(e,o)
{
var val = $("input[name=end]").val()
Session.set("end", val);
}
});
// And your template will look something like:
<template name="search">
Search alarms
<input type="text" name="text" placeholder="Enter your text here…"/>
<input type="text" name="start" placeholder="start time"/>
<input type="text" name="end" placeholder="end time/>
There is {{alarmsCount}} alarms(s);
{{#each alarms}}
Alarm object: {{.}}
{{/each}}
</template>
I Guess its Solved it by using Session.set & get, and automatically subscribing to the Serevr and send the dynamic Query.
Check the below Code
Template.zConsole.rendered = function () {
Session.set("obj", getSearchSelector($('#searchTxt').val(), $('#startTimeTxt').val(), $('#endTimeTxt').val()))
Deps.autorun(function (){
Meteor.subscribe("dynamicAlarms", Session.get("obj"))
console.log("Count from AutoRun ==> " + Alarms.find(Session.get("obj")).count())
})
}
on the server
Meteor.publish('dynamicAlarms',function (searchObj) {
return Alarms.find(searchObj)
})
& it works perfect with less code.

Meteor minimongoid update()

So I have already written a fairly large app in meteor but just adding minimongoid package to it. I have figured out it likes first() rather than findOne(), create() rather than insert(), but I can't figure out how to update a document. I am trying to do the following, but it shows the error below... What am I doing wrong?
Transactions.update {_id: txn._id},
$set:
"isActive": false
TypeError: Object function Transactions() {
I20140302-18:22:54.226(-5)? return Transactions.__super__.constructor.apply(this, arguments);
I20140302-18:22:54.226(-5)? } has no method 'update'
All I have in my postings.coffee is
class #Transactions extends Minimongoid
#_collection: new Meteor.Collection('transactions')
With minimongoid you update object instances rather then with Class methods.
Something like this:
aTransaction = Transaction.create({-some attributes-})
aTransaction.update({attributeName: attributeValue})
I am still new to MeteorJS but let me try. This took a while before I figured out on my own. Good thing we use RoR at work. What I am going to do is something like:
on server JavaScript: (If you use Rails, this should be easy to get.)
Meteor.methods
updateTransaction: (id, name, url) ->
Transaction.find(id).update(
"isActive": status
)
then on my client JavaScript (template):
Template.transaction_template.events
'submit .transaction-form': (event, template)->
event.preventDefault() // if using an <a>, remove is using button with type=button
status = template.find('[name=status]').value // find the input form with the name=status
Meteor.call 'updateTransaction', { id: #_id, status }

How to work with callbacks in meteor?

I've created a helper for displaying a generic modal in my Meteor app (in coffeescript).
Here's modal.coffee:
showModal = (modalType, title, body, callback) ->
validModals = [ "Error", "YesNo" ]
if not modalType in validModals
alert "Invalid modal type specified" # #todo - find a better way of handling this error
Session.set "modalType", modalType
Session.set "modalTitle", title or ""
Session.set "modalBody", body or ""
modalCallback = callback or undefined
Session.set "showModal", true
Template.modal.title = () ->
Session.get "modalTitle"
Template.modal.body = () ->
Session.get "modalBody"
Template.modal.response = () ->
switch Session.get "modalType"
when "Error"
[{
css: 'cancel',
message: 'OK'
}]
when "YesNo"
[
{
css: 'cancel',
message: "No"
},
{
css: 'btn-primary',
message: "Yes"
},
]
Template.page.shouldShowModal = () ->
Session.get "showModal"
Template.modal.events {
'click .cancel': ->
Session.set "showModal", false
cb = modalCallback
alert "here " + cb
if cb
cb(false)
'click .btn-primary': ->
Session.set "showModal", false
cb = Session.get "modalCallback"
if cb
cb(true)
}
The template it quite boring.
Here's my client code (as in the invoker of this helper):
Template.details.events {
'click .remove': () ->
showModal "YesNo",
"Are you sure you want to delete this item?",
"Deleting an items can't be undone. Are you sure you want to delete?",
(response) =>
if response
Items.remove this._id, (err) =>
if err
showModal "Error", "Error removing item", err.reason
return false;
}
I can't get it execute the callback. All of the examples I've seen end up putting everything into the session, but apparently it can't convert the function to json so it doesn't deserialise it correctly when a user clicks on the ok or cancel buttons.
How can I execute a callback when a user responds to my modal?
The Parties example demonstrates another pattern, which I like. Use a session variable to show/hide the dialog/modal, and put the callback in a Template event handler.
For example, see the inviteDialog template here:
https://github.com/meteor/meteor/blob/master/examples/parties/client/parties.html#L194
This session variable controls its visibility:
https://github.com/meteor/meteor/blob/master/examples/parties/client/parties.html#L15
This is a callback:
https://github.com/meteor/meteor/blob/master/examples/parties/client/client.js#L257
I think the problem with the code you have there is that the variable modalCallback is local to the function (this is coffeescript, after all).
However, as you probably realise, this approach isn't really the right way to do it. The problem is that because the callback isn't being saved anywhere beyond a global variable, it will get lost in the case of a hot code push. (i.e. try hitting save on a file in your meteor project whilst the dialog is open, and see what happens when you subsequently close it).
This is actually a really good question, and I've been thinking a lot about the best approach. The best answer I have right now is to use a global reactive callback. Something like this:
Meteor.autorun(function() {
if (Session.equals('lastDialog', 'alert-dialog') &&
Session.equals('dialogOpen', false) &&
Session.equals('lastDialogSuccess', true)) {
alert("I'm alerting");
// ensure it doesn't happen again, e.g. on another HCP
Session.set('lastDialog', null);
}
});
That code needs to be at the top level, so it re-runs on HCP. Take a look at this project I've set up to try and figure out a better way to do it: https://github.com/tmeasday/dialog-experiment/tree/global-reactivity
Perhaps a better answer is 'do things differently, in a more declarative/reactive way'. I'm not sure.

Resources