How to split angularjs controller into chunks? - angularjs

I am very new to AngularJs and hope I can get some help here. Below I have two controllers that are very similar. One is for editing an item and one for adding a new item. I would like to know how I can refactor this code in order to reuse most of this code for both controllers or simply use one controller for both. I originally tried to use one controller for both but the new item page wouldn't let me type anything into the fields. I supposed because there was no current model data like there is when editing.
Any help would be appreciated.
tplApp.controller('editController', function ($scope, $http, $routeParams){
$scope.template = {};
$scope.id = $routeParams.template_id;
$http.get(baseUrl+'templates/get_template/'+$scope.id).success(function(data) {
$scope.template = data;
});
$scope.bodyOptions = {
plugins: 'link image code',
toolbar: 'bold, italic, underline, alignleft, aligncenter, alignright, alignjustify, styleselect, bullist, numlist, outdent, indent, removeformat, mybutton, code',
height: 300,
menubar: false,
statusbar: false,
setup: function(editor) {
editor.addButton('mybutton', {
type: 'menubutton',
text: 'Variables',
icon: false,
menu: [
{text: 'Candidate Name', onclick: function() {editor.insertContent('%name%');}},
{text: 'Company Name', onclick: function() {editor.insertContent('%company-name%');}},
{text: 'Today\'s Date', onclick: function() {editor.insertContent('%date%');}},
{text: 'Your Name', onclick: function() {editor.insertContent('%your-name%');}},
]
});
}
};
$scope.saveTemplate = function() {
$http({
method : 'POST',
url : baseUrl+'templates/save',
data : $.param($scope.template),
headers : { 'Content-Type': 'application/x-www-form-urlencoded' }
})
.success(function(data) {
$scope.message = data.message;
if (data.success) {
console.log(data);
$scope.templates = data.templates;
}
});
};
});
tplApp.controller('addController', function ($scope, $http){
$scope.template = {};
$scope.bodyOptions = {
plugins: 'link image code',
toolbar: 'bold, italic, underline, alignleft, aligncenter, alignright, alignjustify, styleselect, bullist, numlist, outdent, indent, removeformat, mybutton, code',
height: 300,
menubar: false,
statusbar: false,
setup: function(editor) {
editor.addButton('mybutton', {
type: 'menubutton',
text: 'Variables',
icon: false,
menu: [
{text: 'Candidate Name', onclick: function() {editor.insertContent('%name%');}},
{text: 'Company Name', onclick: function() {editor.insertContent('%company-name%');}},
{text: 'Today\'s Date', onclick: function() {editor.insertContent('%date%');}},
{text: 'Your Name', onclick: function() {editor.insertContent('%your-name%');}},
]
});
}
};
$scope.saveTemplate = function() {
$http({
method : 'POST',
url : baseUrl+'templates/save',
data : $.param($scope.template),
headers : { 'Content-Type': 'application/x-www-form-urlencoded' }
})
.success(function(data) {
$scope.message = data.message;
if (data.success) {
console.log(data);
$scope.templates = data.templates;
}
});
};
});

What you want is a service: https://docs.angularjs.org/guide/services
"You can use services to organize and share code across your app"
Google it and/or look here in SO for more info.

