How to share a global variable/object with RequireJS - javascript

I'd like to set a variable or object in main.js which I can then reference from any backbone view.
I considered using localStorage, but the data is dynamic and a little sensitive so I wouldn't like to have it stored in localStorage as it could be manipulated by the user very easily.

Since you said "main.js" I think you're confused between RequireJS and Backbone.js. RequireJS is not part of Backbone. It is an AMD module loader which happens to be used a lot with backbone projects.
Looks like you need a RequireJS module like:
define(function (require) {
var someData;
var singleton = function () {
return {
getMyData = function(){},
setMyData = function(data){},
};
};
return singleton();
});
P.S: Above code can be made object literal, an instance of proper constructor function, es6 class of whatever. I just posted something as an example.

#TJ already gave what's needed to achieve what I call a Service within my app, borrowed from AngularJS services. I have a couple services, like i18n which is just a i18next instance.
Like you, I wanted to manage certain data relative to the app that could be shared everywhere, without putting it in the window object.
I came up with a AppState service which is just a Backbone model instance.
define(['underscore', 'backbone', 'color'], function(_, Backbone, Color) {
var State = Backbone.Model.extend({
setBrandColor: function(hexColor, options) {
return this.set({ color: new Color(hexColor) }, options);
},
getBrandColor: function() {
return this.get('color');
},
});
return new State(); // return a new instance instead of the constructor.
});
What's cool with a Backbone model is that anything within the app can listen to its events. This is inspired from React.
this.listenTo(AppState, 'change:color', this.onBrandColorChange);
Note that I prefer to use PascalCase for services even though they're instances, they're closely related to a static type in other languages.
This way, when the app state changes, other parts of the app may or may not react accordingly. Without the events, the app would need to be more coupled which is undesirable.

Related

In Vanilla JavaScript, how do I structure my app when having multiple Models/Views/Controllers?

