I kind of rolled into React when stateless functions where popular so I never experienced the Class approach of it, which is bothering me now..
I'm not sure what this function does:
var keywordMapper = this.createKeywordMapper({
"constant.false": 'false',
"constant.true": 'true',
}, "identifier", true);
Which is called as:
this.$rules = {
"start": [{
token: "constant.numeric", // float
regex: "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"
}, {
token: keywordMapper,
regex: "[a-zA-Z_$][a-zA-Z0-9_$]*\\b"
}, {
token: "keyword.operator",
regex: "\\+|\\-|\\/|\\/\\/|%|<#>|#>|<#|&|\\^|~|<|>|<=|=>|==|!=|<>|="
}]
};
I think it maps the input (what input :s) and returns either 'false', 'true' or 'identifier' as default.
But what if I want to use it within a stateless functional component? Since I can't use this in there.
Any help or explanation on how the 'this' function works is much appreciated.
Greetings,
edit:
The whole useEffect:
useEffect(() => {
const newCompleter = {
getCompletions(editor, session, pos, prefix, callback) {
callback(null, completions);
},
};
const keywordMapper = this.createKeywordMapper({
"constant.false": 'false',
"constant.true": 'true',
}, "identifier", true);
const completionString = completions.map((x) => x.value).join('|');
const session = editor.current.editor.getSession();
session.setMode(`ace/mode/text`, () => {
const rules = session.$mode.$highlightRules.getRules();
if (Object.prototype.hasOwnProperty.call(rules, 'start')) {
rules.start = [
{
token: 'constant.numeric', // float
regex: '[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b',
},
{
token: keywordMapper(),
regex: '[a-zA-Z_$][a-zA-Z0-9_$]*\\b',
},
{
token: 'keyword.operator',
regex:
'\\+|\\-|\\/|\\/\\/|%|<#>|#>|<#|&|\\^|~|<|>|<=|=>|==|!=|<>|=',
},
];
}
// }
// force recreation of tokenizer
session.$mode.$tokenizer = null;
session.bgTokenizer.setTokenizer(session.$mode.getTokenizer());
// force re-highlight whole document
session.bgTokenizer.start(0);
});
// to extend existing
// addCompleter(myCompleter);
// to override all
setCompleters([newCompleter]);
}, [completions]);
Original class component
ace.define("ace/mode/brms_highlight_rules", ["require", "exports",
"module", "ace/lib/oop", "ace/mode/text_highlight_rules"], function(require, exports, module) {
"use strict";
var oop = require("../lib/oop");
var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
var BrmsHighlightRules = function() {
var FalseBool = (
"false"
);
var TrueBool = (
"true"
);
var keywordMapper = this.createKeywordMapper({
"constant.false": 'false',
"constant.true": 'true',
}, "identifier", true);
this.$rules = {
"start": [{
token: "constant.numeric", // float
regex: "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"
}, {
token: keywordMapper,
regex: "[a-zA-Z_$][a-zA-Z0-9_$]*\\b"
}, {
token: "keyword.operator",
regex: "\\+|\\-|\\/|\\/\\/|%|<#>|#>|<#|&|\\^|~|<|>|<=|=>|==|!=|<>|="
}]
};
this.normalizeRules();
};
oop.inherits(BrmsHighlightRules, TextHighlightRules);
exports.BrmsHighlightRules = BrmsHighlightRules;
});
ace.define("ace/mode/brms", ["require", "exports", "module", "ace/lib/oop", "ace/mode/text", "ace/mode/brms_highlight_rules"], function(require, exports, module) {
"use strict";
var oop = require("../lib/oop");
var TextMode = require("./text").Mode;
var BrmsHighlightRules = require("./brms_highlight_rules").BrmsHighlightRules;
var Mode = function() {
this.HighlightRules = BrmsHighlightRules;
this.$behaviour = this.$defaultBehaviour;
};
oop.inherits(Mode, TextMode);
(function() {
this.$id = "ace/mode/brms";
}).call(Mode.prototype);
exports.Mode = Mode;
});
(function() {
ace.require(["ace/mode/brms"], function(m) {
if (typeof module == "object" && typeof exports == "object" && module) {
module.exports = m;
}
});
})();
I am trying to test this createParameterGroup function which is calling a private function, When I try to test this createParameterGroup function, it gives an error saying that validateParameterGroup is not a function.
controller
angular.module('PpmApp')
.controller('parameterGroupListController', ['$scope', '$injector', 'parameterGroups', parameterGroupListController]);
function parameterGroupListController($scope, $injector, parameterGroups) {
$scope.createParameterGroup = function (parameterGroup) {
var validationErrors = validateParameterGroup(parameterGroup);
if (validationErrors.isError) return;
parameterGroupApiService.createParameterGroup(parameterGroup);
}
function validateParameterGroup(parameterGroup) {
var validationErrors = {};
validationErrors.isError = false;
// Validations goes here
return validationErrors;
}
};
Test
describe('createParameterGroup', function() {
var validationErrors, parameterGroup;
beforeEach(function() {
validationErrors = {};
validationErrors.isError;
parameterGroup = {
GroupName: "ABC",
Description: "ABC",
fromMonth: 1,
fromYear: 18,
toMonth: 12,
toYear: 18
}
});
it('should create a parameter group', function() {
expect($scope.createParameterGroup(parameterGroup)).toEqual(false);
});
});
After spending some time around how to write test cases right, I figured it out that I was doing expecting the wrong condition to evaluate the correct output. So this is how I have done it now.
Test Cases
describe('createParameterGroup', function() {
it('with blank name returns error message of property name can not be blank', function() {
var parameterGroup = {
name: "",
description: "sss",
fromMonth: 1,
fromYear: 18,
toMonth: 12,
toYear: 18
};
$scope.createParameterGroup(parameterGroup);
for (var property in parameterGroup) {
if (!parameterGroup[property] && property != 'description') {
var propertyName = (property == 'name') ? 'Parameter group name' : property;
}
return property;
}
expect($scope.createPopupInfo.validationErrors.isError).toEqual(true);
expect($scope.createPopupInfo.validationErrors[property]).toEqual(propertyName + ' ' + constantsProvider.validationMessages.blankField);
});
it('with special characters returns error message of invalid parameter group name', function() {
var parameterGroup = {
name: "/*&",
description: "ABC",
fromMonth: 1,
fromYear: 18,
toMonth: 12,
toYear: 18
};
$scope.createParameterGroup(parameterGroup);
expect($scope.createPopupInfo.validationErrors.isError).toEqual(true);
expect($scope.createPopupInfo.validationErrors.name).toEqual('Parameter group name \'' + parameterGroup.name + '\' ' + constantsProvider.validationMessages.specialCharacters);
});
it('with invalid effective time period returns error message of Invalid effective time period', function() {
var parameterGroup = {
name: "ABC",
description: "ABC",
fromMonth: 5,
fromYear: 18,
toMonth: 4,
toYear: 18
};
$scope.createParameterGroup(parameterGroup);
expect($scope.createPopupInfo.validationErrors.isError).toEqual(true);
expect($scope.createPopupInfo.validationErrors.toYear).toEqual(constantsProvider.validationMessages.effectiveTimePeriod);
});
it('with valid input returns the given input back without any error message', function() {
var parameterGroup = {
GroupName: "ABC",
Description: "sss",
EffectiveStartDateTime: 1 / 18,
EffectiveEndDateTime: 12 / 18
};
var createResponse = {};
createResponse.IsSuccess = true;
spyOn(parameterGroupApiService, 'createParameterGroup').and.callFake(function() {
return {
then: function(callback) {
return callback(createResponse);
}
}
});
spyOn(parameterGroupApiService, 'getParameterGroups').and.callFake(function() {
return {
then: function(callback) {
callback(parameterGroup);
return {
catch: function() {}
}
}
}
});
$scope.createParameterGroup(parameterGroup);
expect($scope.createPopupInfo.validationErrors.isError).toEqual(false);
expect(parameterGroupApiService.createParameterGroup).toHaveBeenCalled();
expect(parameterGroupApiService.getParameterGroups).toHaveBeenCalled();
expect($scope.parameterGroups).toBe(parameterGroup);
});
});
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.
I have a service to share an object in my app... I want to post that object to the mongo db but when I call the function that should return the object it gives me the function's text.
The service is here:
angular.module('comhubApp')
.service('markerService', function () {
this.markers = [];
this.newMarker = { title: '',
description: '',
lat: '',
lon: '',
user: '',
created_at: '' };
// This is supposed to return the marker object
this.newMarker = function () {
return this.newMarker;
};
this.setTitle = function (title) {
this.newMarker.title = title;
console.log('title service set: ' + title);
};
this.setDescription = function (description) {
this.newMarker.description = description;
console.log('Description service set: ' + description);
};
this.setLat = function (lat) {
this.newMarker.lat = lat;
console.log('lat service set: ' + lat);
};
this.setLon = function (lon) {
this.newMarker.lon = lon;
console.log('lon service set: ' + lon);
};
this.reset = function () {
this.newMarker = { title: '',
description: '',
lat: '',
lon: '',
user: '',
created_at: ''};
}
this.setMarkers = function (markers) {
this.markers = markers;
}
this.markers = function () {
return this.markers;
}
this.addMarker = function (marker) {
//todo append marker
}
});
newMarker returns:
this.newMarker = function () {
return this.newMarker;
};
The Controller using the service is here
$scope.addMarker = function() {
if($scope.newMarker.title === '') {
console.log('newMarker title is empty');
return;
}
markerService.setTitle($scope.newMarker.title);
markerService.setDescription($scope.newMarker.description);
console.log(markerService.newMarker());
// $http.post('/api/markers', { name: $scope.newMarker });
// $scope.newMarker = '';
};
$scope new marker is form data.. i tried to put that right into my service with no success. Instead I out the form data into the controller then push it to the service. If there is a better way to do that please let me know.
If this service is bad in any other way let me know I am new to all this and so I followed another answer I saw on here.
You are overriding your object with function. Just give them different names and it should work just fine.
this.newMarker = { ... };
this.getNewMarker = function () { return this.newMarker };
EDIT:
You should also always create new instance from marker. Otherwise you just edit the same object all the time. Here is example I made. Its not best practice but hope you get the point.
angular.module('serviceApp', [])
.factory('Marker', function () {
function Marker() {
this.title = '';
this.descrpition = '';
}
// use setters and getters if you want to make your variable private
// in this example we are not using these functions
Marker.prototype.setTitle = function (title) {
this.title = title;
};
Marker.prototype.setDescription = function (description) {
this.description = description;
};
return Marker;
})
.service('markerService', function (Marker) {
this.markers = [];
this.getNewMarker = function () {
return new Marker();
}
this.addMarker = function (marker) {
this.markers.push(marker);
}
})
.controller('ServiceCtrl', function ($scope, markerService) {
$scope.marker = markerService.getNewMarker();
$scope.addMarker = function () {
markerService.addMarker($scope.marker);
$scope.marker = markerService.getNewMarker();
}
$scope.markers = markerService.markers;
});
You could also create Marker in controller and use markerService just to store your object.
And working demo:
http://jsfiddle.net/3cvc9rrs/
So, that function is the problem. I was blindly following another example and it was wrong in my case. The solution is to remove that function and access markerService.newMarker directly.
I am still a big enough noob that I am not sure why the call was returning the function as a string. It seems to have something to do with how it is named but it is just a guess.
Slowly extending my nested form at http://jsfiddle.net/gZC5k/1004/ I'm running into a difficulty getting to work ko.computed that I want to use to compute the number of children in the nested JSON array. The code that breaks is at // this breaks
self.contacts = ko.observableArray(ko.utils.arrayMap(contacts, function (contact) {
return {
firstName: ko.observable(contact.firstName),
lastName: ko.observable(contact.lastName),
isKey: ko.observable(contact.isKey),
gender: ko.observable(contact.gender),
phones: ko.observableArray(ko.utils.arrayMap(contact.phones, function (phone) {
return {
type: ko.observable(phone.type),
number: ko.observable(phone.number),
calls: ko.observableArray(phone.calls),
callsVisible: ko.observable(false)
};
})),
addresses: ko.observableArray(contact.addresses),
optionGender: optionGender,
phonesVisible: ko.observable(false),
addressesVisible: ko.observable(false),
// this breaks
// numberOfPhones: ko.computed(function (contact) {
// return contact.phones.length;
// });
};
}));
Where is the error?
I think that you're going to need to create each contact as a function:
var ContactModel = function(contact)
{
var self = this;
self.firstName = ko.observable(contact.firstName);
self.lastName = ko.observable(contact.lastName);
self.isKey = ko.observable(contact.isKey);
self.gender = ko.observable(contact.gender);
self. phones = ko.observableArray(ko.utils.arrayMap(contact.phones, function (phone) {
return {
type: ko.observable(phone.type),
number: ko.observable(phone.number),
calls: ko.observableArray(phone.calls),
callsVisible: ko.observable(false)
};
}));
self.addresses = ko.observableArray(contact.addresses);
self.optionGender = optionGender;
self.phonesVisible = ko.observable(false);
self.addressesVisible = ko.observable(false);
self.numberOfPhones = ko.computed(function () {
return self.phones().length;
});
return self;
};
And create it like this:
self.contacts = ko.observableArray(ko.utils.arrayMap(contacts, function (contact) {
return new ContactModel(contact);
}));
You can try this:
numberOfPhones: ko.computed(function () {
return contact.phones.length;
})