Why callback function doesn't work in $scope? Angularjs - angularjs

Why function getPrice('Table') doesn't work in controller? It return undefined instead 400. That quite works in plain JS (without $scope).
function MyCtrl($scope) {
$scope.prices = [{
name: 'Bed',
price: 900
}, {
name: 'Table',
price: 400
}];
$scope.getPrice = function (name) {
$scope.prices.forEach(
function (el) {
if (el.name == name) {
return el.price;
} else {
return null;
}
}
);
}
};

Your getPrice function does not return anything. Try this one :
$scope.getPrice = function (name) {
$price = null;
$scope.prices.forEach(
function (el) {
if (el.name == name) {
$price = el.price;
return;
} else {
return;
}
}
);
return $price;
}

Related

how to call two apis in one typeahead selection

I am using the angular typeahead:-
<label>USER</label>
<input type="text" name="user" ng-model="a.user" autocomplete="off" typeahead="a for a in getAllUsers($viewValue);getAllStaffs($viewValue)" typeahead-loading="loadingCodes" typeahead-no-results="noResults">
My directive Code:-
scope.getAllUsers = function(key) {
var obj = {
"key": key
}
if (key.length >= 2) {
return ApiServices.getAllUsers(obj).then(function(response) {
return response.data.map(function(item) {
return item;
});
});
} else {
return false;
}
};
scope.getAllStaffs = function(key) {
var obj = {
"key": key
}
if (key.length >= 2) {
return ApiServices.getAllStaffs(obj).then(function(response) {
return response.data.map(function(item) {
return item;
});
});
} else {
return false;
}
};
There are two functions:- One is used for fetching users name and one is used for fetching staffs name. I want both this functions to call on the same input.
But the typeahead is getting list of staff members only. Is there any way to get both list populated in the same input.
Define a new function that does both requests and merges the results.
scope.getAllNames = function(key) {
var obj = {
"key": key
}
function extract(resp) {
return resp.data.slice(0)
}
if (key.length >= 2) {
return Promise.all([
ApiServices.getAllUsers(obj).then(extract),
ApiServices.getAllStaffs(obj).then(extract)
])
.then(function(results) {
return [].concat.apply([], results)
});
} else {
return false;
}
}

unable to open hyperlink in a new tab which is created from bootstrap-wysiwyg editor