I'm trying to get a practical grasp of MVC model implementation (not the conceptual understanding) in JavaScript.
As for the start, I thought it would be worth making an effort and try building a MVC app in plain JS. I've read dozens of articles and book chapters referring to MVC and its variations. Of course I googled lots of examples to see how it's done for real. The most understandable and with the proper meaning is in my opinion this one:
https://github.com/tastejs/todomvc/tree/master/examples/vanillajs
In the end, I was able to refactor my own app in the todomvc-vanillajs way.
However, there is one thing that still bothers me. All these apps and examples are very basic, so there is only one Model, View and Controller specified for the whole app.
What if I wanted to add more (equally complex) features to such app?
Should I add them one by one to my controller.js view.js and model.js files or whether should I stop developing spaghetti code and add new files instead, thus creating new models, controllers and views for each of the new feature individually?
It seems to me, that every feature should have its own view, controller and model, or at least, could have, depending on the subjective evaluation. But I'm not quite sure how such implementation should look at this situation in terms of code structure, namespacing etc.?
What if I want to imitate a scale by creating multiple views, models and controllers on every single functionality like e.g. handling an "add task to the list" or "delete the task" actions.
For the purpose of my dilemma, I've created my own MVC draft, which has two models, controllers and views. Whether such an approach would make sense? What happens when further developing my application, I quickly get to the point where I have dozens and more specific (coresponding) models, views and controllers.
Heres is the aforementioned fiddle.
;(function () {
'use strict';
/**
* #file ./App.js
*/
var App = {
Model : {},
Controller : {},
View : {}
};
console.log('start');
window.App = App;
})();
/* -------------Views-folder----------------------*/
/* -------------separate-file-----------------------*/
(function () {
'use strict';
/**
* #file Views/buildAdd.js
*/
var buildAdd = {
// render
// event
// pass the reference to event handler in Controller
};
App.View.buildAdd = buildAdd;
})(App);
/* -------------separate-file-----------------------*/
(function () {
'use strict';
/**
* #file Views/buildDelete.js
*/
var buildDelete = {
// render
// event
// pass the reference to event handler in Controller
};
App.View.buildDelete = buildDelete;
})(App);
/* -------------Controllers-folder----------------------*/
/* -------------separate-file-----------------------*/
(function () {
'use strict';
var addController = {
// handle the event and decide what the Model has to do
// handle the response from Model and tells the View how to update
};
App.Controller.addController = addController;
})(App);
/* -------------separate-file-----------------------*/
(function () {
'use strict';
var deleteController = {
// handle the event and decide what the Model has to do
// handle the response from Model and tells the View how to update
};
App.Controller.deleteController = deleteController;
})(App);
/* -------------Models-folder----------------------*/
/* -------------separate-file-----------------------*/
(function () {
'use strict';
var addModel = {
// send request
// get response
};
App.Model.addModel = addModel;
})(App);
/* -------------separate-file-----------------------*/
(function () {
'use strict';
var deleteModel = {
// send request
// get response
};
App.Model.deleteModel = deleteModel;
})(App);
/* -------------separate-file-----------------------*/
Thus, I found this question very similar to mine, but the provided answers are not entirely satisfactory, at least to me.
Check my implementation of so called Single Page Application framework. The whole thing is of 60 lines of code. It uses jQuery but can be implemented in VanilaJS.
Basic idea is simple - your app is just a collection of pages a.k.a. views
<section id="route1" src="content1.htm" />
<section id="route2" src="content2.htm" />
...
Sections id's define set of possible "routes"
SpAPP catches browser's navigate event and load requested view on the route.
And partial content1..N.htm files contain view markup, setup and controller functions.
Data model here is JS data received from server and stored in memory or in local storage.
As of MVC frameworks in general... You cannot bring joy to everyone and free of charge. That small SpAPP thing that can easily be understood and adjusted to particular project's needs is a way to go I think.
Looking at my experience in Ruby on Rails framework, I don't always need all three elements of MVC pattern. Sometimes you need a model for a database but it's only accessed internally, not by the client. Or sometimes you only need a generic helper class.
As a convention, the files are split, each one has its own controller, model and view, following a naming convention, maybe something like:
articles-view.html
articles-controller.js
articles-model.js
Views are split for each action in the controller:
articles-index.html
articles-show.html
articles-update.html
...
articles-controller.js
articles-model.js
Inside the controller, you will have the "actions", the functions for everything semantically related to an Article in a blog.
function ArticlesController() {
function index() { ... }
function create() { ... }
function edit() { ... }
...
function delete() { ... }
}
In models, you basically have the class / prototype itself, something that is built with the given data.
function Article() {
this.name = "";
this.author = "";
this.text = "";
this.dateCreated = "";
}
And finally, your views should have element with the same name used in the model.
If you have a basic CRUD system, for example, you can have just one controller and one model, but different views (one for listing all items, one for creating and editing, one for just one item, etc).
Taking examples from Rails and NodeJS, a way to write less code for the views is by using "partials". Common HTML structures can be saved on a file and imported into other HTML files as needed, such a form, the headers, the footer of a page and so on.
Example:
Instead of having a form on articles-create.html and another on articles-edit.html, you will have something like:
_articles-form.html <- this is your partial!
articles-create.html
articles-edit.html
"_articles-form.html" will be imported / appended into the create and edit pages.
Other common features can be consider as "Helpers". They are not a letter in "MVC", but often used. Like the Datepicker library, a simple validation function, a parser, etc. Something that can be used by everyone, not a specific feature of a class.
The project structure could be something like:
app/
app/controllers/
app/controllers/articles-controller.js
app/models/
app/models/articles-model.js
app/views/
app/views/articles/
app/views/articles/index.html
app/views/articles/create.html
app/views/articles/edit.html
app/views/articles/delete.html
app/views/articles/_form.html
Also, having a Manager functionality as you described above, will help you load all the data needed. Some function that maybe will read a json file, looking for the feature's name and parsing through the file's names, loading everything.
The manager would check if there is a model file, a controller file and a folder with N view files in it, containg the word "articles". The same would happen to "authors", "comments", "users" and so on.
I understand that you are proposing this question for study reasons and you took JS as a personal preference, so I´m not saying "don't try it" or something like that. But something to consider: the MVC pattern tackles applications that involves both client and server side. Unless your are developing on a full stack with NodeJS and MongoDB (or other similar technologies), HTML and Javascript are more on the View side of the application (or as helpers).
And if you are developing something like a library, you'll end up putting everything on a single file and minifying/uglifying it. Take JQuery as an example. Javascript developers often go with the Module pattern. They create an object, expose methods and variables that the other developer needs to know and that's it.
So, probably (but not for sure, you never know!), you won't see or work on many vanilla Javascript applications implementing MVC pattern.

