I'm trying to share a Marionette App with some of its Views. I've read the wiki here, but the example leaves me with a question.
I've got a file with a couple of views in it that will all need to use the request/response system and possibly the commands. I don't want to do var MyApp = require('app'); in all of the Views in the file. I came up with the following, but I think there's probably a better way to do it.
Example:
//Views.js
define( ["marionette"], function (Marionette) {
var App = function(){
return require('app');
};
var ExampleItemView = Marionette.ItemView.extend({
initialize: function(){
App().request("getInfo", "aboutStuff");
}
});
return Marionette.CollectionView.extend({
itemView: ExampleItemView,
initialize: function(){
App().request("getInfo", "aboutStuff");
}
});
Is there a better way to do this?
I'd definitely not inject your app into your views since it can create circular dependencies which are ALWAYS a code smell (regardless of whether they can be solved or not) The simples solution by far is to create a separate (singleton) reqres object which is handled by the app and injected into the views.
//reqres.js
define(['backbone.wreqr'], function( Wreqr ){
return new Wreqr.RequestResponse();
});
//app
define(['reqres'], function(reqres){
reqres.setHandlers({
'getInfo' : function(){
return 'foo';
}
});
});
//Views.js
define( ["marionette", "reqres"], function (Marionette, reqres) {
var ExampleItemView = Marionette.ItemView.extend({
initialize: function(){
reqres.request("getInfo", "aboutStuff");
}
});
return Marionette.CollectionView.extend({
itemView: ExampleItemView,
initialize: function(){
reqres.request("getInfo", "aboutStuff");
}
});
Backbone Wreqr is to be replaced by Backbone Radio in the next major release of Marionette, v3.
To use Backbone Radio you could create a channel and do the following:
/**
* App.js (for example)
*/
var Radio = require('backbone.radio');
// Use the 'data' channel - this could be whatever channel name you want
var dataChannel = Radio.channel('data');
// Handler for a request
dataChannel.reply('getMessage', function(name) {
return 'Hello ' + name + '. Alba gu brath!';
});
/**
* View.js (for example)
*/
var Radio = require('backbone.radio');
var dataChannel = Radio.channel('data');
// Make the request
console.log( dataChannel.request('getMessage', 'Craig') ); // -> Hello Craig. Alba gu brath!
Related
I know that I can access the javascript var userName everywhere in the extjs backend, cause its defined globally in the themes/Backend/ExtJs/backend/index/header.tpl, to get information about the logged in user. But is there a way to get userId or role of the user in some way (without ajax requests)?
//{block name="backend/order/view/list/list"}
//{$smarty.block.parent}
Ext.define('Shopware.apps.Test.view.List', {
/**
* Defines an override applied to a class.
* #string
*/
override: 'Shopware.apps.Order.view.list.List',
getToolbar: function() {
// get this
var me = this;
// get parent action column
var result = me.callParent( arguments );
console.log(userName);
},
});
//{/block}
{block name="backend/base/header/javascript" append}
<script type="text/javascript">
var currentTabState = 'active';
window.addEventListener('blur', function () {
currentTabState = 'inactive';
});
window.addEventListener('focus', function () {
currentTabState = 'active';
});
var userName = '{$user->name}',
maxParameterLength = '{$maxParameterLength}';
You need to make the user available as Smarty variable, so you can simply access the data from this object.
For this you can subscribe to the PostDispatch event of the order backend controller Enlight_Controller_Action_PostDispatchSecure_Backend_Order in a subscriber and then you use the same code as in the Shopware_Controllers_Backend_Index to add the $user variable.
//{block name="backend/order/view/list/list"}
//{$smarty.block.parent}
Ext.define('Shopware.apps.Test.view.List', {
/**
* Defines an override applied to a class.
* #string
*/
override: 'Shopware.apps.Order.view.list.List',
getToolbar: function() {
// get this
var me = this;
// get parent action column
var result = me.callParent( arguments );
var userId = '{$user->id}';
var roleId = '{$user->role->getId()}';
console.log(userName);
},
});
//{/block}
{$user->role} contains the Shopware\Models\User\Role model, so you can access all data from this object with the corresponding getters.
If you want to know, which data you have available, just make a debug output in the \Shopware_Controllers_Backend_Index::indexAction method, right after the call $identity = $auth->getIdentity();
echo '<pre>';
\Doctrine\Common\Util\Debug::dump($identity);
echo '</pre>';
exit();
I have created a collection which should be accessible to client side and server side. But when I try to use it in browser it gives me undefined.
var lists = new Meteor.Collection("Lists");
//lists.insert({Category:"DVDs", items: {Name:"Mission Impossible",Owner:"me",LentTo:"Alice"}});
if (Meteor.isClient) {
// counter starts at 0
Session.setDefault('counter', 0);
Template.hello.helpers({
counter: function () {
return Session.get('counter');
}
});
Template.hello.events({
'click button': function () {
// increment the counter when button is clicked
Session.set('counter', Session.get('counter') + 1);
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
}
Now when I use lists in client side browser console it gives me undefined.
Define the collection without var keyword. It will make a global variable accessible in the whole application. And define collections uppercase:
Lists = new Meteor.Collection("lists");
It's a good practice.
If you has removed autopublish package, you should subscribe on collection at the client-side
Meteor.subscribe("lists");
publish it at server-side
Meteor.publish("lists", function () {
return Lists.find({});
});
and use lowercase for collection name.
I am working on a node.js application with postgresql, using the express framework. I am trying to follow MVC as much as possible.
I want to generate query results in a model class and then pass them to a controller class. That controller class is actually defined in the routes, so that controller class can take the results and pass them as http response.
This is my database helper class, i.e. the model class. My problem is at the listener at the very end of the class.
exports.DatabaseHelper = function()
{
var allVenues;
var client;
var customEventEmitter;
this.init = function()
{
this.customEventEmitter = new events.EventEmitter();
client = new pg.Client(
{
host:'localhost',
port:5432,
database:'postgres',
user:'postgres',
password:'password'
});
}
this.getVenuesWithEvents = function(searchParams)
{
allVenues = new Array();
var query_for_venues;
this.init();
client.connect();
client.addListener("error",function()
{
sys.puts("postgresql interface error");
});
query_for_venues = client.query("select id, name, location, latitude, longitude, category_generalized from venues");
query_for_venues.addListener("row",function(row)
{
//some code
});
query_for_venues.addListener("end",function(result)
{
this.customEventEmitter.emit("data",allVenues);
//////////////////////////////////////////////////////////
//this line shows error....'this' refers to the query object so customEventEmitter is undefined
//customEventEmitter is my idea of sharing the query results to my controller class.
//but I cannot do this becasue of this error
console.log("after emission");
});
}
}
How can I access the customEventEmitter instance variable from within the listener?
Just remove this from your init function:
this.customEventEmitter = new events.EventEmitter();
So you'll have:
customEventEmitter = new events.EventEmitter();
And in your listener just emit the emitter without this as follows:
query_for_venues.addListener("end",function(result){
customEventEmitter.emit("data",allVenues);
console.log("after emission");
});
let me show you a nice trick.
you could change custom
var customEventEmitter;
to
this.customEventEmitter =null;
at the top of the function. then you can call
var self = this;
outside of the query function. then inside the query function you reference the outer "this" with self.
as in:
self.customEventEmitter.emit()
the methodology I just described is standard.
I'm trying to load and render additional views async and append them to the ItemView.
Simplified code - why is $el not defined in the require() block in render() - what am I missing here? Am I not using RequireJS properly, or Marionette, or just my inexperience with javascript?
What is the recommended way of doing this? It needs to be dynamic as additional section views could be available at runtime that I don't know about yet as registered by plugins.
define(['require','marionette', 'App', 'swig', 'backbone.wreqr','text!./settings.html'],
function (require,Marionette, App,Swig, Wreqr, settingsHtml )
{
var sectionViews = ['./settingscontent/GeneralView'];
var SettingsView = Marionette.ItemView.extend(
{
template: Swig.compile(settingsHtml),
commands: new Wreqr.Commands(),
initialize: function ()
{
this.commands.addHandler('save', function (options, callback)
{
callback();
});
Marionette.ItemView.prototype.initialize.apply(this, arguments);
},
render: function()
{
Marionette.ItemView.prototype.render.call(this);
var $el = this.$el;
var self = this;
require(sectionViews, function (View)
{
$el.find('div.tab-content').append(new View(self.model).render().$el);
// $el is not defined
// self != outer this - $el is an empty div
});
return this;
}
}
return SettingsView;
})
Why are you trying to overload itemview.render?
Why not use the built in onrender event
https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.itemview.md#render--onrender-event
from that documentation :
Backbone.Marionette.ItemView.extend({
onRender: function(){
// manipulate the `el` here. it's already
// been rendered, and is full of the view's
// HTML, ready to go.
}
});
seems easier and more typical of marionette usage
You need to bind this inside the function to the SettingsView object. Something like:
render: function()
{
Marionette.ItemView.prototype.render.call(this);
var $el = this.$el;
var self = this;
require(sectionViews, _.bind(function (View)
{
...
}, this));
return this;
}
The local variables will not be visible inside the bound function. You can use this and this.$el safely however.
I would like to use chrome.storage API to save the settings of my users instead of localStorage in my Chrome extension.
Currently my options.js (with localStorage and JSON) file looks like this:
$(function(){ //jQuery Ready
// INIT
$("#notifysav").hide();
// STORAGE
if(localStorage['options']){
var o = JSON.parse(localStorage['options']);
$("#option1").attr('checked', o.option1);
$("#option2").attr('checked', o.option2);
.... [list of options]
}
// Save Button Click event
$("#save").live('click', function(){
localStorage['options'] = JSON.stringify({
"option1":$("#option1").attr('checked'),
"option2":$("#option2").attr('checked'),
.... [list of options]
});
// notification
$("#notifysav").fadeIn("slow").delay(2000).fadeOut("slow");
// reload to apply changes
chrome.extension.sendRequest({action: 'reload'});
});
});// #jQuery ready
My question is how to convert my current code to use the chrome.storage API. From what I understand, I would apply those changes:
$(function(){
// INIT
var storage = chrome.storage.sync;
$("#notifysav").hide();
// Load Options
loadOptions();
// Save Button Click Event
$("#save").live('click',function(){ saveOptions(); });
function loadOptions() {
storage.get( /* Something */ )
}
function saveOptions() {
var option1 = $("#option1").attr('checked');
var option2 = $("#option2").attr('checked');
storage.set({"option1":option1,"option2":option2}, function() {
// Notification
$("#notifysav").fadeIn("slow").delay(2000).fadeOut("slow");
// Reload Event to apply changes
chrome.extension.sendRequest({action: 'reload'});
});
}
});
Thanks for your help!
If I understand correctly, your main problem is how to retrieve data from the storage. Here is what can be done:
chrome.storage.local.get(null, function(all){console.log(all)});
will return you an object with all keys and values stored in the storage, in your case it will output:
Object {option1: "value1", option2: "value2"}
Also you can get just one of the keys:
chrome.storage.local.get("optionkey", function(all){console.log(all)});
or an array of keys:
chrome.storage.local.get(["opt1", "opt2"], function(all){console.log(all)});
In any case you can access data in the callback just by key names.
Thanks for your reply. I finally managed to get something working by changing my original code as follows:
$(function(){
// INIT
const storage = chrome.storage.sync;
var options = new Array();
$("#notifysav").hide();
loadOptions();
// STORAGE
// Save Button Click event
$("#save").live('click', function(){ saveOptions(); });
function loadOptions() {
storage.get('options', function(o) {
$("#option1").attr('checked', o.options[0]);
$("#option2").attr('checked', o.options[1]);
...
});
}
function saveOptions() {
options[0] = $("#option1").prop('checked');
options[1] = $("#option2").prop('checked');
...
//console.log(options);
storage.set({'options':options},function(){
$("#notifysav").fadeIn("slow").delay(2000).fadeOut("slow");
chrome.extension.sendRequest({action: 'reload'});
});
}
});