Try these changes. You will need 2 services + 2 controllers (for saveTemplateService.js and buildBodyOptionsService.js ). The services will be injected to the controllers.
At the end,don't forget to add the src of each service in the template/html file.
I think it could be even more reduced (without the $http's success in the controllers) but since we are working with callbacks here, I'm not sure. Try it out ;).
It might not be fully functional because I don't have all your code. But debugging should solve it.
saveTemplateService.js:
app.factory('saveTemplateService', function ( baseUrl, $http ) {
return $http({
method : 'POST',
url : baseUrl+'templates/save',
data : $.param($scope.template), //fix these (injecting them like baseUrl)
headers : { 'Content-Type': 'application/x-www-form-urlencoded' }
});
});
buildBodyOptionsService.js:
app.factory('buildBodyOptionsService', function() {
return {
build: function ( editor ) { //maybe editor needs to be injected
var output = {
plugins: 'link image code',
toolbar: 'bold, italic, underline, alignleft, aligncenter, alignright, alignjustify, styleselect, bullist, numlist, outdent, indent, removeformat, mybutton, code',
height: 300,
menubar: false,
statusbar: false,
setup: function(editor) {
editor.addButton('mybutton', {
type: 'menubutton',
text: 'Variables',
icon: false,
menu: [
{text: 'Candidate Name', onclick: function() {editor.insertContent('%name%');}}, // I dont like this functions here. There must be a better way to do this (ex: in a partial html with ng-click)
{text: 'Company Name', onclick: function() {editor.insertContent('%company-name%');}},
{text: 'Today\'s Date', onclick: function() {editor.insertContent('%date%');}},
{text: 'Your Name', onclick: function() {editor.insertContent('%your-name%');}},
]
});
}
};
return output;
}
};
});
editController.js
tplApp.controller('editController', function ($scope, saveTemplateService, buildBodyOptionsService) {
$scope.template = {};
$scope.id = $routeParams.template_id;
$http.get(baseUrl+'templates/get_template/'+$scope.id)
.success(function(data) {
$scope.template = data;
});
// call 1st service
$scope.bodyOptions = buildBodyOptionsService.build( editor );
// call 2nd service
$scope.saveTemplate = saveTemplateService.success( function(data) {
$scope.message = data.message;
if (data.success) {
console.log(data); //use $log.info() instead
$scope.templates = data.templates;
}
});
});
addController.js
tplApp.controller('addController', function ($scope, saveTemplateService, buildBodyOptionsService) {
$scope.template = {};
// call 1st service
$scope.bodyOptions = buildBodyOptionsService.build( editor );
// call 2nd service
$scope.saveTemplate = saveTemplateService.success( function(data) {
$scope.message = data.message;
if (data.success) {
console.log(data); //use $log.info() instead
$scope.templates = data.templates;
}
});
});
Check these links for more info:
custom services in Angular (really well explained): https://youtu.be/rlx1cf7qM0E?list=PL6n9fhu94yhWKHkcL7RJmmXyxkuFB3KSl
promises in services: AngularJS: Performing $http request inside custom service and returning data
(relevant) Angular $http difference between .success() and .then()

I would probably put everything in a single service (templateService). I'm not sure if the bodyOption belongs there but for now I would just put it there. Then I would move the load/save template function to the service.
You could probably do more, for instance, your might set the $scope.templateService = templateService and in your html use templateService.bodyOptions/templates directly.
Also, you could probably move the save.success from the controller to the service aswell.
tplApp.service('templateService', function($http, $routeParams) {
var self = this;
this.template = {};
this.loadTemplate = function() {
$http.get(baseUrl+'templates/get_template/' + $routeParams.template_id)
.success(function(data) {
self.template = data;
});
};
this.saveTemplate = function() {
return $http({
method: 'POST',
url: baseUrl + 'templates/save',
data: $.param(self.template),
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
});
};
this.bodyOptions = {
plugins: 'link image code',
toolbar: 'bold, italic, underline, alignleft, aligncenter, alignright, alignjustify, styleselect, bullist, numlist, outdent, indent, removeformat, mybutton, code',
height: 300,
menubar: false,
statusbar: false,
setup: function(editor) {
editor.addButton('mybutton', {
type: 'menubutton',
text: 'Variables',
icon: false,
menu: [
{text: 'Candidate Name', onclick: function() {editor.insertContent('%name%');}},
{text: 'Company Name', onclick: function() {editor.insertContent('%company-name%');}},
{text: 'Today\'s Date', onclick: function() {editor.insertContent('%date%');}},
{text: 'Your Name', onclick: function() {editor.insertContent('%your-name%');}},
]
});
}
};
});
tplApp.controller('editAndAddController', function ($scope, templateService){
$scope.template = templateService.template;
$scope.bodyOptions = templateService.bodyOptions;
if(edit) {
templateService.loadTemplate();
}
$scope.saveTemplate = function() {
templateService.saveTemplate()
.success(function(data) {
$scope.message = data.message;
if (data.success) {
console.log(data);
$scope.templates = data.templates;
}
});
};
});
Ideally your controller would probably look like this:
tplApp.controller('editAndAddController', function ($scope, templateService){
$scope.templateService = templateService;
if(edit) {
templateService.loadTemplate();
}
});
Heads up! This is just example, code is not tested!