JavaScript variable scope - proper use

I read this style guide for angular from johnpapa. There is a snippet:
/*
* recommend
* Using function declarations
* and bindable members up top.
*/
function Avengers(dataservice, logger) {
var vm = this;
vm.avengers = [];
vm.getAvengers = getAvengers;
vm.title = 'Avengers';
activate();
function activate() {
return getAvengers().then(function() {
logger.info('Activated Avengers View');
});
}
function getAvengers() {
return dataservice.getAvengers().then(function(data) {
vm.avengers = data;
return vm.avengers;
});
}
}
So my question is in functions activate() and getAvengers(), they both reference variable (dataservice) and function (getAvengers()) outside of their scope. Is this proper use? Should I bind these 2 in the variable vm instead, e.g:
vm.getAvengers = getAvengers;
vm.dataservice = dataservice;
...
function activate() {
return vm.getAvengers().then(....);
}
function getAvengers() {
return vm.dataservice.getAvengers().then(.....);
}
Specifically for your case
Would say if you are meaning to use this within angular app would recommend not exposing the service, exposing it through this object does not add value and might down the road, when a less experienced developer modifies your code, might result in wonky access to shared dependencies.
If you want access to the dataservice objects functionality across multiple entities then register it as an angular service, and inject it to the different entities that need it.
In General
Both of the ways you are describing are perfectly correct use, but as is usually the case the answer which to use is "it depends."
Why you would use one for another would be if you wanted to expose the variable externally (i.e. if you wanted to let others access that object through the returned object, expecting others to dynamically change the service on your object)
So in this example you should ask yourself a few question
Do I want to expose this object through another object or do I want to let angular DI pass this along to the other controllers that need this functionality
Do I want to allow external entities to modify this object
Does exposing this service through my object make the use of the perceived use of this object more confusing?
But again for this particular case you should not expose it through your object ( through your variable vm, which is bound to the return object this, in this case )
The vm is a acronym for a view model (a object representation of your view) it is meant to be used within your view to bind elements, ui events to it. The dataservice and the logger seems to nothing to do with the view at all, they are just services used within a controller. If you assign them to the vm then you probably create a tightly coupling between your view and services thus it seems like a not a very good idea to me. You can think about the VM as a interface (glue) between your view and controller.
Here is a picture of the interactions between view model, controller, view and services.

How to clear view model in Durandal