i am using bootstrap-wysiwyg rich text editor in my application and i am not able to open the hyerlink created in a new window.
i am using the bootstrap-wysiwyg.js file which is below. i am not able to figure out how to make the hyperlink created, to open in a new tab.
(function ($) {
'use strict';
/** underscoreThrottle()
* From underscore http://underscorejs.org/docs/underscore.html
*/
var underscoreThrottle = function(func, wait) {
var context, args, timeout, result;
var previous = 0;
var later = function() {
previous = new Date;
timeout = null;
result = func.apply(context, args);
};
return function() {
var now = new Date;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(context, args);
} else if (!timeout) {
timeout = setTimeout(later, remaining);
}
return result;
};
}
var readFileIntoDataUrl = function (fileInfo) {
var loader = $.Deferred(),
fReader = new FileReader();
fReader.onload = function (e) {
loader.resolve(e.target.result);
};
fReader.onerror = loader.reject;
fReader.onprogress = loader.notify;
fReader.readAsDataURL(fileInfo);
return loader.promise();
};
$.fn.cleanHtml = function (o) {
if ( $(this).data("wysiwyg-html-mode") === true ) {
$(this).html($(this).text());
$(this).attr('contenteditable',true);
$(this).data('wysiwyg-html-mode',false);
}
// Strip the images with src="data:image/.." out;
if ( o === true && $(this).parent().is("form") ) {
var gGal = $(this).html;
if ( $(gGal).has( "img" ).length ) {
var gImages = $( "img", $(gGal));
var gResults = [];
var gEditor = $(this).parent();
$.each(gImages, function(i,v) {
if ( $(v).attr('src').match(/^data:image\/.*$/) ) {
gResults.push(gImages[i]);
$(gEditor).prepend("<input value='"+$(v).attr('src')+"' type='hidden' name='postedimage/"+i+"' />");
$(v).attr('src', 'postedimage/'+i);
}});
}
}
var html = $(this).html();
return html && html.replace(/(<br>|\s|<div><br><\/div>| )*$/, '');
};
$.fn.wysiwyg = function (userOptions) {
var editor = this,
wrapper = $(editor).parent(),
selectedRange,
options,
toolbarBtnSelector,
updateToolbar = function () {
if (options.activeToolbarClass) {
$(options.toolbarSelector,wrapper).find(toolbarBtnSelector).each(underscoreThrottle(function () {
var commandArr = $(this).data(options.commandRole).split(' '),
command = commandArr[0];
// If the command has an argument and its value matches this button. == used for string/number comparison
if (commandArr.length > 1 && document.queryCommandEnabled(command) && document.queryCommandValue(command) == commandArr[1]) {
$(this).addClass(options.activeToolbarClass);
// Else if the command has no arguments and it is active
} else if (commandArr.length === 1 && document.queryCommandEnabled(command) && document.queryCommandState(command)) {
$(this).addClass(options.activeToolbarClass);
// Else the command is not active
} else {
$(this).removeClass(options.activeToolbarClass);
}
}, options.keypressTimeout));
}
},
execCommand = function (commandWithArgs, valueArg) {
var commandArr = commandWithArgs.split(' '),
command = commandArr.shift(),
args = commandArr.join(' ') + (valueArg || '');
var parts = commandWithArgs.split('-');
if ( parts.length == 1 ) {
document.execCommand(command, 0, args);
}
else if ( parts[0] == 'format' && parts.length == 2) {
document.execCommand('formatBlock', false, parts[1] );
}
editor.trigger('change');
updateToolbar();
},
bindHotkeys = function (hotKeys) {
$.each(hotKeys, function (hotkey, command) {
editor.keydown(hotkey, function (e) {
if (editor.attr('contenteditable') && editor.is(':visible')) {
e.preventDefault();
e.stopPropagation();
execCommand(command);
}
}).keyup(hotkey, function (e) {
if (editor.attr('contenteditable') && editor.is(':visible')) {
e.preventDefault();
e.stopPropagation();
}
});
});
editor.keyup(function(){ editor.trigger('change'); });
},
getCurrentRange = function () {
var sel, range;
if (window.getSelection) {
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
}
} else if (document.selection) {
range = document.selection.createRange();
} return range;
},
saveSelection = function () {
selectedRange = getCurrentRange();
},
restoreSelection = function () {
var selection;
if (window.getSelection || document.createRange) {
selection = window.getSelection();
if (selectedRange) {
try {
selection.removeAllRanges();
} catch (ex) {
document.body.createTextRange().select();
document.selection.empty();
}
selection.addRange(selectedRange);
}
}
else if (document.selection && selectedRange) {
selectedRange.select()
}
},
// Adding Toggle HTML based on the work by #jd0000, but cleaned up a little to work in this context.
toggleHtmlEdit = function(a) {
if ( $(editor).data("wysiwyg-html-mode") !== true ) {
var oContent = $(editor).html();
var editorPre = $( "<pre />" )
$(editorPre).append( document.createTextNode( oContent ) );
$(editorPre).attr('contenteditable',true);
$(editor).html(' ');
$(editor).append($(editorPre));
$(editor).attr('contenteditable', false);
$(editor).data("wysiwyg-html-mode", true);
$(editorPre).focus();
}
else {
$(editor).html($(editor).text());
$(editor).attr('contenteditable',true);
$(editor).data('wysiwyg-html-mode',false);
$(editor).focus();
}
},
insertFiles = function (files) {
editor.focus();
$.each(files, function (idx, fileInfo) {
if (/^image\//.test(fileInfo.type)) {
$.when(readFileIntoDataUrl(fileInfo)).done(function (dataUrl) {
execCommand('insertimage', dataUrl);
editor.trigger('image-inserted');
}).fail(function (e) {
options.fileUploadError("file-reader", e);
});
} else {
options.fileUploadError("unsupported-file-type", fileInfo.type);
}
});
},
markSelection = function (input, color) {
restoreSelection();
if (document.queryCommandSupported('hiliteColor')) {
document.execCommand('hiliteColor', 0, color || 'transparent');
}
saveSelection();
input.data(options.selectionMarker, color);
},
bindToolbar = function (toolbar, options) {
toolbar.find(toolbarBtnSelector, wrapper).click(function () {
restoreSelection();
editor.focus();
if ($(this).data(options.commandRole) === 'html') {
toggleHtmlEdit();
}
else {
execCommand($(this).data(options.commandRole));
}
saveSelection();
});
toolbar.find('[data-toggle=dropdown]').click(restoreSelection);
toolbar.find('input[type=text][data-' + options.commandRole + ']').on('webkitspeechchange change', function () {
var newValue = this.value; /* ugly but prevents fake double-calls due to selection restoration */
this.value = '';
restoreSelection();
if (newValue) {
editor.focus();
execCommand($(this).data(options.commandRole), newValue);
}
saveSelection();
}).on('focus', function () {
var input = $(this);
if (!input.data(options.selectionMarker)) {
markSelection(input, options.selectionColor);
input.focus();
}
}).on('blur', function () {
var input = $(this);
if (input.data(options.selectionMarker)) {
markSelection(input, false);
}
});
toolbar.find('input[type=file][data-' + options.commandRole + ']').change(function () {
restoreSelection();
if (this.type === 'file' && this.files && this.files.length > 0) {
insertFiles(this.files);
}
saveSelection();
this.value = '';
});
},
initFileDrops = function () {
editor.on('dragenter dragover', false)
.on('drop', function (e) {
var dataTransfer = e.originalEvent.dataTransfer;
e.stopPropagation();
e.preventDefault();
if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) {
insertFiles(dataTransfer.files);
}
});
};
options = $.extend(true, {}, $.fn.wysiwyg.defaults, $.fn.wysiwyg.defaults1, userOptions);
toolbarBtnSelector = 'a[data-' + options.commandRole + '],button[data-' + options.commandRole + '],input[type=button][data-' + options.commandRole + ']';
bindHotkeys(options.hotKeys);
// Support placeholder attribute on the DIV
if ($(this).attr('placeholder') != '') {
$(this).addClass('placeholderText');
$(this).html($(this).attr('placeholder'));
$(this).bind('focus',function(e) {
if ( $(this).attr('placeholder') != '' && $(this).text() == $(this).attr('placeholder') ) {
$(this).removeClass('placeholderText');
$(this).html('');
}
});
$(this).bind('blur',function(e) {
if ( $(this).attr('placeholder') != '' && $(this).text() == '' ) {
$(this).addClass('placeholderText');
$(this).html($(this).attr('placeholder'));
}
})
}
if (options.dragAndDropImages) {
initFileDrops();
}
bindToolbar($(options.toolbarSelector), options);
editor.attr('contenteditable', true)
.on('mouseup keyup mouseout', function () {
saveSelection();
updateToolbar();
});
$(window).bind('touchend', function (e) {
var isInside = (editor.is(e.target) || editor.has(e.target).length > 0),
currentRange = getCurrentRange(),
clear = currentRange && (currentRange.startContainer === currentRange.endContainer && currentRange.startOffset === currentRange.endOffset);
if (!clear || isInside) {
saveSelection();
updateToolbar();
}
});
return this;
};
$.fn.wysiwyg.defaults = {
hotKeys: {
'Ctrl+b meta+b': 'bold',
'Ctrl+i meta+i': 'italic',
'Ctrl+u meta+u': 'underline',
'Ctrl+z': 'undo',
'Ctrl+y meta+y meta+shift+z': 'redo',
'Ctrl+l meta+l': 'justifyleft',
'Ctrl+r meta+r': 'justifyright',
'Ctrl+e meta+e': 'justifycenter',
'Ctrl+j meta+j': 'justifyfull',
'Shift+tab': 'outdent',
'tab': 'indent'
},
toolbarSelector: '[data-role=editor-toolbar]',
commandRole: 'edit',
activeToolbarClass: 'btn-info',
selectionMarker: 'edit-focus-marker',
selectionColor: 'darkgrey',
dragAndDropImages: true,
keypressTimeout: 200,
fileUploadError: function (reason, detail) { console.log("File upload error", reason, detail); }
};
$.fn.wysiwyg.defaults1 = {
hotKeys: {
'Ctrl+b meta+b': 'bold',
'Ctrl+i meta+i': 'italic',
'Ctrl+u meta+u': 'underline',
'Ctrl+z': 'undo',
'Ctrl+y meta+y meta+shift+z': 'redo',
'Ctrl+l meta+l': 'justifyleft',
'Ctrl+r meta+r': 'justifyright',
'Ctrl+e meta+e': 'justifycenter',
'Ctrl+j meta+j': 'justifyfull',
'Shift+tab': 'outdent',
'tab': 'indent'
},
toolbarSelector: '[data-role=editor1-toolbar]',
commandRole: 'edit',
activeToolbarClass: 'btn-info',
selectionMarker: 'edit-focus-marker',
selectionColor: 'darkgrey',
dragAndDropImages: true,
keypressTimeout: 200,
fileUploadError: function (reason, detail) { console.log("File upload error", reason, detail); }
};
}(window.jQuery));
#
i am saving the editor contents and then i am retrieving it in a different page as below.
<p class="textAlignLeft" ng-bind-html="editorContent | unsafe"></p>
"editorContent" will have the contents entered in the richtext editor, and the hyper link in it has to open in a new window.
in the browser console i am getting the following output.
<p class="textAlignLeft ng-binding" ng-bind-html="editorContent | unsafe">ajslkjsak sdsad</p>
One way of doing that would be to bind to click event of anchor tag and open URL in new tab using JavaScript.
Say, the ID of your editor is "editor", the following code would work with it
$("a", "#editor").click(function(e) {
window.open($(this).attr('href'), '_blank')
});
this will bind click event on all the a tags inside editor div and when the user clicks any of it, the url will be opened in new window.
Update:
using jQuery, it is very easy. First assign an id to your <p> tag like
<p id="myCustomContent" class="textAlignLeft" ng-bind-html="newsContent | unsafe"></p>
Where myCustomContent is the id
now use the following code
$("a", "#myCustomContent").each(function() {
$(this).attr('target', '_blank');
});
this will loop once on all the anchor tags and make them open in new tab when user clicks on them.