Related

Hightchart Angularjs feeding controller

I am trying to plot a graph with Hightchart, my data comes from an REST web service.
I am using angularjs (1.5) to get the data.
my Service look like :
app.factory('ServicesImpl', function($http){
var obj = {};
obj.getData = function(){
return $http.get('http://localhost:8080/chartDepense/');
};
return obj;
});
my controller is :
app.controller('myController', function ($scope, $rootScope, ServicesImpl) {
$scope.chart = ServicesImpl.getData().then(function(r){
return r.data;
});
console.log($scope.chart);
$scope.chartOptions = {
title: {
text: $scope.chart.libelle
},
xAxis: {
categories: $scope.chart.categories ,
title: {
text: 'les X'
}
},
yAxis: {
title: {
text: 'les Y'
}
},
series: [{
data: $scope.chart.peroides
}]
};
});
and I got this :
I don't know can I can get my data in my controller to put in chartConfig?

cannot call function from within ionic popup

I am new to ionic.
I have a function in my controller. I want to call that function from another function which has ionic popup. but, the function call does not happen.
Here is the code
Function with ionic popup
$scope.show = function() {
var myPopup = $ionicPopup.show({
templateUrl: 'views/d.html',
cssClass: 'custom-ipopup',
title: 'Welcome to myapp',
subTitle: 'enter username.',
scope: $scope,
buttons: [
{ text: '<b>Cancel</b>' },
{
text: 'Test',
type: 'button-positive',
onTap: function(e) {
e.preventDefault();
scope.usernameCheck = usernameLookup();
console.log(scope.usernameCheck); //prints undefined
}
}
]
});
};
usernameLookup() function i am calling from $ioninPopup.show()'s onTap()
$scope.usernameLookup = function() {
console.log('inside usernameLookup()');
$scope.lookingDB = true;
$scope.unamePresent = 3;
$http({
method: 'GET',
url: $rootScope.cred.url + '/api',
params: {
}
}).success(function(d) {
$scope.lookingDB = false;
$scope.unamePresent = 1;
return $scope.unamePresent
}).error(function(d) {
$scope.lookingDB = false;
$scope.unamePresent = 2;
return $scope.unamePresent
});
};
Can someone tell me why is it not calling usernameLookup() function? Both these methods are in same controller.

How to create a callback function on angular datatable select

