Critical view on object based application structure in AngularJS - javascript

As i tried to learn some Angular in the past weeks, i have adopted a certain application architecture.
Assuming a intermediate large SPA, i create the view and apply a maincontroller:
<html ng-app="app">
<body ng-controller="appCtrl">
<!-- All of the view goes in here -->
</body>
</html>
Now i think about the stuff that my application will have to do. Assuming that this one would have to store some data and working with it, i would create a provider to build a "class".
var data = angular.module('data', []);
data.provider('$dataObject',function(){
this.$get = function($http){
function DataObject(){
var field = "testvalue";
var object = {
// ...
}
}
DataObject.prototype.processData = function(){
// do something
};
return {
Shipment: function(){
return new Shipment();
}
}
}
});
I would then create an instance of that class in my maincontroller, storing all of the necessary data from the view into that object, processing it by calling the class methods.
If the application would need another functionality, e.g a dialog to pick some stuff, i would repeat the steps above and create a class for that dialog.
Now, while i am quite comfortable to do things that way, i still wonder if someone would consider this bad practice, and if so, why is that and what could i do better?

Creating services/providers for your models/data (with APIs to expose the data), and then injecting them into controllers is the "Angular way."
When I design an angular app, I think about the models I need and create services for them. I then (or in parallel) think about the views and design those. I normally create custom directives at this point also. Lastly, each view gets a controller, whose job it is to glue the models/data from the services that the view needs. (Make controllers as thin as possible.)

Related

Lodash & Angular: Transform JSON object into view model

