Need help to make changes in Angular Factory. Have example working with array JSON data, but need this code work with JSON data from $http request, how do that?
This is example Factory:
angular.module('starter.services', [])
.factory('catgs', function() {
// Might use a resource here that returns a JSON array
// Some fake testing data
var catgs = [{
id: 0,
name: 'CATG1',
items: [{
img:'img/G1I1.png',
name:'category1Item1',
status:'OPEN'
}]
},{
id: 1,
name: 'CATG2',
items: [{
img:'img/G2I1.png',
name:'category2Item1',
status:'OPEN'
},{
img:'img/G2I2.png',
name:'category2Item2',
status:'CLOSED'
}]
}];
return {
all: function() {
return catgs;
},
remove: function(catg) {
catgs.splice(catgs.indexOf(catg), 1);
},
get: function(catgId) {
for (var i = 0; i < catgs.length; i++) {
if (catgs[i].id === parseInt(catgId)) {
return catgs[i];
}
}
return null;
}
};
});
Need help to replace catgs JSON data to $http request, for example musite.com/jsondata.json
//try this
$http.get('your api url or .json').then(function(result){
$scope.jsonData = result.data;
}, function(err) {
console.log(err);
});
I am new in AngularJS.
I download AngularJS theme from URL here
I have to get Data from GET Service.
GET Service URL is given below
http://localhost:8080/api/getHostStatus
that is running correctly.
So I have to use this URL and get data from that service for this I just change the code in my VisitorsController.js
VisitorsController.js file code is given below
(function () {
angular
.module('app')
.controller('VisitorsController', [
VisitorsController
]);
function VisitorsController() {
var vm = this;
// TODO: move data to the service
var servicesUrl = app.serviceCallUrl+'getHostStatus';
console.log(servicesUrl);
var myService = angular.module("app").factory("myService", ['$http', function($http) {
return {
getResponders: (function(response) {
return $http.get(servicesUrl).then(function(response) {
console.log(response.data);
return response.data;
});
})()
};
return myService;
}]);
vm.visitorsChartData = [ {key: 'UP', y: 5264}, { key: 'Critical', y: 3872},{key: 'Warning', y: 1000} ];
vm.chartOptions = {
chart: {
type: 'pieChart',
height: 210,
donut: true,
x: function (d) { return d.key; },
y: function (d) { return d.y; },
valueFormat: (d3.format(".0f")),
color: ['rgb(0, 150, 136)', '#E75753','#fbbc05'],
showLabels: false,
showLegend: false,
title: 'Over 9K',
margin: { top: -10 }
}
};
}
})();
When I run this it is showing me correct servicesUrl on console.
But response.data not showing on console.
So please guide me how can I resolve it.
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!
I have a ui-select field
{
key: 'data_id',
type: 'ui-select',
templateOptions: {
required: true,
label: 'Select label',
options: [],
valueProp: 'id',
labelProp: 'name'
},
controller: function($scope, DataService) {
DataService.getSelectData().then(function(response) {
$scope.to.options = response.data;
});
}
}
How can I access that inner controller in my unit tests and check that data loading for the select field actually works ?
UPDATE:
An example of a test could be as such:
var initializePageController = function() {
return $controller('PageCtrl', {
'$state': $state,
'$stateParams': $stateParams
});
};
var initializeSelectController = function(selectElement) {
return $controller(selectElement.controller, {
'$scope': $scope
});
};
Then test case looks like:
it('should be able to get list of data....', function() {
$scope.to = {};
var vm = initializePageController();
$httpBackend.expectGET(/\/api\/v1\/data...../).respond([
{id: 1, name: 'Data 1'},
{id: 2, name: 'Data 2'}
]);
initializeSelectController(vm.fields[1]);
$httpBackend.flush();
expect($scope.to.options.length).to.equal(2);
});
You could do it a few ways. One option would be to test the controller that contains this configuration. So, if you have the field configuration set to $scope.fields like so:
$scope.fields = [ { /* your field config you have above */ } ];
Then in your test you could do something like:
$controller($scope.fields[0].controller, { mockScope, mockDataService });
Then do your assertions.
I recently wrote some test for a type that uses ui-select. I actually create a formly-form and then run the tests there. I use the following helpers
function compileFormlyForm(){
var html = '<formly-form model="model" fields="fields"></formly-form>';
var element = compile(html)(scope, function (clonedElement) {
sandboxEl.html(clonedElement);
});
scope.$digest();
timeout.flush();
return element;
}
function getSelectController(fieldElement){
return fieldElement.find('.ui-select-container').controller('uiSelect');
}
function getSelectMultipleController(fieldElement){
return fieldElement.find('.ui-select-container').scope().$selectMultiple;
}
function triggerEntry(selectController, inputStr) {
selectController.search = inputStr;
scope.$digest();
try {
timeout.flush();
} catch(exception){
// there is no way to flush and not throw errors if there is nothing to flush.
}
}
// accepts either an element or a select controller
function triggerShowOptions(select){
var selectController = select;
if(angular.isElement(select)){
selectController = getSelectController(select);
}
selectController.activate();
scope.$digest();
}
An example of one of the tests
it('should call typeaheadMethod when the input value changes', function(){
scope.fields = [
{
key: 'selectOneThing',
type: 'singleSelect'
},
{
key: 'selectManyThings',
type: 'multipleSelect'
}
];
scope.model = {};
var formlyForm = compileFormlyForm();
var selects = formlyForm.find('.formly-field');
var singleSelectCtrl = getSelectController(selects.eq(0));
triggerEntry(singleSelectCtrl, 'woo');
expect(selectResourceManagerMock.searchAll.calls.count()).toEqual(1);
var multiSelectCtrl = getSelectController(selects.eq(1));
triggerEntry(multiSelectCtrl, 'woo');
expect(selectResourceManagerMock.searchAll.calls.count()).toEqual(2);
});