I am having a problem on how to trigger an event when a certain data is selected using angular datatable select.
I want to enable Edit and Delete Button when there is a selected row.
Here is my code:
app.controller('SampleController', function($http, DTOptionsBuilder, DTColumnBuilder, $q) {
var vm = this;
vm.dtOptions = DTOptionsBuilder.fromFnPromise(function() {
var defer = $q.defer();
$http.get("{!! url('sample/api/v1/questions?questionType=1') !!}")
.then(function(response) {
defer.resolve(response.data.active_question_contents);
});
return defer.promise;
})
.withDOM('frtip')
.withPaginationType('full_numbers')
.withDisplayLength(5)
.withButtons([{
text: 'Add',
key: '1',
action: function(e, dt, node, config) {
alert('Add');
}
}, {
text: 'Edit',
key: '2',
action: function(e, dt, node, config) {
alert('Edit');
},
enabled: false
}, {
text: 'Delete',
key: '3',
action: function(e, dt, node, config) {
alert('Delete');
},
enabled: false
}])
.withSelect({
style: 'os',
selector: 'td:first-child'
});
I tried drawCallback but it only triggered once.
I already solved it! I just added this.DataTable() inside the drawCallback function.
Here is the code:
vm.dtOptions.drawCallback = function() {
var table = this.DataTable();
table.on('select', function() {
alert('Selected!');
// Enable/disable buttons here...
});
};

AngularJS Testing - ngGrid is not available

I'm using Karma and Jasmine to run tests on this project.
I'm getting an error of:
Failed to instantiate module itunes due to:
Failed to instantiate module ngGrid due to
Module 'ngGrid' is not available! You either misspelled the module name or forgot to load it.
In the spec below I'm trying to simulate the call to the service by using spyOn to grab the call to itunes.getArtist and return a test value. If it works I should expect findArtist to return an Object. Right now I get this error even if I block out the entire spec and run a different spec like this one:
it('should call getArtist', function() {
spyOn(itunesService, 'getArtist');
scope.artist = "The Dudes";
$scope.findArtist();
expect(itunesService.getArtist).toHaveBeenCalled();
})
Here's the app
var app = angular.module('itunes', ['ngGrid'])
And here's the controller
var app = angular.module('itunes');
app.controller('mainCtrl', function($scope, itunesService, $timeout){
$scope.songData = ...
$scope.gridOptions = {
data: 'songData',
height: '110px',
sortInfo: {fields: ['Song', 'Artist', 'Collection', 'Type'], directions: ['asc']},
columnDefs: [
{field: 'Play', displayName: 'Play', width: '40px', cellTemplate: '<div class="ngCellText" ng-class="col.colIndex()"><img src="http://www.icty.org/x/image/Miscellaneous/play_icon30x30.png"></div>'},
{field: 'Artist', displayName: 'Artist'},
{field: 'Collection', displayName: 'Collection'},
{field: 'AlbumArt', displayName: 'Album Art', width: '110px', cellTemplate: '<div class="ngCellText" ng-class="col.colIndex()"><img src="{{row.getProperty(col.field)}}"></div>'},
{field: 'Type', displayName: 'Type'},
{field: 'CollectionPrice', displayName: 'Collection Price'},
]
};
$scope.findArtist = function() {
itunesService.getArtist($scope.artist).then(function(response) {
$scope.songData = response.data.results.map(function(item) {
return {
AlbumArt: item.artworkUrl100,
Artist: item.artistName,
Collection: item.collectionName,
CollectionPrice: item.collectionPrice,
Play: item.previewUrl,
Type: item.kind
}
});
})
}
$scope.getSongData = function() {
$scope.findArtist();
}
});
Here's the service
var app = angular.module('itunes');
app.service('itunesService', function($http, $q){
this.getArtist = function(name) {
var baseUrl = 'https://itunes.apple.com/search?term=';
return $http({
method: "JSONP",
url: baseUrl + name + '&callback=JSON_CALLBACK'
})
}
});
And here's the spec
describe('itunes', function() {
beforeEach(module('itunes'));
describe('mainCtrl', function() {
var controller, scope, itunesService, ngGrid;
beforeEach(inject(function($rootScope, _itunesService_, $controller) {
scope = $rootScope.$new();
itunesService = _itunesService_;
controller = $controller('mainCtrl', { $scope: scope });
}))
it('should find artist with itunesService', function(done) {
var testValue = {
data: {
artworkUrl100: 'someUrl',
artistName: 'Beach Dudes',
collectionName: 'Beach it up',
collectionPrice: '$999,999,999.99',
previewUrl: 'somePreviewUrl',
kind: 'song'
}
};
spyOn(itunesService, 'getArtist').and.returnValue(testValue);
scope.artist = "Beach Dudes"
var result = scope.findArtist();
expect(result).toEqual(jasmine.any(Object))
})
})
})
ngGrid.min.js needs to be added to the karma config file, like so:
files: [
'bower_components/angular/angular.js',
'bower_components/angular-mocks/angular-mocks.js',
'bower_components/jQuery/jquery-2.2.0.min.js',
'bower_components/angular-mocks/ng-grid.min.js',
'js/app.js',
'js/mainCtrl.js',
'js/itunesService.js',
'test/spec/test.js'
],
This also requires jQuery to be added.

create/update user story using rally app sdk

Until now, I have been querying the data stores using Rally App SDK, however, this time I have to update a story using the js sdk. I tried looking up for examples for some sample code that demonstrates how the App SDK can be used to update/add values in Rally. I have been doing CRUD operations using Ruby Rally API but never really did it with the app sdk.
Can anyone provide some sample code or any link to where I could check it out?
Thanks
See this help document on updating and creating reocrds. Below are examples - one updates a story, the other creates a story. There is not much going on in terms of UI: please enable DevTools console to see console.log output.
Here is an example of updating a Defect Collection on a User Story:
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
launch: function() {
console.log("launch");
Rally.data.ModelFactory.getModel({
type: 'User Story',
success: this._onModelRetrieved,
scope: this
});
},
_onModelRetrieved: function(model) {
console.log("_onModelRetrieved");
this.model = model;
this._readRecord(model);
},
_readRecord: function(model) {
var id = 13888228557;
console.log("_readRecord");
this.model.load(id, {
fetch: ['Name', 'Defects'],
callback: this._onRecordRead,
scope: this
});
},
_onRecordRead: function(record, operation) {
console.log('name...', record.get('Name'));
console.log('defects...', record.get('Defects'));
if(operation.wasSuccessful()) {
//load store first by passing additional config to getCollection method
var defectStore = record.getCollection('Defects', {
autoLoad: true,
listeners: { load: function() {
//once loaded now do the add and sync
defectStore.add({'_ref':'/defect/13303315495'});
defectStore.sync({
callback: function() {
console.log('success');
}
});
}}
});
}
},
});
Here is an example of creating a user story, setting a project and scheduling for an iteration:
Ext.define('CustomApp', {
extend: 'Rally.app.TimeboxScopedApp',
componentCls: 'app',
scopeType: 'iteration',
comboboxConfig: {
fieldLabel: 'Select an Iteration:',
labelWidth: 100,
width: 300
},
addContent: function() {
this._getIteration();
},
onScopeChange: function() {
this._getIteration();
},
_getIteration: function() {
var iteration = this.getContext().getTimeboxScope().record.get('_ref');
console.log('iteration',iteration);
if (!this.down('#b2')) {
var that = this;
var cb = Ext.create('Ext.Container', {
items: [
{
xtype : 'rallybutton',
text : 'create',
id: 'b2',
handler: function() {
that._getModel(iteration);
}
}
]
});
this.add(cb);
}
},
_getModel: function(iteration){
var that = this;
Rally.data.ModelFactory.getModel({
type: 'UserStory',
context: {
workspace: '/workspace/12352608129'
},
success: function(model) { //success on model retrieved
that._model = model;
var story = Ext.create(model, {
Name: 'story 777',
Description: 'created via appsdk2'
});
story.save({
callback: function(result, operation) {
if(operation.wasSuccessful()) {
console.log("_ref",result.get('_ref'), ' ', result.get('Name'));
that._record = result;
that._readAndUpdate(iteration);
}
else{
console.log("?");
}
}
});
}
});
},
_readAndUpdate:function(iteration){
var id = this._record.get('ObjectID');
console.log('OID', id);
this._model.load(id,{
fetch: ['Name', 'FormattedID', 'ScheduleState', 'Iteration'],
callback: function(record, operation){
console.log('ScheduleState prior to update:', record.get('ScheduleState'));
console.log('Iteration prior to update:', record.get('Iteration'));
record.set('ScheduleState','In-Progress');
record.set('Iteration', iteration);
record.set('Project', '/project/12352608219')
record.save({
callback: function(record, operation) {
if(operation.wasSuccessful()) {
console.log('ScheduleState after update..', record.get('ScheduleState'));
console.log('Iteration after update..', record.get('Iteration'));
}
else{
console.log("?");
}
}
});
}
})
}
});

Resources