Is there a way to selectively tell Durandal to reinitialize a view model. I am aware of the singleton vs new instance approaches to initialize view models.
//singleton since a declared object is returned
define(function() {
return { prop1: 1, prop2: 2 }
});
//new instance since a constructor is returned
define(function() {
var ctor = function(){};
return ctor;
});
I generally don't like to declare view models as singletons, but I have to do it in a special case due to sub routing which requires me to pass data from a parent router to my child router. However, the singleton has other side effects, so I was wondering: Is there a way to selectively request a new instance of the view model even if it was initially declared as a singleton?
Not that I know of - this is more of a limitation of requirejs versus Durandal, though. Once require has loaded the module, so far as I know it will always return you the same version of that module. Unless there's a way to tell require to reload the module?
The only thing I could think of would be to "reset" the view model during the activate method. If you're changing routes and finding that the activate method isn't being called, it may be because Durandal thinks that your module is already active (in which case it won't reactivate). You can change this behavior by customising the areSameItem function for the router (see this question for an explanation).
Hope that helps.
I don't know if it helps in your case, but you could use the activate() method in your view model. For more information see http://durandaljs.com/documentation/Hooking-Lifecycle-Callbacks.

Dependency Injection in a distributable Javascript library?

We are using Backbone to create reusable components. We create controllers in order to setup bindings between models and views. We wish to offer people the ability to replace the models and views with their own implementations.
Since the majority of people will use the the components we provide, I don't want to force developers to configure or create anything that isn't different from the defaults.
This means that someone should be able to pass an instance of an object they want to use as a model or view to the controller, all configured and setup and ready to go, or they can pass in some configuration to what the controller will use by default.
I am not sure what the best approach is.
// Idea #1
var controller = new Controller({
dependencyA: {
conf: { // config for depedencyA }
},
dependencyB: {
conf: { // config for dependencyB }
class: custom.implement.Class
}
});
In this approach, the user doesn't have control over how to instantiate the object. What's bad about this is, for example, Backbone models take two arguments in the constructor while views only take one.
// Idea #2
var controller = new Controller({
dependencyA: {
args: ['arg1',{
opt1: 'opt-value',
}]
},
dependencyB: {
args: ['a','b','c']
class: custom.implement.Class
}
});
Args would be the arguments passed to a constructor. This means the controller calls the constructor with the args array, and again this only really benefits you if you're passing in custom configuration for default dependencies. If you want to pass your own implementation it's more awkward.
// Idea #3
var controller = new Controller({
dependencyA: new outOfBoxModel({ // configuration }),
dependencyB: new custom.imeplement.Class('a','b','c')
});
In this approach, the user is forced to instantiate the out of box model. If the model's default settings are all appropriate though, then the user is doing unnecessary work. The only bit they HAVE to do is create an instance of their own custom implementation.
I am not sure what the best approach would be here?
Of the three approaches, I most prefer approach number 3. Here is why:
It is more consistent than the other approaches. In the 3rd approach, the user only has to learn to pass in constructed instances of dependencies into the controller. In the other approaches, the user has to pass in either args, or args and a class name.
It does not violate the Single Responsibility Principle. In the first two approaches, your controller is made responsible for constructing and configuring its dependencies. This doesn't feel like dependency injection at all! I think it's better, and simpler, to leave the construction of dependencies to the user or another part of your application. In my opinion, its not a terrible thing to force the user to construct their own implementations - it gives them the freedom to define their constructors however they want, rather than forcing you to define and maintain constructor APIs for the Controllers dependencies, and forcing the user to conform to them.
A different idea:
If you have this freedom in your application, I would consider putting your Controller construction logic in a factory class or method:
var createController = function(config) {
// Parse, validate, extract relevant config items
// var a = create dependency a
// var b = create dependency b
return new Controller(a, b);
}
This approach allows you to be as fancy as you want with your definition of config - you could support all three of the config definitions you provided in your original post - although I wouldn't recommend that :-). At a minimum, I would have the factory method support a zero args invocation (in which case it would return the default construction of Controller) and one of your preferred config definitions.

Storing model or collection-specific views inside model or collection constructors

I realize there is many solutions to this but I was wondering what the community's opinion is.
I have a series of models and collections. Each model has a number of views like details, edit, print, aside, help, etc. Collections have views that often have the same names (ie: aside, help, etc).
One requirement I have is that I need to structure my code in modules. The application should have no trace of a module's functionality if the module is NOT loaded. This may happen if, say, a user has no permissions to view, edit, etc other users. So the "Users" module would not even be loaded.
So...
I thought a good place to store view definitions for a model could be the model's constructor and for a collection in the collection's constructor. For example:
var User = (function(){ // module definition
// model definition
var Model = Backbone.Model.extend({
initialize: function() {
// ...
}
},{
Views: {
Details: Backbone.View.extend({
// ...
}),
Aside: Backbone.View.extend({
// ...
}),
Help: Backbone.View.extend({
// ...
})
}
});
// collection definition
var Collection = Backbone.Collection.extend({
model: Model,
initialize: function() {
// ...
}
},{
Views: {
Aside: Backbone.View.extend({
// ...
}),
Help: Backbone.View.extend({
// ...
})
}
});
// add more code here
return { // make model and collection public
Model: Model,
Collection: Collection
};
})(); // end module definition
I realize I could have my views live elsewhere but would this approach have any considerable drawbacks that I may not be aware of? Perhaps memory leaks or something less obvious?
Thank you!
I think you would be better off not adding your views as "class methods" onto your models and collections. Because of the nature of JavaScript's prototypical inheritance, you aren't really adding class methods so much as properties to the constructor functions for your model types. As to whether or not this is going to cause you issues like memory leaks, I can't say.
I would instead say that, unless you have an unlisted compelling reason for using this structure, you are better off just grouping your views on simple objects.
If the goal is to modularize your code I would take advantage of something like require.js or Marionette modules or just grouping "related" code in an IIFE.
If you are interested in knowing more about what exactly happens to the classProperties that are passed into the Backbone.Model.extend method then I would recommend looking directly at the annotated source.
Have a look at require.js. With it you should be able to add logic that deals with module loading. In general you should still have a look at it, works great for organising backbone applications, especially with the text plugin.

Categories

Resources