I use websql to get my data from web sql
I create my controller, my model and my service :
Service
app.project.service('ProjectService', ['$webSql', 'config', function ($webSql, config) {
var db = $webSql.openDatabase('MyApp', '1.0', 'MyApp', 2 * 1024 * 1024);
this.getAll = function (params) {
var projects = [];
db.selectAll("projects").then(function(results) {
for(var i=0; i < results.rows.length; i++){
projects.push(results.rows.item(i));
}
return projects;
})
};
this.create = function (project) {
return db.insert('projects', project).then(function(results) {
console.log(results.insertId);
});
};
this.createTable = function () {
return db.createTable('projects', {
"id":{
"type": "INTEGER",
"null": "NOT NULL", // default is "NULL" (if not defined)
"primary": true, // primary
"auto_increment": true // auto increment
},
"created":{
"type": "TIMESTAMP",
"null": "NOT NULL",
"default": "CURRENT_TIMESTAMP" // default value
},
"name":{
"type": "TEXT",
"null": "NOT NULL"
},
"path": {
"type": "TEXT",
"null": "NOT NULL"
},
"file": {
"type": "TEXT",
"null": "NOT NULL"
}
});
};
}]);
Model
app.project.factory('Project', function (ProjectService) {
var Project = function (properties) {
// Model
this.file = null;
this.name = null;
this.path = null;
this.is_active = null;
angular.extend(this, properties);
};
Project.prototype.setModel = function (obj) {
angular.extend(this, obj);
};
// With service
// ------------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------
Project.prototype.getAll = function () {
return ProjectService.getAll();
};
Project.prototype.create = function (project) {
return ProjectService.create(project);
};
Project.prototype.createTable = function () {
return ProjectService.createTable();
};
return Project;
});
Controller
app.home.controller('HomeController', ['$scope', '$http', '$webSql', 'Project', function ($scope, $http, $webSql, Project) {
var project = new Project();
$scope.refresh = function(){
new_project = {"name": 'Project1 WEB PREPROD', "path": 'project1.preprod.site.com', 'file': '/release.txt'};
// project.createTable();
project.create(new_project);
$scope.projects = project.getAll();
console.log(project.getAll());
};
$scope.refresh();
}]);
I can createTable and create a new object (project), but when I execute $scope.projects = project.getAll();
my console log return undefined
I tried this in my service
this.getAll = function (params) {
var projects = [];
db.selectAll("projects").then(function(results) {
for(var i=0; i < results.rows.length; i++){
projects.push(results.rows.item(i));
}
}).then(function() {
return projects;
})
};
But I have the same problem
You missed to return promise from service getAll method. Basically db.selectAll function returns a promise, which you are not returning.
this.getAll = function (params) {
//return promise from here.
return db.selectAll("projects").then(function(results) {
for(var i=0; i < results.rows.length; i++){
projects.push(results.rows.item(i));
}
return projects;
})
};
and replace this :
$scope.projects = project.getAll();
by
project.getAll().then(function(results) {
$scope.projects = results;
});
Related
Service
angular.module('App').service('producerContactSvc', ['$http', 'configSvc', function($http, configSvc) {
//Reset headers to avoid OPTIONS request (aka preflight)
$http.defaults.headers.common = {};
var _this = this;
_this.getProducerContacts = function(producerId) {
var endpoint = configSvc.backendUrl + '/producerContact/read?producerId=' + producerId;
return $http.get(endpoint)
.then(function(response) {
return response.data;
})
.catch(function(response) {
return response.data;
});
};
_this.addProducerContact = function(producerContact) {
var endpoint = configSvc.backendUrl + '/producerContact/create';
return $http.post(endpoint, producerContact)
.success(function(response) {
return response.data;
})
.catch(function(response) {
return response.data;
});
};
_this.deleteProducerContact = function(producerContactId, producerId) {
var endpoint = configSvc.backendUrl + '/producerContact/delete?producerContactId=' + producerContactId + '&' + 'producerId=' + producerId;
return $http.delete(endpoint)
.then(function(response) {
return response.data;
})
.catch(function(response) {
return response.data;
});
};
_this.updateProducerContact = function(producerContact) {
var endpoint = configSvc.backendUrl + '/producerContact/update';
return $http.put(endpoint, producerContact)
.then(function(response) {
return response.data;
})
.catch(function(response) {
return response.data;
});
};
}]);
Controller
angular.module('App').controller('producerAdminCtrl', ['$scope', '$rootScope', 'producerSvc', '$mdDialog', 'producerContactSvc', 'errorToastSvc', function($scope, $rootScope, producerSvc, $mdDialog, producerContactSvc, errorToastSvc) {
$scope.results = false;
$scope.producerTypeCodes = ['CX', 'FG'];
$scope.producerId = null;
$scope.documents = [];
$scope.selectedDocs = [];
$scope.producerContactRows = [];
$scope.missingName = false;
$scope.producerContactTypeCodes = ['MC', 'BC'];
/**
* Get a list of all producers on load
*/
producerSvc.getAllProducers()
.then(function(response) {
if (response !== null) {
$scope.unusedProducers = response.data;
} else {
$scope.noFound = true;
$scope.results = false;
$scope.message = 'No Data Received';
}
});
/**
* Perform a search on a producer. This will call the backend and find information about the producer searched for
*/
$scope.search = function() {
$scope.documents = [];
$scope.selectedDocs = [];
$scope.query = $scope.selectedItem.producerId;
producerSvc.getProducer($scope.query).then(function(response) {
if (response && response.data) {
$scope.producer = response.data;
$scope.producerId = $scope.query;
$scope.producer.inactiveDate = response.data.inactiveDate === null ? null : new Date(response.data.inactiveDate);
producerSvc.getAllDocs().then(function(response) {
_.forEach(response, function(doc) {
var docObject = {
documentId: doc.documentId,
documentDescription: doc.documentDescription,
documentName: doc.documentName,
selected: false
};
$scope.documents.push(docObject);
});
producerSvc.getProducerDocs($scope.producerId).then(function(response2) {
_.forEach(response2.data, function(docProducer) {
var docId = docProducer.documentId;
_.forEach($scope.documents, function(doc) {
if (docId === doc.documentId) {
doc.selected = true;
$scope.selectedDocs.push(doc);
}
});
});
$scope.documents.sort(function(x, y) {
return (x.selected === y.selected) ? 0 : x.selected ? -1 : 1;
});
$scope.results = true;
$scope.noFound = false;
});
});
} else {
$scope.noFound = true;
$scope.results = false;
$scope.message = 'No Producer Found';
}
});
producerContactSvc.getProducerContacts($scope.query).then(function(response) {
_.forEach(response, function(producerContact) {
var producerContactObject = {
producerContactName: producerContact.producerContactName,
editable: false,
producerContactEmail: producerContact.producerContactEmail,
producerContactType: producerContact.producerContactType,
invalid: false,
producerContactIsNew: false,
producerContactId: producerContact.producerContactId,
producerId: producerContact.producerId
};
$scope.producerContactRows.push(producerContactObject);
});
});
/**
* Add a new producerContact to the producerContactRows array.
*/
$scope.addProducerContact = function() {
var producerId = '';
var producerContactName = '';
var producerContactEmail = '';
var producerContactType = '';
var producerContactObject = {
producerId: producerId,
producerContactName: producerContactName,
editable: true,
producerContactEmail: producerContactEmail,
producerContactType: producerContactType,
invalid: false,
producerContactIsNew: true
};
$scope.producerContactRows.push(producerContactObject);
};
/**
* Enable edit mode on a selected producerContact.
* #param {number} producerContactIndex the index within the producerContactRows array of the producerContact to edit.
*/
$scope.editProducerContact = function(producerContactIndex) {
$scope.producerContactRows[producerContactIndex].editable = true;
};
/**
* Save a selected producerContact.
* #param {number} producerContactIndex the index within the producerContactRows array of the producerContact to save.
*/
$scope.saveProducerContact = function(producerContactIndex) {
if ($scope.producerContactRows[producerContactIndex].producerContactIsNew) {
producerContactSvc.addProducerContact($scope.sanitizeProducerContact($scope.producerContactRows[producerContactIndex])).then(function(response) {
$scope.producerContactRows[producerContactIndex].producerContactId = response.data.producerContactId;
});
$scope.producerContactRows[producerContactIndex].producerContactIsNew = false;
} else {
producerContactSvc.updateProducerContact($scope.sanitizeProducerContact($scope.producerContactRows[producerContactIndex]));
}
$scope.producerContactRows[producerContactIndex].editable = false;
};
/**
* Prompt the user for confirmation they want to delete a producerContact.
* #param {number} producerContactIndex the index within the producerContactRows array of the producerContact to delete.
*/
$scope.deleteProducerContact = function(producerContactIndex, ev) {
var deleteProducerContact = $mdDialog.confirm()
.title('Are you sure you want to delete this ProducerContact?')
.ariaLabel('Delete ProducerContact')
.targetEvent(ev)
.ok('Delete ProducerContact')
.cancel('Cancel');
$mdDialog.show(deleteProducerContact).then(function() {
$scope.performDelete(producerContactIndex);
});
};
/**
* Delete a selected producerContact after verification.
* #param {number} producerContactIndex the index within the producerContactRows array of the producerContact to delete.
*/
$scope.performDelete = function(producerContactIndex) {
console.log($scope.producerContactRows[producerContactIndex]);
if (!$scope.producerContactRows[producerContactIndex].producerContactIsNew) {
producerContactSvc.deleteProducerContact($scope.producerContactRows[producerContactIndex].producerContactId, $scope.producerId);
}
$scope.producerContactRows.splice(producerContactIndex, 1);
};
/**
* Save the producer information to the backend
*/
$scope.saveProducer = function() {
var docObjects = [];
var timestamp = new Date().getTime();
var user = null;
if ($rootScope.apmHeader) {
user = $rootScope.apmHeader.userI;
}
_.forEach($scope.selectedDocs, function(doc) {
var docObject = {
documentid: doc.documentId,
producerId: $scope.producerId,
approvedby: user,
dateapproved: timestamp
};
docObjects.push(docObject);
});
$scope.producer.documentList = docObjects;
producerSvc.saveProducerInfo($scope.producer);
};
}]);
Test Class
describe('Test Producer Contact', function() {
//Add an initialize here:
var $scope;
var $rootScope;
var controller;
// deferred variables
var getProducerDefer;
var getProducerContactsDefer;
var addProducerContactDefer;
var updateProducerContactDefer;
var deleteProducerContactDefer;
var producerContactSvc;
var mockProducerContacts = [{
producerContactId: '11111',
producerId: '1111',
producerContactName: 'abcd',
producerContactEmail: 'abcd#abc.com',
producerContactType: 'cc',
editable: false,
invalid: false,
producerContactIsNew: false
},
{
producerContactId: '11112',
producerId: '1111',
producerContactName: 'efgh',
producerContactEmail: 'efgh#xyc.com',
producerContactType: 'primary',
editable: false,
invalid: false,
producerContactIsNew: false
},
{
producerContactId: '12345',
producerId: '1111',
producerContactName: 'asdh',
producerContactEmail: 'asdh#xyc.com',
producerContactType: 'cc',
editable: false,
invalid: false,
producerContactIsNew: false
}
];
beforeEach(module('FirstSaleApp'));
beforeEach(inject(function($controller, producerSvc, producerContactSvc, _$q_, _$rootScope_) {
$scope = _$rootScope_.$new();
$rootScope = _$rootScope_;
producerContactSvc.deleteProducerContact = function() {
return null;
};
// We use the $q service to create a mock instance of defer
getProducerDefer = _$q_.defer();
getProducerContactsDefer = _$q_.defer();
addProducerContactDefer = _$q_.defer();
updateProducerContactDefer = _$q_.defer();
deleteProducerContactDefer = _$q_.defer();
// Use a Jasmine Spy to return the deferred promise
spyOn(producerSvc, 'getAllProducers').and.returnValue(getProducerDefer.promise);
spyOn(producerContactSvc, 'getProducerContacts').and.returnValue(getProducerContactsDefer.promise);
spyOn(producerContactSvc, 'addProducerContact').and.returnValue(addProducerContactDefer.promise);
spyOn(producerContactSvc, 'updateProducerContact').and.returnValue(updateProducerContactDefer.promise);
spyOn(producerContactSvc, 'deleteProducerContact').and.returnValue(deleteProducerContactDefer.promise);
$controller('producerAdminCtrl', {
$scope: $scope,
producerSvc: producerSvc,
producerContactSvc: producerContactSvc
});
// getProducerDefer.resolve(mockProducers);
getProducerContactsDefer.resolve(mockProducerContacts);
// $scope.$digest();
$rootScope.$digest();
}));
it('controller exists', function() {
expect(controller).not.toBeNull();
});
it('should get the producerContacts from the producerContact service', function() {
// $scope.producerContactRows = $scope.getProducerContacts(1111);
// The length should match
expect($scope.producerContactRows.length).toBe(3); // <-- coming as 0
// The values should be the same too
// Mock 1
expect($scope.producerContactRows[0].producerContactName).toBe(mockProducerContacts[0].producerContactName);
expect($scope.producerContactRows[0].producerContactEmail).toBe(mockProducerContacts[0].producerContactEmail);
expect($scope.producerContactRows[0].producerContactType).toBe(mockProducerContacts[0].producerContactType);
expect($scope.producerContactRows[0].editable).toBeFalsy();
expect($scope.producerContactRows[0].producerContactIsNew).toBeFalsy();
// Mock 2
expect($scope.producerContactRows[1].producerContactName).toBe(mockProducerContacts[1].producerContactName);
expect($scope.producerContactRows[1].producerContactEmail).toBe(mockProducerContacts[1].producerContactEmail);
expect($scope.producerContactRows[1].producerContactType).toBe(mockProducerContacts[1].producerContactType);
expect($scope.producerContactRows[1].editable).toBeFalsy();
expect($scope.producerContactRows[1].producerContactIsNew).toBeFalsy();
// Mock 3
expect($scope.producerContactRows[2].producerContactName).toBe(mockProducerContacts[2].producerContactName);
expect($scope.producerContactRows[2].producerContactEmail).toBe(mockProducerContacts[2].producerContactEmail);
expect($scope.producerContactRows[2].producerContactType).toBe(mockProducerContacts[2].producerContactType);
expect($scope.producerContactRows[2].editable).toBeFalsy();
expect($scope.producerContactRows[2].producerContactIsNew).toBeFalsy();
});
});
I'm new to angularjs - I have started working on an app that uses karma and jasmine to test. I am trying to use deferred promise to stub a method call. However I don't see the data after I digest (or apply) the scope. Any help is appreciated on what I might be missing. Thanks
I have the following code with the JSFiddle for the same here:
var result = [
{"Id": 10004, "PageName": "club"},
{"Id": 10040, "PageName": "qaz"},
{"Id": 10059, "PageName": "jjjjjjj"}
];
$.each(result, function(i, item) {
alert(result[i].PageName);
});
In order to see all the results, I have to click Ok in alert window two times. How can I display the contents using one alert only?
//f1.js ---
var PatientReviewPage = (function () {
var cls = function () { }
var self = cls.prototype;
self.urlKey = "showmrn";
self.getData = function (mrn_) {
/*
if (isEmpty(mrn_)) { alert("Invalid mrn in getData()"); return false; }
// Lookup the AJAX web service URL
var url = regman.getWebServiceURL(self.urlKey);
if (isEmpty(url)) { alert("Invalid URL in getData()"); return false; }
var ajaxRequest = jQuery.ajax({
//beforeSend: TODO: show spinner!
data: {
registry_id: regman.registry.id,
mrn: mrn_
},
dataType: "json",
method: "GET",
url: url
})*/
//Some code related to ajax reques
this.done(function (data_, textStatus_, jqXHR_) {
// Validate the web service and retrieve the status.
if (typeof (data_) === "undefined" || data_ === null) {
alert("Invalid data returned from web service");
return false;
}
if (isEmpty(data_.webservice_status) || isEmpty(data_.webservice_status.status)) {
alert("Invalid web service status");
return false;
}
if (data_.webservice_status.status != "SUCCESS") {
alert(data_.webservice_status.message);
return false;
}
self.processdataDocuments(data_.data_document_list);
});
this.fail(function (jqXHR_, textStatus_, errorThrown_) {
alert("Error in getData(): " + errorThrown_);
return false;
});
};
// Initialize the page
self.initialize = function () {
var mrn = regman.selectedData["mrn"];
if (isEmpty(mrn)) {
alert("Invalid MRN provided by calling page");
return false;
}
self.getData(mrn);
};
self.processdataDocuments = function (collection_) {
if (isEmpty(collection_) || collection_.length < 1) {
// Populate the count and exit
jQuery("#nlpDocumentCount").html("(0)");
return true;
}
var source =
{
localdata: collection_,
datatype: "array"
};
var dataAdapter = new $.jqx.dataAdapter(source, {
loadComplete: function (data) { },
loadError: function (xhr, status, error) { }
});
$("#nlpDocumentPanel").jqxGrid(
{
source: dataAdapter,
width: '1000',
height: 150,
columns: [
{
text: 'Type', datafield: 'nc_type'
},
{
text: 'SubType', datafield: 'nc_subtype'
},
{
text: 'Concept', datafield: 'concept_text'
},
{
text: 'Date', datafield: 'nc_dos'
}
]
});
// For getting the contents of a row, I am using jqxgrid approach as mentioned in their doc here :
// http://www.jqwidgets.com/getting-the-clicked-grid-row/
$("#nlpDocumentPanel").on('rowclick', function (event) {
var row = event.args.rowindex;
var datarow = $("#nlpDocumentPanel").jqxGrid('getrowdata', row);
response = JSON.stringify(datarow, null, 10)
//alert(jsonStringify); // This alert displays the JSON data in a formatted manner
var obj = jQuery.parseJSON(response);
//alert("display Subtype "+obj.nc_subtype) // Works fine
self.mySubtype = obj.nc_subtype;
});
};
//I added this line for the demo to show 'f2' accessing this property from 'f1'. You should remove it if copying this code into your application
self.mySubtype = "Foo";
return cls;
})();
var f1 = new PatientReviewPage();
//f2.js ---
var DocumentDetailsDialog = (function () {
var cls = function () { }
var self = cls.prototype;
alert(f1.mySubtype);
/*this.getData = function (IDNumber_) {
// some code will be here
var ajaxRequest = jQuery.ajax({
// some code will be here
})
.done(function (data_, textStatus_, jqXHR_) {
// some code will be here
})
.fail(function (jqXHR_, textStatus_, errorThrown_) {
// some code will be here
});
};*/
return cls;
})();
var f2 = new DocumentDetailsDialog();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
You can use map() and join() and return string with page names.
var result = [
{"Id": 10004, "PageName": "club"},
{"Id": 10040, "PageName": "qaz"},
{"Id": 10059, "PageName": "jjjjjjj"}
];
var r = result.map(function(e) {
return e.PageName;
}).join(' ');
alert(r);
Build a string with the results and alert the string:
alert(result.map( _ => _.PageName).join(', '))
I want to return data from a Service function and inject it into a controller via the routeProvider, but only once the data has been fully loaded.
app.js:
.when('/sources', {
templateUrl: 'views/app/source/inventory.html',
controller: 'InventoryCtrl',
pageTitle: 'Data Inventory',
resolve: {
orders: function (OrderService) {
return OrderService.getOrders();
},
sources: function (SourceService) {
return SourceService.getSources();
}
}
})
SourceService:
angular.module('wp.source')
.factory('SourceService', function ($http, $q) {
return {
getSources: function () {
var model = {
sources: []
};
var promises = [];
return $http.get('datasets/project.json')
.then(function (data) {
if(localStorage['files'] != null) {
var files = JSON.parse(localStorage['files']);
for (var i = 0; i < files.length; i++) {
model.sources.push(files[i]);
}
}
var sources = [];
for (var sourceId in data.data.sources) {
var source = data.data.sources[sourceId];
source.id = sourceId;
sources.push(source);
}
angular.forEach(sources, function (source) {
promises.push($http.get('datasets/' + source.filename).then(function (datasource, resolve) {
var metadata = datasource.data.metadata;
var source = {
name: metadata.name,
address: metadata.url,
frequency: metadata.refresh,
type: metadata.type,
tags: metadata.tags,
size: metadata.size,
active: source.active,
sourceID: source.id,
sourceFile: source.filename
};
return source;
}));
});
//var finalPromise = sources.reduce(function (promise, source, index, array) {
// return promise.then(function (a) {
// var query = $http.get('datasets/' + source.filename)
// .then(function (datasource) {
// var metadata = datasource.data.metadata;
// model.sources.push({
// name: metadata.name,
// address: metadata.url,
// frequency: metadata.refresh,
// type: metadata.type,
// tags: metadata.tags,
// size: metadata.size,
// active: source.active,
// sourceID: source.id,
// sourceFile: source.filename
// });
// });
// });
//}, $q.when([]));
//
//finalPromise.finally(function () {
// return model;
//})
$q.all(function () {
return model;
});
});
}
}
});
Controller:
angular.module('wp.source')
.controller('InventoryCtrl', function ($scope, orders, sources) {
$scope.orders = orders.data;
$scope.gridView = true;
$scope.rowView = false;
$scope.allSources = sources.sources;
$scope.shownSources = $scope.allSources;
...
Currently I have the problem that the value is injected before "model" is fully loaded. I tried to pack the subqueries into a $q.all promise, but I don't know exactly where to go from there
Any help would be appreciated
You want to use $q.defer() and returned the deferred promise object.
You code will look something like this.
angular.module('wp.source')
.factory('SourceService', function ($http, $q) {
return {
getSources: function () {
var model = {
sources: []
};
var deffered = $q.defer();
var promises = [];
return $http.get('datasets/project.json')
.then(function (data) {
if(localStorage['files'] != null) {
var files = JSON.parse(localStorage['files']);
for (var i = 0; i < files.length; i++) {
model.sources.push(files[i]);
}
}
var sources = [];
for (var sourceId in data.data.sources) {
var source = data.data.sources[sourceId];
source.id = sourceId;
sources.push(source);
}
angular.forEach(sources, function (source) {
promises.push($http.get('datasets/' + source.filename).then(function (datasource, resolve) {
var metadata = datasource.data.metadata;
var source = {
name: metadata.name,
address: metadata.url,
frequency: metadata.refresh,
type: metadata.type,
tags: metadata.tags,
size: metadata.size,
active: source.active,
sourceID: source.id,
sourceFile: source.filename
};
return source;
}));
});
//var finalPromise = sources.reduce(function (promise, source, index, array) {
// return promise.then(function (a) {
// var query = $http.get('datasets/' + source.filename)
// .then(function (datasource) {
// var metadata = datasource.data.metadata;
// model.sources.push({
// name: metadata.name,
// address: metadata.url,
// frequency: metadata.refresh,
// type: metadata.type,
// tags: metadata.tags,
// size: metadata.size,
// active: source.active,
// sourceID: source.id,
// sourceFile: source.filename
// });
// });
// });
//}, $q.when([]));
//
//finalPromise.finally(function () {
// return model;
//})
$q.all(promises).then(function () {
deferred.resolve(model);
});
});
}
return deferred.promise();
}
});
EDIT As others have mentioned you need to pass your array of promises into $q.all to get this to work right. Also it's worth noting that $q.all will return an array with the results of each promise in the order they were in the promise array.
Here's a plunker demoing the use of defer.
There's more $q.defer and $q.all here in the Angular's Docs.
You will need to use $q.all with array of promises and return it from outer promise:
angular.module('wp.source')
.factory('SourceService', function($http, $q) {
return {
getSources: function() {
var model = {
sources: []
};
return $http.get('datasets/project.json').then(function(data) {
if (localStorage['files'] != null) {
var files = JSON.parse(localStorage['files']);
for (var i = 0; i < files.length; i++) {
model.sources.push(files[i]);
}
}
var sources = [],
promises = [];
for (var sourceId in data.data.sources) {
var source = data.data.sources[sourceId];
source.id = sourceId;
sources.push(source);
}
var promises = sources.map(function(source) {
return $http.get('datasets/' + source.filename).then(function(datasource, resolve) {
var metadata = datasource.data.metadata;
model.sources.push({
name: metadata.name,
address: metadata.url,
frequency: metadata.refresh,
type: metadata.type,
tags: metadata.tags,
size: metadata.size,
active: source.active,
sourceID: source.id,
sourceFile: source.filename
});
});
});
return $q.all(promises).then(function() {
return model;
});
});
}
}
});
You are not using $q.all correctly. The simplest possible fix to your code would be changing
$q.all(function () {
return model;
});
to
$q.all(promises).then(function() {
return model;
});
You're basically not "telling" $q.all which promises it needs to aggregate. Please refer to the documentation for $q and check how all works.
I am having trouble with a directive. My model updates with data that is being loaded by a http call. Without the call the service the directive works correctly.
I have a directive called validationmessagefor that listens to when a field has been marked to have an error. When it has an error it displays the error message.
The plunker attached Plnkr
How do I get the directive to respond or load correctly against the model when using services to load data?
angular.module('dataApp', ['servicesModule', 'directivesModule'])
.controller('dataCtrl', ['$scope', 'ProcessService', 'ValidationRuleFactory', 'Validator',
function($scope, ValidationRuleFactory, Validator, ProcessService) {
$scope.viewModel = {};
var FormFields = {};
// we would get this from the meta api
ProcessService.getProcessMetaData().then(function(data) {
FormFields = {
Name: "Course",
Fields: [{
type: "text",
Name: "name",
label: "Name",
data: "",
required: true,
ruleSet: "personFirstNameRules"
}, {
type: "text",
Name: "description",
label: "Description",
data: "",
required: true,
ruleSet: "personEmailRules"
}]
};
$scope.viewModel = FormFields;
ProcessService.getProcessRuleData().then(function(data) {
var genericErrorMessages = {
required: 'Required',
minlength: 'value length must be at least %s characters',
maxlength: 'value length must be less than %s characters'
};
var rules = new ValidationRuleFactory(genericErrorMessages);
$scope.viewModel.validationRules = {
personFirstNameRules: [rules.isRequired(), rules.minLength(3)],
personEmailRules: [rules.isRequired(), rules.minLength(3), rules.maxLength(7)]
};
$scope.viewModel.validator = new Validator($scope.viewModel.validationRules);
});
});
var getRuleSetValuesMap = function() {
return {
personFirstNameRules: $scope.viewModel.Fields[0].data,
personEmailRules: $scope.viewModel.Fields[1].data
};
};
$scope.save = function() {
$scope.viewModel.validator.validateAllRules(getRuleSetValuesMap());
if ($scope.viewModel.validator.hasErrors()) {
$scope.viewModel.validator.triggerValidationChanged();
return;
} else {
alert('person saved in!');
}
};
}
]);
The directive to display the custom error message
(function(angular, $) {
angular.module('directivesModule')
.directive('validationMessageFor', [function() {
return {
restrict: 'A',
scope: {eID: '#val'},
link: function(scope, element, attributes) {
//var errorElementId = attributes.validationMessageFor;
attributes.$observe('validationMessageFor', function(value) {
errorElementId = value;
if (!errorElementId) {
return;
}
var areCustomErrorsWatched = false;
var watchRuleChange = function(validationInfo, rule) {
scope.$watch(function() {
return validationInfo.validator.ruleSetHasErrors(validationInfo.ruleSetName, rule.errorCode);
}, showErrorInfoIfNeeded);
};
var watchCustomErrors = function(validationInfo) {
if (!areCustomErrorsWatched && validationInfo && validationInfo.validator) {
areCustomErrorsWatched = true;
var validator = validationInfo.validator;
var rules = validator.validationRules[validationInfo.ruleSetName];
for (var i = 0; i < rules.length; i++) {
watchRuleChange(validationInfo, rules[i]);
}
}
};
// get element for which we are showing error information by id
var errorElement = $("#" + errorElementId);
var errorElementController = angular.element(errorElement).controller('ngModel');
var validatorsController = angular.element(errorElement).controller('validator');
var getValidationInfo = function() {
return validatorsController && validatorsController.validationInfoIsDefined() ? validatorsController.validationInfo : null;
};
var validationChanged = false;
var subscribeToValidationChanged = function() {
if (validatorsController.validationInfoIsDefined()) {
validatorsController.validationInfo.validator.watchValidationChanged(function() {
validationChanged = true;
showErrorInfoIfNeeded();
});
// setup a watch on rule errors if it's not already set
watchCustomErrors(validatorsController.validationInfo);
}
};
var getErrorMessage = function(value) {
var validationInfo = getValidationInfo();
if (!validationInfo) {
return '';
}
var errorMessage = "";
var errors = validationInfo.validator.errors[validationInfo.ruleSetName];
var rules = validationInfo.validator.validationRules[validationInfo.ruleSetName];
for (var errorCode in errors) {
if (errors[errorCode]) {
var errorCodeRule = _.findWhere(rules, {errorCode: errorCode});
if (errorCodeRule) {
errorMessage += errorCodeRule.validate(value).errorMessage;
break;
}
}
}
return errorMessage;
};
var showErrorInfoIfNeeded = function() {
var validationInfo = getValidationInfo();
if (!validationInfo) {
return;
}
var needsAttention = validatorsController.ruleSetHasErrors() && (errorElementController && errorElementController.$dirty || validationChanged);
if (needsAttention) {
// compose and show error message
var errorMessage = getErrorMessage(element.val());
// set and show error message
element.text(errorMessage);
element.show();
} else {
element.hide();
}
};
subscribeToValidationChanged();
if (errorElementController)
{
scope.$watch(function() {
return errorElementController.$dirty;
}, showErrorInfoIfNeeded);
}
scope.$watch(function() {
return validatorsController.validationInfoIsDefined();
}, subscribeToValidationChanged());
});
}
};
}]);
})(angular, $);
I'm trying to create a button class that extends an AbstractComponent class using $.extend() but the functions in AbstractComponent aren't available when I'm constructing the button.
The specific error I'm receiving is:
Uncaught TypeError: Object [object Object] has no method 'setOptions'
var Button = {};
var abstract = new AbstractComponent;
$.extend(Button,abstract);
//debugger;
//this.setOptions is available here
Button = function(options) {
'use strict';
var defaultOptions = {
templateName: '#button-tmpl',
title: "Label goes here",
type: "primary",
size: "medium",
disabled: null,
autosave: null,
href: null,
onclick: null
};
//debugger
//this.setOptions is not available here
this.setOptions(options, defaultOptions);
this.checkRequiredKeys('title');
return this;
};
Button.prototype.updateOptions = function() {
var options = this.options;
if (options.href === null) {
options.href = 'javascript:;';
}
if (options.disabled === null) {
options.disabled = 'disabled';
}
if (options.autosave === true) {
options.autosave = 'ping-autosave';
}
};
AbstractComponent.js
var AbstractComponent = function() {
console.log('this will be the constructor for elements extending this class');
};
AbstractComponent.prototype.show = function() {
this.render();
};
AbstractComponent.prototype.close = function() {
// stop listeners and remove this component
this.stopListening();
this.remove();
};
AbstractComponent.prototype.getTemplateName = function() {
return this.options.templateName;
};
AbstractComponent.prototype.checkRequiredKeys = function() {
var errors = new Array();
if (typeof this.getTemplateName() === "undefined") {
errors.push('templateName');
}
for (var i = 0; i < arguments.length; i++) {
if (!this.options.hasOwnProperty(arguments[i])) {
errors.push(arguments[i]);
}
}
if (errors.length > 0) {
throw new Exception("Required property(s) not found:" + errors.join(', ') + " in " + this.toString());
}
};
AbstractComponent.prototype.getElement = function() {
'use strict';
if(!this.options.updated) {
this.updateOptions();
}
return new AbstractView(this.options).render().$el;
};
AbstractComponent.prototype.updateOptions = function() {
this.options.updated = true;
return true;
};
AbstractComponent.prototype.getHtml = function() {
return this.getElement().html();
};
AbstractComponent.prototype.setOptions = function(options, defaultOptions) {
this.options = _.defaults(options, defaultOptions);
};
AbstractComponent.prototype.toString = function() {
return "Component" + this.getTemplateName() + "[id=" + this.options.id + "]";
};
jQuery extend is for moving properties from one (or more) object(s) to another object.
$.extend({}, {
foo: 10,
bar: 20
});
You should use prototypal inheritance isntead
function Button(options) {
'use strict';
var defaultOptions = {
templateName: '#button-tmpl',
title: "Label goes here",
type: "primary",
size: "medium",
disabled: null,
autosave: null,
href: null,
onclick: null
};
//debugger
//this.setOptions is not available here
this.setOptions(options, defaultOptions);
this.checkRequiredKeys('title');
return this;
};
Button.prototype = new AbstractComponent;