I have a BackboneJS & MarionetteJS app
class MyApp extends Marionette.Application
app = new MyApp
app.addRegions
tag_container :"#tag_container"
item_container :"#item_container"
app.addInitializer( =>
app.items = new ItemCollection()
app.item_container.show(new ItemListView({collection:app.items}))
)
In my ItemCollection,
class ItemCollection extends Backbone.Collection
model :ItemModel
url :"/get_items"
initialize: =>
#search()
search: =>
#reset()
#fetch()
Above code displays the ItemListView immediately and adds the items as they are being fetched.
How can I wait until the collection has finished fetching THEN display the ItemListView in the "item_container"?
Either use a listener to know when the collection has finished his job. You can listen to reset if you're resetting it (not the default behavior as of Backbone 0.9.10) or sync (always done).
Or you can use the success callback.
Source
A third solution would be to do a synchronous fetch:
#fetch async: false
fetch returns a jquery promise. so you can just do
collection.fetch().done(function() {
// create & show the view
});
Related
Normally in django with templates I implement basic notifications like this.
For example.
class Article(models.Model):
name = models.CharField()
owner = models.ForeignKey(User)
class Comment():
article = models.ForeignKey(Article)
txt = models.CharField()
user = models.ForeginKey()
datetime = models.DateTimeField(auto_now_add=True)
class ArticleNotification():
article = models.ForeignKey(Article)
msg = models.CharField()
is_seen = models.BooleanField(default=False)
datetime = models.DateTimeField(auto_now_add=True)
If someone commented on article the owner will see notifications.
#transaction.atomic
def post_comment(request, article_id):
comment = Comment.objects.create(article_id=article_id, txt="Nice Article", user=request.user)
ArticleNotification.objects.create(article_id=article_id, msg=f"User {request.user} commented on your post")
Now to show the notifications I normally make a context processor:
# context_processor:
def notifcations(request):
notifs = Notfication.objects.filter(article__owner=request.user).order_by("-datetime")
return {"notifs":notifs}
In this way I can normally implement basic notification system with refresh.
Now in (drf + react) what will be the preferred way for this type of task.
Instead of context processor should I have to make an get api to list notifications
And call this api on every request from react frontend ?
Instead of context processor should I have to make an get api to list notifications
Yes. You can create DRF API view like this
serializers.py
class ArticleNotificationSerializer(ModelSerializer):
class Meta:
model = ArticleNotification
fields = ["id", "article", "msg", "is_seen", "datetime"]
views.py
class ArticleNotificationListView(ListAPIView):
serializer_class = ArticleNotificationSerializer
queryset = ArticleNotification.objects.all()
urls.py
path('notification', ArticleNotificationListView.as_view()),
And call this api on every request from react frontend ?
Yes. Also you can check for Notifications for every 10 seconds with setInterval and componentDidMount hook in your react component.
componentDidMount: function() {
this.countdown = setInterval(function() {
axios.get(
'/notifications/'
).then(r =>
this.setState({ notifications: r.data }); // Changing state
)
}, 10000);
},
For real-time notification, you need something like Django channels or you can set a get api from react which runs after every defined time (say 5 minutes) and would fetch the required notifications based on user.
In your case things in context processor would be in listapiview and later you can fetch all the list.
I've been struggling with a simple use case: read a number, ie signed-up user counts, on my React web app from Firebase Database and display it when the page is loaded.
There is documentation for older versions of React and Firebase, but it would seem that this use case should be doable the new way.
Screenshot of my Firebase Database.
import React from 'react'
import {Badge} from 'react-bootstrap'
var firebase = require('firebase')
var config = {
apiKey: "MyKey", // redacted key
authDomain: "dnavid-c48b6.firebaseapp.com",
databaseURL: "https://dnavid-c48b6.firebaseio.com",
storageBucket: "dnavid-c48b6.appspot.com",
};
var firebaseApp = firebase.initializeApp(config);
var UCRef = firebaseApp.database().ref("numberofusers")
class UserCount extends React.Component{
constructor(props){
super(props)
this.state = {usercount: '3'}
}
componentDidMount() {
// this.setState({usercount:4}) // This actually works and displays "4"
var uc = UCRef.on('value',function(snapshot){return(snapshot.val())})
debugger;
this.setState({usercount:uc}) // This does not work
}
render(){
return (
<Badge>
{this.state.usercount}
</Badge>
)
}
}
export default UserCount;
In the Chrome debugger (note "debugger" command) I get in the console:
> uc
<. function(snapshot) {
return snapshot.val();
}
And
> uc()
Uncaught TypeError: Cannot read property 'val' of undefined(…)
From which I would deduce that since the snapshot is undefined, the event has not been triggered. But this doesn't sound logical as:
componentDidMount has been evaluated (because the component has rendered and the execution reaches it when the debugger stops for inspection)
Firebase documentation claims that the event is triggered, see below "This method is triggered once when the listener is attached".
Value events
You can use the value event to read a static snapshot of the contents
at a given path, as they existed at the time of the event. This method
is triggered once when the listener is attached and again every time
the data, including children, changes. The event callback is passed a
snapshot containing all data at that location, including child data.
If there is no data, the snapshot returned is null.
So, is the listener not being attached? What's going on?
You need to do it like this:
componentDidMount() {
UCRef.on('value', snapshot => {
this.setState({usercount: snapshot.val()});
});
}
UCRef.on('value', func) would not return you a value. You can get it inside a callback
I have a collection of users (model user)
model has a boolean value: isExport
i have a button that on click supposed to post to the server all the users that isExport=true
Can anyone suggest a suitable solution for this problem?
I know it's possible to wrap the collection as a model and overwrite the toJSON function
but couldn't manage it so far (can someone please give a code example?)
App.User = Backbone.Model.extend({ defaults: {isExport: false}...});
App.Users = Backbone.Collections.extend({model: App.User...});
Thanks!
Roy
Backbone collections by default don't have any write operations to the server, so you'll need to add a new method to your Collection subclass such as doExport, use .where to get the models with isExport=true and their toJSON() to get an array of objects which you can then send to the server with Backbone.sync or $.post.
Backbone comes with RESTful support.
So, if you give each collection a url pointing to the collection rest service, then with a few functions (create,save) you can handle server requests.
App.Models.User = Backbone.Model.extend();
App.Collections.Users = Backbone.Collection.extend({
url: 'users',
model: App.Models.User
});
So, this way, you can:
var users = new App.Collections.Users();
users.fetch(); // This will call yoursite.com/users
// Expecting a json request
And then you can:
users.create({ name: 'John', isExport: true });
This will send a post request to the server, in order to create a new record.
And you can check on server side if it has the flag you want.
App.Views.ExportAll = Backbone.View.extend({
el: '#exportAll',
events: {
'click': 'exportAll'
},
exportAll: function(e){
e.preventDefault();
console.log('exporting all');
console.log(this.collection.toJSON());
var exportModel = new App.Models.Export;
exportModel.set("data", this.collection.toJSON());
console.log(exportModel.toJSON());
exportModel.save();
}
});
I think this is the best solution for the problem
I would like to fetch model from specific url with parameter:
url: server/somecontroller/id/?type=gift
Simple working way is:
collection.fetch({ data: { type: 'gift'} });
But I want to set it in model:
...
if(id){
App.coupon = new AffiliatesApp.Coupon({id: id});
} else {
App.coupon = new AffiliatesApp.Coupon({id: 'somecontroller'}, {type: 'gift'});
}
App.coupon.fetch();
How can I achieve it?
The easiest way to achieve this is to override Backbone's url method on the Coupon model with one defined by you. For example you can do :
Affiliates.Coupon = Backbone.Model.extend({
urlRoot : "server/somecontroller/",
url : function(){
var url = this.urlRoot + this.id;
if(this.get("type")){
url = url + "/?type=" + this.get("type");
}
return url;
}
});
This solution is easy to implement but has a drawback: the generated URL will be used for every action that syncs to the server (fetch,save,..).
If you need to have a finer control over the generation of the URL depending on what action you are doing you will need to override Backbone's Sync method for your model.
It can be done by overriding the fetch method in model to use some custom data. Using CoffeeScript it could look like this:
class AffiliatesApp.Coupon extends Backbone.Model
fetch: ->
super(data: { type: #get('type') })
Note that this example will ignore any attributes passed to coupon.fetch(), however it can be easily adjusted for any override logic.
Is there an extension to backbone for Flash Messages? It seems to be a common feature in Web Frameworks (server side at least). There appears to be none, and I attempted to make my own:
class FlashMessenger extends Backbone.Model
constructor: ->
#messages = []
# add a message to the messages array
add: (type, message) ->
#messages.push
type: type
message: message
# returns all existing messages and clearing all messages
getMessages: ->
ret = #messages.slice(0)
#messages = []
return ret
Now, I was wondering how can I inject them into my views automatically. I will like my messages to show when I use Backbone.Router.navigate() eg:
app.flashMessages.add("success", "Successfully logged in")
appRouter.navigate("dashboard")
# flash messages should show when I render the view
My 5 cents -- it seems to be a bit of an overkill to use Backbone for flash messages. If you have only 1 instance of flash message on the page, you're better off not using a separate model for it.
Instead I would use a view for Flash message and a global dispatcher:
Dispatcher = _.extend({}, Backbone.Events);
Create view:
var FlashMessage = Backbone.View.extend({
initialize: function() {
Dispatcher.bind('show_flash_message', this.render);
},
render: function(msg) {
// do something with the message
}
});
And from the part of the app where you have to show the flash message, do
Dispatcher.trigger('show_flash_message', 'Some message');