Supposed that I created a js object like this
Cart = function Cart(){
this.contents = new Array();
}
Cart.prototype = {
add : function(obj){
this.contents.push(obj);
}
}
and put it in the project lib folder so that this object can be used project-wide.
Is it possible to use this object persistently in the template backend (js file)? For example, I declared :
Template.pageA.rendered = function(){
var smallCart = new Cart();
}
Can I use it in the template event? For example:
Template.pageA.events = {
'click button#add' : function(event){
smallCart.add('newItem'); //Is this object as same as the one in rendered?
}
}
I have been doing stuff by using Sessions but when there are a lot of operations to be done, the events will be cluttered with business logics and calculations. I want to avoid this, thus I am thinking of putting the logics into Javascript object functions.
Will this approach work? Will the object stay persistent?
You can place that code inside a Meteor.startup, so the object will persist always.
Meteor.startup(function(){
Cart = function Cart(){
this.contents = new Array();
}
Cart.prototype = {
add : function(obj){
this.contents.push(obj);
}
}
})
Is this object as same as the one in rendered? yes
From my point of view, putting the class definition of your cart into the lib folder can be good. If it's client-side code only, you can store it into a cart.js file into your client folder.
This definition will be available on client.
Then you need to store an instance of your cart. Instanciate a cart into your template can be a good idea (you don't need it before that), but your need to store a reference into you client side application scope (usually into app.js at the root of your client folder for example)
client/cart.js
Cart = function() {}....
client/app.js
myCart = null;
Into your template, you can instantiate the cart
myCart = new Cart();
If you're using your only on one template, you can store the instance directly to the template into the controller logic. Attach it to the rendered of the template is a bit wired, because you usually handle template rendering logic.
This way, a cart is going to be created for every user which come on your site and called the template. If you have a multi page app using iron router, myCart will be available whatever the page as soon as you have already create it into your template.
Be aware, in this way, if the client "refresh" the page reloading the JavaScript code, you'll loose the current cart. You need to persist the cart on local storage in order to support the refresh. Not worth in all the case, but for a shopping cart, can be usefull
Putting the Cart code in lib is correct, it will be available everywhere.
As far as smallCart is concerned you have declared it inside a function so it isn't available to the other template function.
Declare it outside the two functions and it will be available to both.
var smallCart;
Template.pageA.rendered = function(){
smallCart = new Cart();
}
Template.pageA.events = {
'click button#add' : function(event){
smallCart.add('newItem'); //Is this object as same as the one in rendered?
}
}
Related
I'm trying to write functions for storing and retrieving window state but cannot figure out how to do that. The idea is that user could make at any time a "snapshot" of the screen and with next login to the app he could retrieve it back, also he can store as many snapshots as he want.
For example: on the page I have 4 different closed panels with some kind of filters and 6 different tabs with grid inside (by default the first tab is opened). Now let's say, I have opened 2 of 4 panels, set some filters and worked with 5th tab. I want to be able to store whole window state (For example "My state 1"), and when I logged in at next time, just choose "My state 1" and get back my window state.
I already store and retrieve all grid properties in DB with next functions:
Store:
$scope.state = {}
$scope.saveStateString = function(store) {
$scope.state = JSON.stringify($scope.gridApi.saveState.save(store));
// console.log("function save state string")
};
Retrieve
if(objNode.folderId){
eventService.singleFilter(nodeId)
.then(function (res) {
if (res.body){
$scope.restoreStateString(JSON.parse(res.body));
}
});
}
else if (typeof objNode.folderId === "undefined"){
return false
}
$scope.restoreStateString = function(restore) {
$scope.gridApi.saveState.restore( $scope, restore );
};
For now I'm trying to store window state in localstorage and do next:
var storeValue = null;
var keyName = null;
var _window = {};
$scope.storeWorkspace = function (){
for (prop in window)
_window[prop] = window[prop];
storeValue = JSON.stringify(_window)
keyName = prompt("put your key name");
localStorage.setItem(keyName, storeValue);
};
but I get this error
angular.js:13708 TypeError: Converting circular structure to JSON
at Object.stringify (native)
I clearly understand, why I'm getting this error, it cause JSON doesn't accept circular objects - objects which reference themselves also I see from
console.log(_window) how the "window" has many objects inside, so I decided to ask:
How to store and retrieve window state?
Don't mix application data and resources to store, its huge, hard to reuse and will lead to running into other issues.
Keep it simple!
Construct appState object with what you required to reload the views
var appState ={config:{}, data:{}};
Store it in internalStorage / sessionStorage based on how long you to retain forever vs per session
localStorage.setItem("appState", appState);
On initial app start logic, load data from internalStorage / sessionStorage or server and you may modify existing controller code for binding it to the view.
getApplicationData(){
var appState = localStorage.getItem("appState");//get it from browser storage
if(!appState)
//get it from server
return appState;
}
This is more robust and performant approach.
The vast majority of values stored on the window object are simply not serializable. You will not be able to use JSON for this. You should instead track all changes you make to window and store those in a separate JSON object as POJOs. Alternatively, you can copy the initial window object when your application starts and then only store the differences between the current window object and the original copy.
In any case, this is probably going to be a hunt of trial and error, depending on what libraries you are using, and how they are using global variables. You will probably find you need to manually filter out some stuff when you serialize. Best practices would suggest nothing should write to the window object. If you have things writing to the window object, you're probably dealing with badly behaving code.
Do not try to store the whole window. Store your application's state in a separate object, e.g. state, which you can then attach to the global object if you absolutely have to:
window.state = {}; // your application's state goes here
var serializedState = JSON.stringify(window.state); // To be put into localStorage
Make sure that all the information you need to rebuild your app during the next launch is contained in this object, and nothing more than that. Avoid global state where possible.
Also make sure that your state object only contains serializable data. E.g. functions or symbols cannot be serialzied to a JSON string and will get lost in the process.
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.
Problem: Meteor JS app with 2 distinct templates that need to share some data.
They are dependent on one another, since I aim to extract text (Step 1) from one, and then create dynamic buttons (Step 2) in another template. The content of the buttons is dependent on the table.
buttons.html
<template name="buttons">
{{#each dynamicButtons }}
<button id="{{ name }}">{{ name }}</button>
{{/each}}
</template>
My goal is for the name property to come from the content of reactiveTable.html (see above, or their Github page, package meteor add aslagle:reactive-table.
These need to be dynamically linked since table re-renders constantly w/ different group of products, which are linked up through Template.reactiveTable and a specific data context (Pub/Sub pattern).
IF the table is (re)rendered, then parse it's content and extract text. Once the table is parsed, dynamically inject newly created buttons into the UI. Note UI.insert takes two arguments, the Object to insert, and then location (DOM node to render it in).
Template.reactiveTable.rendered = function () {
UI.insert( UI.render( Template.buttons ) , $('.reactive-table-filter').get(0) )
};
(Insert new buttons every time a reactiveTable is rendered.)
This code works, but is flawed since I cannot grab the newly rendered content from reactiveTable. As shown in this related question, using ReactiveDict package:
Template.buttons.helpers({
dynamicButtons: function() {
var words = UI._templateInstance().state.get('words');
return _.map(words, function(word) {
return {name: word};
});
}
});
Template.buttons.rendered = function() {
// won't work w/ $('.reactiveTable) since table not rendered yet, BUT
// using $('h1') grabs content and successfully rendered dynamicButtons!
var words = $('h1').map(function() {
return $(this).text();
});
this.state.set('words', _.uniq(words));
};
Template.buttons.created = function() {
this.state = new ReactiveDict;
};
How can I change my selector to extract content from Template.reactiveTable every time is re-renders to create buttons dynamically? Thanks.
You’re using a lot of undocumented functions in there, and UI.insert and UI.render which are bad practice. The just-released Meteor 0.9.1 eliminates them, in fact.
Create your dynamic buttons the Meteoric way: by making them dependent on a reactive resource. For example, a Session variable. (You could also use a client-side-only collection if you want.)
Template.reactiveTable.rendered = function () {
// Get words from wherever that data comes from
Session.set('buttons', words);
};
Template.buttons.helpers({
dynamicButtons: function() {
if (Session.equals('buttons', null))
return [];
else
return _.map(Session.get('buttons'), function(word) {
return {name: word};
});
}
});
Every time reactiveTable is rendered or rerendered, the buttons Session variable will update. And because your dynamic buttons are depending on it, and since Session variables are a reactive resource, the buttons will rerender automatically to reflect the changes.
I have an app with the following basic structure,
weatherDisplayController.js
weatherGrabbingService.js
userColorPreferencesService.js
When the user changes their color preferences for viewing the weather, it is stored in userColorPreferencesService.js.
However, I want to add another view where you can view all your friends' dashboards, which means creating a new micro-instance of the module. However, when I do, they will overwrite the color preferences in the Service.
How can I have multiple instances of the same module on one page?
Thats because the services are singletons. there's is only 1 existing instance of it.
Maybe make the constructor within your colorservice as a oop class and store multiple of them in the service itself.
function ColorPreferences(){
//any data
}
app.service("userColorPreferencesService", function(){
this.ownColors = new ColorPreferences({ /* data goes here */});
this.buddyColors = [];
});
//in your controller, when sharing actived
userColorPreferencesService.buddyColor = new ColorPreferences({ /* data from ajax? */});
The only varying data should be on the scope for that part of the page. Instead of storing the data in the service you could store it in each scope and pass the required data to the service.
Where do I store user specific (session) information in an ExtJS MVC application?
Is it right to define a custom base controller that can contain an object with user specific info and use it in application?
Example:
Ext.define("MyApp.controller.BaseController", {
extend: "Ext.app.Controller",
session: Ext.create("MyApp.lib.UserSession"),
init: function() {
var me = this;
me.session.init();
/** some code **/
},
doSomething: function() {
var me = this;
var counter = me.session.get("counter");
}
});
If you need to persist the data after page refresh you can use Ext.state.Manager.
Setup state manager with Cookie or LocalStorage provider during application launch:
Ext.state.Manager.setProvider(new Ext.state.CookieProvider());
// Shortcut for quick reference across the project, if MyApp.user is null - user is not authorized.
MyApp.user = Ext.state.Manager.get('user');
Save the data you need to persist for current user after authorization or other actions:
Ext.state.Manager.set('user', {
first_name: 'John'
last_name: 'Doe'
});
I do it two ways depending on the application target.
My preferred method is using a LocalStorage proxy for my models I want to save locally, that way there is no change in how I interact with them in the application and it's a bit more handy when relaunching your application to set things up with out DB calls.
Alternatively, I create a global variable when starting the application. Inside the launch function do something like this.
var app = Ext.application({
name: 'MyApp',
launch: function() {
MyApp.model.User.load('profile', {
scope: this,
success: function(user) {
// Setup the app space under your Application namespace
// so you don't conflict with anything the the ExtJS framework has set
MyApp.app.user = user;
}
}
}
}
That way throughout your application, you'll have access to the current User's model through the variable MyApp.user So this can then be used in all areas of your application
var currentUserName = MyApp.app.user.get('name');
The downside of this is that you are introducing a global variable which is considered bad practise when it can be avoided.
I don't see there being anything wrong with constructing a base controller like you have suggested, but if you are doing it purely for access to the session variables you want to store I would suggest it's maybe a bit overkill.
I've done a few different apps that involved users with specific permission settings pulled from their session when they first start the app.
I struggled with doing this a few different ways, but I came to recognize that the user permission data all fit best within the MODEL context of MVC. With that in mind I decided to just put it all in a store.
This worked out nicely. If you create the store following the MVC guidelines, you can always call upon the user session data from anywhere in the app with Ext.getStore('SessionStoreId')
I almost always create a State Manager class for these kind of things. You can also create singleton classes with your own custom code and include it in your app like so:
Ext.Loader.setPath('MyApp.Util', 'app/util');
And do a require like so:
Ext.require('MyApp.Util.Class')
The class itself should be something like this:
Ext.define("MyApp.Util.Class", {
singleton : true,
options : {},
myFunction: function(){}
});
That way you can put all your non-ExtJS like functionality in seperate classes.