I'm using toJSON() method of my model in Sails in order to control the visibility of some of it's properties, when model is exposed via application's API.
In order to decide which properties to display and which to omit I need to know the permissions of the current user. So, how do I get the current user from inside the model? Or is there a better way (pattern) to solve this problem?
Here's some sample code I want to achieve:
toJSON: function () {
var result = {};
result.firstName = this.firstName;
result.lastName = this.lastName;
// Exposing emails only to admin users.
if (currentUser.isAdmin()) {
result.email = this.email;
}
return result;
}
Your asking about reading a session inside the model call. Currently the way sails and waterline are built you can not do this.
You can use the select property on your initial model call to restrict the columns returned. Since this would be in the context of your controller you would have access to the req object.
Here are a bunch of related questions / answers on this topic.
sails.js Use session param in model
Is it possible to access a session variable directly in a Model in SailsJS
https://github.com/balderdashy/waterline/issues/556
https://github.com/balderdashy/waterline/pull/787
Sails Google Group Discussion on the topic
Related
I have an API in ExpressJS and a middleware that gets executed before each endpoint controller:
app.use(segregationMiddleware);
app.get('/some-endpoint', controller1);
app.get('/some-endpoint-2', controller2);
The segregationMiddleware is used to look for some parameters in the request and then it calculates a value that then is stored in the request object as req.locals.domain so the controllers can access it.
In each Mongoose model, I define a field named domain using a Mongoose plugin (so I don't have to do it every time). That field is used to segregate my assets. This means that when the segregationMiddleware populates req.locals.domain = 'foo' for example, if I make a model.find({}) I want to get only assets that have { domain: 'foo' }. Same thing if I try to update, save, delete, and so.
Of course, I can just simply modify the query on each controller since I have accesso to req, but I need to do it every time and I need to remember it for finds, findAndUpdate, save, and soo... sooner or later I'm gonna forget it.
I can define some hooks in Mongoose that will modify the query using a plugin so it adds the domain constraint to the query so I don't have to do it in the controller BUT I don't have the current req object in the Mongoose plugin unless I pass it, and the only way that come to my mind is to abstract the DB methods in the plugin, so in the controller, I do something like this:
model.safeFind(req, query);
And in the plugin I define safeFind like:
safeFind = () => {
const theRealQuery = Object.assign({}, query, { domain: req.locals.domain });
return this.find(query);
}
BUT, in this way, I need to redefine each DB query function (find, findOne, update, save...) and I need to remember to use the safe methods. Then again, I'm going to forget it sooner or later.
Is there a way I can still use the regular Mongoose methods from the controller and have the plugin somehow modify the query for every method using the current req object?
My angular app have 2 controllers. My problem is that the controllers does not keep the data when the user navigates away from the page.
How can I store the selected data on of my controllers into a data store so it can be used between other controllers?
Option 1 - custom service
You can utilize a dedicated angular service to store and share data between controllers (services are single instance objects)
service definition
app.service('commonService', function ($http) {
var info;
return {
getInfo: getInfo,
setInfo: setInfo
};
// .................
function getInfo() {
return info;
}
function setInfo(value) {
info = value;
}
});
usage in multiple controllers
app.controller("HomeController", function ($scope, commonService) {
$scope.setInfo = function(value){
commonService.setInfo(value);
};
});
app.controller("MyController", function ($scope, commonService) {
$scope.info = commonService.getInfo();
});
Option 2 - html5 localStorage
You can use the built-in browser local storage and store your data from anywhere
writing
$window.localStorage['my-data'] = 'hello world';
reading
var data = $window.localStorage['my-data']
// ...
check out this awesome project:
https://github.com/grevory/angular-local-storage
Option 3 - via web server api
If you need to persist data among different users, you should save it somewhere in the server side (db / cache)
function getProfile() {
return $http.get(commonService.baseApi + '/api/profile/');
}
function updateProfile(data) {
var json = angular.toJson(data);
return $http.post(commonService.baseApi + '/api/profile/', json);
}
EDIT See Jossef Harush's answer where he has written an in-depth response that covers other methods including this one.
I'd recommend using either localStorage or sessionStorage - http://www.w3schools.com/html/html5_webstorage.asp.
HTML local storage provides two objects for storing data on the client:
window.localStorage - stores data with no expiration date
window.sessionStorage - stores data for one session (data is lost when the browser tab is closed)
This assumes that you don't want to POST/PUT the data to your web service (windows service mention in your question).
If you data is an array or some sort, you can convert it to JSON to store as a string and then when you need it you can parse it back as follows - How do I store an array in localStorage?:
var names = [];
names[0] = prompt("New member name?");
localStorage["names"] = JSON.stringify(names);
//...
var storedNames = JSON.parse(localStorage["names"]);
There is an option not mentioned in other answers (AFAIK).
EVENTS
You can use events for communication between controllers.
It's a straightforward communication that doesn't need a mediator
(like service) and can't be wiped by the user (like HTML storage).
All the code is written in controllers that you are trying to
communicate with and thus very transparent.
A good example how to leverage events to communicate between controllers can be seen below.
The publisher is the scope that wanna publish (in other words let others know something happened). Most don't care about what has happened and are not part of this story.
The subscriber is the one that cares that certain event has been published (in other words when it gets notified hey, this happened, it reacts).
We will use $rootScope as a mediator between publisher and a subscriber. This always works because whatever scope emits an event, $rootScope is a parent of that scope or parent of a parent of a parent.. When $rootScope broadcasts (tells everyone who inherits) about an event, everyone hears (since $rootScope is just that, the root of the scope inheritance tree) so every other scope in app is a child of it or child of a child of a child..
// publisher
angular.module('test', []).controller('CtrlPublish', ['$rootScope','$scope',
function ($rootScope, $scope) {
$scope.send = function() {
$rootScope.$broadcast('eventName', 'message');
};
}]);
// subscriber
angular.module('test').controller('ctrlSubscribe', ['$scope',
function ($scope) {
$scope.$on('eventName', function (event, arg) {
$scope.receiver = 'got your ' + arg;
});
}]);
Above we see two controllers communicating a message to each other using an event. The event has a name, it has to be unique, otherwise, a subscriber doesn't differentiate between events. The event parameter holds autogenerated but sometimes useful data, the message is the payload. In this example, it's a string but it can be any object. So simply put all the data you wish to communicate inside an object and send it via event.
NOTE:
You can avoid using root scope for this purpose (and limit the number of controllers that get notified of an event) in case two scopes are in direct inheritance line of each other. Further explanation below:
$rootScope.$emit only lets other $rootScope listeners catch it. This is good when you don't want every $scope to get it. Mostly a high level communication. Think of it as adults talking to each other in a room so the kids can't hear them.
$rootScope.$broadcast is a method that lets pretty much everything hear it. This would be the equivalent of parents yelling that dinner is ready so everyone in the house hears it.
$scope.$emit is when you want that $scope and all its parents and $rootScope to hear the event. This is a child whining to their parents at home (but not at a grocery store where other kids can hear). This is a shortcut to use when you wanna communicate from the publisher that is a child or n-th child of the subscriber.
$scope.$broadcast is for the $scope itself and its children. This is a child whispering to its stuffed animals so their parents can't hear.
EDIT: I thought plunker with a more elaborate example would be enough so I decided to keep is simple here. This elaborate explanation should be better.
To share data between two controllers on the same page, you can use factories/services. Take a look at Share data between AngularJS controllers for example.
However, if this is across page reloads/refreshes, you will need to store the data in local storage and then read it upon reloading. An example of that is here: How do I store data in local storage using Angularjs?
Checkout this library https://www.npmjs.com/package/angularjs-store
This can help you manage your application state much simpler as it will force you to have a one way data flow on your application.
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.
All, I am a newbie of Backbone. and I am trying to understand the Model of Backone. Especially how to define a Model. so far, I didn't saw a clear or formal way about how to define a Model for backbone.
For example Let's see the set method in help doc .
set
model.set(attributes, [options])
Set a hash of attributes (one or many) on the model.
Say we have some code like below . I think set method actually is assign a javascript object to the Model.
window.Employee = Backbone.Model.extend({
validate:function(attrs){
for(var key in attrs){
if(attrs[key] == ''){
return key + "can not be null";
}
if(key == 'age' && isNaN(attrs.age)){
return "age is numeric";
}
}
}
});
....
var attr = {}; // I can't not sure what is {} mean.
$('#emp-form input,#emp-form select').each(function(){
var input = $(this);//using jquery select input and select. and enumerate all of them.
attr[input.attr('name')] = input.val();//I am not sure what does it means
});
if(employee.set(attr)){
Employees.create(employee);
}
....
in this example ,I didn't saw the classical way which we can see in java class or c# class to define the class fields or methods. but only see a validate function .Is there anybody who can tell me more about it to help me understand? thanks.
To define a model in Backbone you have to extend the Backbone.Model object. For example if you'll like to create a new User model you could write something like this:
var User = Backbone.Model.extend({})
You can also overwrite some model methods to fill your needs. For example you can change the urlRoot attribute to tell the model where should he fetch the data.
Backbone models contain your data in the attributes attribute. You change those attributes by using the model set method and you can read the value stored in the model using the get method. So if you had some inputs where a user can enter information, for example creating a new user with his name and email and you have a form with a text input for both of them. You could do domething like this:
var user = new User;
user.set('name', $('#name').val());
user.set('email', $('#email').val());
attributes = {
name: user.get('name'),
email: user.get('email')
};
user.save(attributes);
There are a lot of ways to re-factor this code to make it look better but it help to see how you could use those methods. You should check the Backbone documentation to explore how they work a little bit more. Hope this helps!
PD: In my example I set an attribute a time, but you could also send a hash of attributes to set more values in one call.
The model in JS is basically a wrapper for data, with CRUD and simple validation functions. To work properly you need to make server functions to work with (ajax), I think this tutorial says it all http://backbonetutorials.com/what-is-a-model/. Instead of database the model works with your application server side.
If you have custom actions (not just add/edit/remove) on your data, you can manually "set()" data, use "onchange" event and refresh your view when needed. You can even attach "onchange" events only on specific fields and make custom functions in your view to handle each special field (for validation or display).
You can define fields at initialize and defaults value, but not custom functions (ofc you can do model.customFuntion() but I don't recommend it.
In order to make it more "clasical way" you need to use the other Backbone functions http://backbonejs.org/#Collection-Underscore-Methods and Backbone.Collection.
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.