I am trying to find the best way to transform a large JSON object into a view model. Previously, I had the model incorporated into the view, which is a bad practice. So, now I have the model being generated inside of a controller. I am using Lodash as a utility library.
My current design plan is to transform the JSON object into a "master" array that is accessible in the controller's scope. The JSON file is being served by Express. ModelService simply gets this file to make it available in the controller.
$scope.arr is the "master" array that I want to use in the view.
I also made the JSON data available for viewing at an external link since it is so large. Here it is.
(function() {
'use strict';
angular
.module('app')
.controller('ModelController', ModelController);
function ModelController($scope, ModelService, _) {
$scope.jsonData = ModelService.getAll();
$scope.getData = function() {
$scope.jsonData.$promise.then(function(data) {
$scope.all = data;
$scope.arr = [];
_.mapValues($scope.all.parent1.clusters, function(cluster) {
$scope.arr.push(cluster.name);
_.mapValues(cluster.subclusters, function(subcluster) {
$scope.arr.push(subcluster.name);
_.mapValues(subcluster.entities, function(entity) {
// map entities
})
});
});
});
};
$scope.getData();
}
})();
This code is just adding cluster and subcluster names to the array. I'd like the subclusters to be mapped to their parent cluster. The idea I have for doing this involves transforming each cluster element into its own array, and then adding the subclusters, and then transforming each subcluster into an array in order to map the entities to them. This seems tedious and inefficient. So, I am looking for a better way to achieve this.
It would be nice if I could add each cluster object to the array in one fell swoop without all the mapping and converting objects to arrays. Is that possible at all?
The wireframe view looks like this. The Flex Cluster Title is the name of the subcluster, and each number inside of them is an entity.
Firstly, I would move this processing into the service. It's easier to test, and keeps your view separated from your models (Controllers, are really more part of the "View" IMO when it comes to Angular especially if you're considering upgrading to Angular 2.0 in the future).
In Angular, I think the appropriate way to solve this, would be to use components (or directives) combined with ng-repeat.
The page template:
<!-- Page template, assume $ctrl is your controller, $ctrl.clusters is the data -->
<cluster ng-repeat = "cluster in $ctrl.clusters"
cluster-data="cluster" >
</cluster>
The cluster directive template:
<!-- Assume $ctrl is the controller for the cluster directive, $ctrl.cluster is the cluster object. -->
<div class="name">{{$ctrl.cluster}}</div>
<div class="subClusterNames"
ng-repeat="subCluster in $ctrl.cluster.subClusters>
{{subCluster.name}}
</div>
You might think that this is mapping your data too closely to the view, but as long as you use components to display your data (ie, don't put it all into one template) I think you'll be fine.

Angular JS Concept 'Separation Of Concerns' --> CRUD Screen

I have a two CRUD screens in AngularJS.
1) Separate html file for View, Add and Edit. View Controller, Add Controller and Edit Controllers also Separate. This structure creating more duplicate code in html and controller side.
2) Separate html file for View, Add. View Controller, Add Controller only Separate. During edit mode I'm using a Boolean in controller to find its in edit mode or not.
I'm new to AngularJS. Anybody clearly tell me pros and cons, which way is correct in AngularJS 'Separation Of Concerns' concept.
I'm not sure you're going to the good direction.
By separation of concern, what is intended is to split the code that is responsible for managing the view, and the code responsible to make calls to your server. Imagine you want to make CRUD around a person, I would do the following :
service :
angular.module('app').factory("personService", ["$http", function($http)]){
return {
create : create,
update: update,
remove : remove,
get: get
};
function create(person){
return http.post("person/create", person);
}
// other functions
}
Then, I would only have 1 controller for everything :
angular.module("app").controller("PersonController", ["personService", function(personService)]){
var self = this;
self.isUpdate = true; // Insert logic here
self.isCreate = false; // Insert logic here
init();
self.save = function(){
var promise = self.isCreate ?
personService.create(self.person)
:personService.update(self.person);
promise.then(function(result){
// Handle return of save;
});
};
function init(){
if (!self.isCreate){
personService.get(personId).then(function(result){
self.person = result.data.person;
});
}
}
}
And then I would have the following view :
<div ng-controller="personController as person">
<label>Name: </label>
<input type="text" ng-disabled="!person.isUpdate" ng-model="person.person.name" />
<button ng-click="person.isUpdate = !person.isUpdate;">Edit</button>
<!-- Edit : the code 'person.isUpdate = !person.isUpdate;' could also be into a controller's function (like the save function) -->
<button ng-click="person.save()" ng-if="person.isUpdate">Save</save>
</div>
I also recommend you to read this : https://github.com/johnpapa/angular-styleguide
Separation of concerns means that you have a well defined structure of your application: the data model in the application is decoupled from the business and presentation logic. It is the base of the MVC pattern, which defines the view, the controller and the model.
This separation makes the code maintainable and easy to test.
Follow this guidance how to architect your Angular application.
The model should:
Include the domain data;
Implement the management of the domain data (query, edit, delete, storing mechanism, REST implementation, http fetching);
Expose an API that makes possibly the model usage in controller or other service;
The model should not:
Provide the details on how the domain data is managed (all REST implementation, http calls should be encapsulated in the model);
Contain logic that transforms the model based on user interaction (it is controller's role);
Contain logic for displaying data to the user (this is the view’s job);
A controller should:
Contain the logic required to initialize the scope;
Contain the logic/behaviors required by the view to present data from the scope;
Contain the logic/behaviors required to update the scope based on user interaction;
A controller should not:
Contain logic that manipulates the DOM (that is the job of the view);
Contain logic that manages the persistence of data (that is the job of the model);
Manipulate data outside of the scope;
A view should:
Contain the logic and markup required to present data to the user
A view should not:
Contain complex logic (this is better placed in a controller);
Contain logic that creates, stores, or manipulates the domain model.
This guidance is taken from this awesome book, which I recommend for any starting AngularJS developer.
Related to your example, in my opinion you should:
Create a controller and a template for editing and adding; Depending on model's isNew property, you can apply editing or adding action;
Create a controller and a template for viewing the model;
Optionally, if you have a collection of models, you can create a new controller and view also.
However it depends on the amount of logic behind the model. If the model is trivial, probably you can implement everything in a single controller and view. But it's rare.

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.

How do I stop a naming clash in angularJS?

We have a service...
angular.module('app.services').service('Booking', function () {})
In our controller we do this...
angular.module('app.controllers')
.controller('PassengersController',
function (Booking) {
Booking.load();
...
Which has been fine up until now. However the application is growing and we need to create another 'Booking' object. This object should live under a different namespace to avoid naming clashes as I can't see how angular will inject the right dependency. For example we now need...
app.services --> Booking
app.states --> Booking
Ideally we would like to refactor our modules to represent the file structure which is now built by feature but trying one thing at a time does anyone know how I can tell angular to access this new booking object explicitly in my controllers?
I was thinking it could be like this....
angular.module('app.controllers')
.controller('PassengersController',
function (Booking) {
Booking.load();
var bookingState = app.states.Booking;
bookingState.goToNextState();
...
Thats ths pseudocode anyway, if it was just pure javascript easy peasy I would just make a global variable and attach some modules but I am not sure how we will do it so we can leverage angularJs's dependency injection and stop the naming clash!

Originzational MVC and Angular keeping from injecting what I don't need

My problem is I have one ng-app. Does that mean I have to do dependency injection for plugins I may not be using on that given view? Example I bring in ngTagsInput does that mean I have to do it even when the view doesn't call for it? That would mean I have to include that js for every view even if it doesn't use ngTagsInput.
I have a very large MVC .NET application and I am trying to figure out what is he best way to handle bringing in external plugins.
I have some code like so in our Main _Layout template:
<html ng-app="ourApp">
<head>
<!-- all of our includes are here -->
</head>
<body>
<directive></directive>
<anotherdirective></anotherdirective>
#RenderBody()
</body>
</html>
RenderBody is where MVC slides in our views from our mvc routing.That view may look like so:
<script src="~/Scripts/HomeAngular.js"></script>
<div ng-controller="HomeCtrl">
<directive3></directive3>
</div>
App JS:
var app = angular.module('ourApp', ['ngTagsInput']);
IS there a way I can get around having to inject ngTagsInput on every view page even if i don't need it?
There are several different ways to handle Dependency Injection (DI) in angular. In this first example, you simply define ngTagsInput before declaring the controller. If ngTagsInput is a service, for example, you'll need to return an object with methods that allow you to access the data inside of that service.
For example:
app.service('ngTagsInput', function($scope) {
var data = {};
return {
getData = function() {
return data;
},
setData = function(val) {
data = val;
}
};
});
app.controller('HomeCtrl', function($scope, ngTagsInput) {
// ...
$scope.tags = ngTagsInput; // whatever you want to do with it
});
However, there's a problem...
In the example above, if you run that code through a minifier, you'll get $scope turned into some variable (a, b, x, etc...) and angular wont know what to do with that.
In this method, we use an array. The last item in your array is your lambda function for the controller, which takes 1 argument for each previous item in the array:
app.controller('HomeCtrl', ['$scope', 'ngTagsInput', function(scp, ngTagIn) {
// ...
}]);
This code can be run through a minifier just fine, since the dependencies are strings in the array and can be renamed to anything you want inside the function's parameters.
DI also works in directives, factories, filters, and services with the same syntax; not just controllers and modules.
app.directive('HomeCtrl', ['$scope', 'ngTagsInput', function(scp, ngTagIn) {
// ...
}]);
Couldn't you break down your application further into smaller modules instead of just one. Then you can inject the smaller modules into the app module and only inject the ngTagsInput dependency on the modules that actually need it. That's how I typically break up my application by function or area.

Categories

Resources