How to wrap a JS script inside an Angular directive

I am learning AngularJS and I have a conventional JS Script that I wrote a while ago and I would like to use it inside my new Angular app.
Can I literally just dump the entire script in side the directive or do I need to change some things like the keyword this to element etc...?
directive.directive("skillLevel", ['$timeout', function($timeout) {
return{
link: function(scope, el, atts){
// CAN I PASTE MY SCRIPT HERE??
}
}]);
I have this 'quite length some' script that I want to use. How would I go about effectivly using this inside my directive?
(function ($) {
'use strict';
var RSS = function (target, url, options, callback) {
this.target = target;
this.url = url;
this.html = [];
this.effectQueue = [];
this.options = $.extend({
ssl: false,
host: 'www.feedrapp.info',
limit: null,
key: null,
layoutTemplate: '<ul>{entries}</ul>',
entryTemplate: '<li>[{author}#{date}] {title}<br/>{shortBodyPlain}</li>',
tokens: {},
outputMode: 'json',
dateFormat: 'dddd MMM Do',
dateLocale: 'en',
effect: 'show',
offsetStart: false,
offsetEnd: false,
error: function () {
console.log('jQuery RSS: url doesn\'t link to RSS-Feed');
},
onData: function () {},
success: function () {}
}, options || {});
// The current SSL certificate is only valid for *.herokuapp.com
if (this.options.ssl && (this.options.host === 'www.feedrapp.info')) {
this.options.host = 'feedrapp.herokuapp.com';
}
this.callback = callback || this.options.success;
};
RSS.htmlTags = [
'doctype', 'html', 'head', 'title', 'base', 'link', 'meta', 'style', 'script', 'noscript',
'body', 'article', 'nav', 'aside', 'section', 'header', 'footer', 'h1-h6', 'hgroup', 'address',
'p', 'hr', 'pre', 'blockquote', 'ol', 'ul', 'li', 'dl', 'dt', 'dd', 'figure', 'figcaption',
'div', 'table', 'caption', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td', 'col', 'colgroup',
'form', 'fieldset', 'legend', 'label', 'input', 'button', 'select', 'datalist', 'optgroup',
'option', 'textarea', 'keygen', 'output', 'progress', 'meter', 'details', 'summary', 'command',
'menu', 'del', 'ins', 'img', 'iframe', 'embed', 'object', 'param', 'video', 'audio', 'source',
'canvas', 'track', 'map', 'area', 'a', 'em', 'strong', 'i', 'b', 'u', 's', 'small', 'abbr', 'q',
'cite', 'dfn', 'sub', 'sup', 'time', 'code', 'kbd', 'samp', 'var', 'mark', 'bdi', 'bdo', 'ruby',
'rt', 'rp', 'span', 'br', 'wbr'
];
RSS.prototype.load = function (callback) {
var apiProtocol = 'http' + (this.options.ssl ? 's' : '');
var apiHost = apiProtocol + '://' + this.options.host;
var apiUrl = apiHost + '?callback=?&q=' + encodeURIComponent(this.url);
// set limit to offsetEnd if offset has been set
if (this.options.offsetStart && this.options.offsetEnd) {
this.options.limit = this.options.offsetEnd;
}
if (this.options.limit !== null) {
apiUrl += '&num=' + this.options.limit;
}
if (this.options.key !== null) {
apiUrl += '&key=' + this.options.key;
}
$.getJSON(apiUrl, callback);
};
RSS.prototype.render = function () {
var self = this;
this.load(function (data) {
try {
self.feed = data.responseData.feed;
self.entries = data.responseData.feed.entries;
} catch (e) {
self.entries = [];
self.feed = null;
return self.options.error.call(self);
}
var html = self.generateHTMLForEntries();
self.target.append(html.layout);
if (html.entries.length !== 0) {
if ($.isFunction(self.options.onData)) {
self.options.onData.call(self);
}
self.appendEntriesAndApplyEffects($('entries', html.layout), html.entries);
}
if (self.effectQueue.length > 0) {
self.executeEffectQueue(self.callback);
} else if ($.isFunction(self.callback)) {
self.callback.call(self);
}
});
};
RSS.prototype.appendEntriesAndApplyEffects = function (target, entries) {
var self = this;
$.each(entries, function (idx, entry) {
var $html = self.wrapContent(entry);
if (self.options.effect === 'show') {
target.before($html);
} else {
$html.css({ display: 'none' });
target.before($html);
self.applyEffect($html, self.options.effect);
}
});
target.remove();
};
RSS.prototype.generateHTMLForEntries = function () {
var self = this;
var result = { entries: [], layout: null };
$(this.entries).each(function () {
var entry = this;
var offsetStart = self.options.offsetStart;
var offsetEnd = self.options.offsetEnd;
var evaluatedString;
// offset required
if (offsetStart && offsetEnd) {
if (index >= offsetStart && index <= offsetEnd) {
if (self.isRelevant(entry, result.entries)) {
evaluatedString = self.evaluateStringForEntry(
self.options.entryTemplate, entry
);
result.entries.push(evaluatedString);
}
}
} else {
// no offset
if (self.isRelevant(entry, result.entries)) {
evaluatedString = self.evaluateStringForEntry(
self.options.entryTemplate, entry
);
result.entries.push(evaluatedString);
}
}
});
if (!!this.options.entryTemplate) {
// we have an entryTemplate
result.layout = this.wrapContent(
this.options.layoutTemplate.replace('{entries}', '<entries></entries>')
);
} else {
// no entryTemplate available
result.layout = this.wrapContent('<div><entries></entries></div>');
}
return result;
};
RSS.prototype.wrapContent = function (content) {
if ($.trim(content).indexOf('<') !== 0) {
// the content has no html => create a surrounding div
return $('<div>' + content + '</div>');
} else {
// the content has html => don't touch it
return $(content);
}
};
RSS.prototype.applyEffect = function ($element, effect, callback) {
var self = this;
switch (effect) {
case 'slide':
$element.slideDown('slow', callback);
break;
case 'slideFast':
$element.slideDown(callback);
break;
case 'slideSynced':
self.effectQueue.push({ element: $element, effect: 'slide' });
break;
case 'slideFastSynced':
self.effectQueue.push({ element: $element, effect: 'slideFast' });
break;
}
};
RSS.prototype.executeEffectQueue = function (callback) {
var self = this;
this.effectQueue.reverse();
var executeEffectQueueItem = function () {
var item = self.effectQueue.pop();
if (item) {
self.applyEffect(item.element, item.effect, executeEffectQueueItem);
} else if (callback) {
callback();
}
};
executeEffectQueueItem();
};
RSS.prototype.evaluateStringForEntry = function (string, entry) {
var result = string;
var self = this;
$(string.match(/(\{.*?\})/g)).each(function () {
var token = this.toString();
result = result.replace(token, self.getValueForToken(token, entry));
});
return result;
};
RSS.prototype.isRelevant = function (entry, entries) {
var tokenMap = this.getTokenMap(entry);
if (this.options.filter) {
if (this.options.filterLimit && (this.options.filterLimit === entries.length)) {
return false;
} else {
return this.options.filter(entry, tokenMap);
}
} else {
return true;
}
};
RSS.prototype.getFormattedDate = function (dateString) {
// If a custom formatting function is provided, use that.
if (this.options.dateFormatFunction) {
return this.options.dateFormatFunction(dateString);
} else if (typeof moment !== 'undefined') {
// If moment.js is available and dateFormatFunction is not overriding it,
// use it to format the date.
var date = moment(new Date(dateString));
if (date.locale) {
date = date.locale(this.options.dateLocale);
} else {
date = date.lang(this.options.dateLocale);
}
return date.format(this.options.dateFormat);
} else {
// If all else fails, just use the date as-is.
return dateString;
}
};
RSS.prototype.getTokenMap = function (entry) {
if (!this.feedTokens) {
var feed = JSON.parse(JSON.stringify(this.feed));
delete feed.entries;
this.feedTokens = feed;
}
return $.extend({
feed: this.feedTokens,
url: entry.link,
author: entry.author,
date: this.getFormattedDate(entry.publishedDate),
title: entry.title,
body: entry.content,
shortBody: entry.contentSnippet,
bodyPlain: (function (entry) {
var result = entry.content
.replace(/<script[\\r\\\s\S]*<\/script>/mgi, '')
.replace(/<\/?[^>]+>/gi, '');
for (var i = 0; i < RSS.htmlTags.length; i++) {
result = result.replace(new RegExp('<' + RSS.htmlTags[i], 'gi'), '');
}
return result;
})(entry),
shortBodyPlain: entry.contentSnippet.replace(/<\/?[^>]+>/gi, ''),
index: $.inArray(entry, this.entries),
totalEntries: this.entries.length,
teaserImage: (function (entry) {
try {
return entry.content.match(/(<img.*?>)/gi)[0];
}
catch (e) {
return '';
}
})(entry),
teaserImageUrl: (function (entry) {
try {
return entry.content.match(/(<img.*?>)/gi)[0].match(/src="(.*?)"/)[1];
}
catch (e) {
return '';
}
})(entry)
}, this.options.tokens);
};
RSS.prototype.getValueForToken = function (_token, entry) {
var tokenMap = this.getTokenMap(entry);
var token = _token.replace(/[\{\}]/g, '');
var result = tokenMap[token];
if (typeof result !== 'undefined') {
return ((typeof result === 'function') ? result(entry, tokenMap) : result);
} else {
throw new Error('Unknown token: ' + _token + ', url:' + this.url);
}
};
$.fn.rss = function (url, options, callback) {
new RSS(this, url, options, callback).render();
return this; // Implement chaining
};
})(jQuery);
I think a better way would be to wrap that script in an angular module and load that module as a dependancy in your main app and use it anywhere you see fit.

How to reuse functions in an AngularJS factory?

I have an AngularJS factory for some common local storage manipulation. It's a common set of functions against different variables. I am constructing it so that the functions are repeated depending on which variable needs to be manipulated. Likely not an elegant way to go about this so open to options.
The factory looks as follows. Is there a way to reuse functions depending on the variable without so much code bloat?
angular.module('app.datastore', [])
.factory('DataStore', function() {
var venue = angular.fromJson(window.localStorage['venue'] || '[]');
var prize = angular.fromJson(window.localStorage['prize'] || '[]');
function persist_venue() {
window.localStorage['venue'] = angular.toJson(venue);
}
return {
list_venue: function () {
return venue;
},
get_venue: function(venueId) {
for (var i=0; i<venue.length; i++) {
if (venue[i].id === venueId) {
return venue[i];
}
}
return undefined;
},
create_venue: function(venueItem) {
venue.push(venueItem);
persist_venue();
},
list_prize: function () {
return prize;
},
get_prize: function(prizeId) {
for (var i=0; i<prize.length; i++) {
if (prize[i].id === prizeId) {
return prize[i];
}
}
return undefined;
},
create_prize: function(prizeItem) {
venue.push(prizeIem);
persist_prize();
}
};
});
My approach is to return in the factory a function which will return a store of a type (venue, prize, ...)
angular.module('app.datastore', [])
.factory('DataStore', function () {
var getStoreFunction = function (storeName) {
var store = angular.fromJson(window.localStorage[storeName] || '[]');
function persist() {
window.localStorage[storeName] = angular.toJson(store);
};
return {
list: function () {
return store;
},
getItem: function (id) {
return store.find(function (elem) {
return elem.id === id;
});
},
createItem: function (item) {
store.push(item);
persist(store);
}
}
};
return { getStore : getStoreFunction };
});
you can create unlimited store by using
var venueStore = DataStore.getStore('venue');
//use of your store
venueStore.createItem({
id : venueStore.list().length + 1,
name : 'myVenue' + venueStore.list().length + 1
});
$scope.venues = venueStore.list();
you can create a factory per type if you want or use it directly in your controller as in this example : https://jsfiddle.net/royto/cgxfmv4q/
i dont know if your familiar with John Papa's angular style guide but you really should take a look it might help you with a lot of design questions.
https://github.com/johnpapa/angular-styleguide
anyway - i would recommend you use this approach -
angular.module('app.datastore', [])
.factory('DataStore', function () {
var venue = angular.fromJson(window.localStorage['venue'] || '[]');
var prize = angular.fromJson(window.localStorage['prize'] || '[]');
return {
list_venue: list_venue,
persist_venue: persist_venue,
get_venue: get_venue,
create_venue: create_venue,
list_prize: list_prize,
get_prize: get_prize,
create_prize: create_prize
};
function persist_venue() {
window.localStorage['venue'] = angular.toJson(venue);
}
function list_venue() {
return venue;
}
function get_venue(venueId) {
for (var i = 0; i < venue.length; i++) {
if (venue[i].id === venueId) {
return venue[i];
}
}
return undefined;
}
function create_venue(venueItem) {
venue.push(venueItem);
persist_venue();
}
function list_prize() {
return prize;
}
function get_prize(prizeId) {
for (var i = 0; i < prize.length; i++) {
if (prize[i].id === prizeId) {
return prize[i];
}
}
return undefined;
}
function create_prize(prizeItem) {
venue.push(prizeIem);
persist_prize();
} });
i like this approach because on the top you can see all the functions available in this factory nice and easy,
and you can also reuse every function you expose outside, inside also, so its very effective and organized,
hope that helped,
good luck.

Accessing a method in the same factory

I have a factory defined like this:
mob.factory("OrderHelper", function() {
return {
lineItemPrice: function(lineItem) {
return lineItem.price * lineItem.quantity;
},
orderTotal: function(order) {
order.items.forEach(function (item)) {
total += lineItemPrice(item);
}
}
}
});
That problem is that I can't use the method "lineItemPrice" in "orderTotal" method.
It throws this error: Error: [$interpolate:interr] Can't interpolate: £{{orderTotal(order)}}
Can somebody please tell how can I call a method from a function in the same factory.
Thanks.
you could also do it like this:
mob.factory("OrderHelper", function() {
var methods = {};
methods.lineItemPrice = function(lineItem) {
return lineItem.price * lineItem.quantity;
};
methods.orderTotal = function(order) {
order.items.forEach(function (item)) {
total += methods.lineItemPrice(item);
}
};
return methods;
});
Try this (the revealing module pattern):
mob.factory("OrderHelper", function() {
var lineItemPrice = function(lineItem) {
return lineItem.price * lineItem.quantity;
};
var orderTotal = function(order) {
order.items.forEach(function (item)) {
total += lineItemPrice(item);
}
};
return {
lineItemPrice: lineItemPrice,
orderTotal: orderTotal
}
});
You can use angular.foreach() to iterate each items and supply the third parameter as the context of the callback function.
mob.factory("OrderHelper", function() {
return {
lineItemPrice: function(lineItem) {
return lineItem.price * lineItem.quantity;
},
orderTotal: function(order) {
var total = 0;
angular.forEach(order.items, function(item) {
total += this.lineItemPrice(item)
}, this); // add `this` as the context of the callback
return total;
}
}
});

Resources