线程很晚,但是我在Angular之前使用的一种技术是利用JSON和JS的灵活性来动态引用集合键,并使用环境的不可分割的事实(主机服务器名称,当前浏览器语言)等)作为输入,以选择性地区分/优先选择JSON数据结构中的后缀键名。
这不仅提供了部署环境上下文(每个OP),而且还提供了任意上下文(例如语言)以同时(在理想情况下)在单个配置清单中提供i18n或任何其他所需的变体,而无需重复且易于理解。
关于10行VANILLA JS
过于简化但经典的示例:JSON格式的属性文件中的API终结点基础URL随环境而异,主机服务器在(环境)上也会有所不同:
...
'svcs': {
'VER': '2.3',
'API@localhost': 'http://localhost:9090/',
'API@www.uat.productionwebsite.com': 'https://www.uat.productionwebsite.com:9090/res/',
'API@www.productionwebsite.com': 'https://www.productionwebsite.com:9090/api/res/'
},
...
区分功能的关键只是请求中的服务器主机名。
自然,可以根据用户的语言设置将其与其他键组合使用:
...
'app': {
'NAME': 'Ferry Reservations',
'NAME@fr': 'Réservations de ferry',
'NAME@de': 'Fähren Reservierungen'
},
...
辨别/偏好的范围可以限于单个键(如上),其中只有在为该函数的输入有匹配的键+后缀或整个结构以及该结构本身的情况下,才会覆盖“基本”键递归解析以匹配歧视/偏好后缀:
'help': {
'BLURB': 'This pre-production environment is not supported. Contact Development Team with questions.',
'PHONE': '808-867-5309',
'EMAIL': 'coder.jen@lostnumber.com'
},
'help@www.productionwebsite.com': {
'BLURB': 'Please contact Customer Service Center',
'BLURB@fr': 'S\'il vous plaît communiquer avec notre Centre de service à la clientèle',
'BLURB@de': 'Bitte kontaktieren Sie unseren Kundendienst!!1!',
'PHONE': '1-800-CUS-TOMR',
'EMAIL': 'customer.service@productionwebsite.com'
},
因此,如果访问生产网站的用户具有德语(de)语言首选项设置,则上述配置将折叠为:
'help': {
'BLURB': 'Bitte kontaktieren Sie unseren Kundendienst!!1!',
'PHONE': '1-800-CUS-TOMR',
'EMAIL': 'customer.service@productionwebsite.com'
},
这种神奇的偏好/区分JSON重写功能是什么样的?不多:
// prefer(object,suffix|[suffixes]) by/par/durch storsoc
// prefer({ a: 'apple', a@env: 'banana', b: 'carrot' },'env') -> { a: 'banana', b: 'carrot' }
function prefer(o,sufs) {
for (var key in o) {
if (!o.hasOwnProperty(key)) continue; // skip non-instance props
if(key.split('@')[1]) { // suffixed!
// replace root prop with the suffixed prop if among prefs
if(o[key] && sufs.indexOf(key.split('@')[1]) > -1) o[key.split('@')[0]] = JSON.parse(JSON.stringify(o[key]));
// and nuke the suffixed prop to tidy up
delete o[key];
// continue with root key ...
key = key.split('@')[0];
}
// ... in case it's a collection itself, recurse it!
if(o[key] && typeof o[key] === 'object') prefer(o[key],sufs);
};
};
在包括Angular和pre-Angular网站的实现中,我们通过将JSON放入自执行的JS闭包中(包括preferred()函数),并馈入主机名和语言代码(并接受您可能需要的任何其他任意后缀):
(function(prefs){ var props = {
'svcs': {
'VER': '2.3',
'API@localhost': 'http://localhost:9090/',
'API@www.uat.productionwebsite.com': 'https://www.uat.productionwebsite.com:9090/res/',
'API@www.productionwebsite.com': 'https://www.productionwebsite.com:9090/api/res/'
},
...
/* yadda yadda moar JSON und bisque */
function prefer(o,sufs) {
// body of prefer function, broken for e.g.
};
// convert string and comma-separated-string to array .. and process it
prefs = [].concat( ( prefs.split ? prefs.split(',') : prefs ) || []);
prefer(props,prefs);
window.app_props = JSON.parse(JSON.stringify(props));
})([location.hostname, ((window.navigator.userLanguage || window.navigator.language).split('-')[0]) ] );
Angular之前的站点现在将具有折叠的(没有@后缀键)window.app_props可供参考。
作为引导/初始化步骤,Angular站点只需将死掉的prop对象复制到$ rootScope中,并(可选)将其从全局/窗口范围中销毁
app.constant('props',angular.copy(window.app_props || {})).run( function ($rootScope,props) { $rootScope.props = props; delete window.app_props;} );
随后注入到控制器中:
app.controller('CtrlApp',function($log,props){ ... } );
或从视图的绑定中引用:
<span>{{ props.help.blurb }} {{ props.help.email }}</span>
注意事项?@字符不是有效的JS / JSON变量/键命名,但到目前为止已被接受。如果这违反了协议,只要您坚持使用它,就可以替换任何喜欢的约定,例如“ __”(双下划线)。
该技术可以在服务器端应用,移植到Java或C#,但效率/紧凑性可能会有所不同。
另外,功能/约定也可能是前端编译脚本的一部分,因此,完整的,全环境/全语言的JSON永远不会通过网络传输。
更新
我们已经改进了该技术的用法,以允许一个键使用多个后缀,避免被迫使用集合(您仍然可以根据需要深度使用),并遵守首选后缀的顺序。
示例(另请参见工作jsFiddle):
var o = { 'a':'apple', 'a@dev':'apple-dev', 'a@fr':'pomme',
'b':'banana', 'b@fr':'banane', 'b@dev&fr':'banane-dev',
'c':{ 'o':'c-dot-oh', 'o@fr':'c-point-oh' }, 'c@dev': { 'o':'c-dot-oh-dev', 'o@fr':'c-point-oh-dev' } };
/*1*/ prefer(o,'dev'); // { a:'apple-dev', b:'banana', c:{o:'c-dot-oh-dev'} }
/*2*/ prefer(o,'fr'); // { a:'pomme', b:'banane', c:{o:'c-point-oh'} }
/*3*/ prefer(o,'dev,fr'); // { a:'apple-dev', b:'banane-dev', c:{o:'c-point-oh-dev'} }
/*4*/ prefer(o,['fr','dev']); // { a:'pomme', b:'banane-dev', c:{o:'c-point-oh-dev'} }
/*5*/ prefer(o); // { a:'apple', b:'banana', c:{o:'c-dot-oh'} }
1/2(基本用法)倾向于使用'@dev'键,并丢弃所有其他后缀键
3比“ @fr”更喜欢“ @dev”,比所有其他偏爱“ @ dev&fr”
4(与3相同,但比起“ @dev”更喜欢“ @fr”)
5没有首选后缀,删除所有后缀属性
它通过对每个带后缀的属性进行评分,并在对属性进行迭代并找到更高分的后缀时,将带后缀的属性的值提升为非带后缀的属性来实现。
此版本中的一些效率,包括消除对JSON的依赖以进行深度复制,以及仅递归到在深度得分中幸存下来的对象:
function prefer(obj,suf) {
function pr(o,s) {
for (var p in o) {
if (!o.hasOwnProperty(p) || !p.split('@')[1] || p.split('@@')[1] ) continue; // ignore: proto-prop OR not-suffixed OR temp prop score
var b = p.split('@')[0]; // base prop name
if(!!!o['@@'+b]) o['@@'+b] = 0; // +score placeholder
var ps = p.split('@')[1].split('&'); // array of property suffixes
var sc = 0; var v = 0; // reset (running)score and value
while(ps.length) {
// suffix value: index(of found suffix in prefs)^10
v = Math.floor(Math.pow(10,s.indexOf(ps.pop())));
if(!v) { sc = 0; break; } // found suf NOT in prefs, zero score (delete later)
sc += v;
}
if(sc > o['@@'+b]) { o['@@'+b] = sc; o[b] = o[p]; } // hi-score! promote to base prop
delete o[p];
}
for (var p in o) if(p.split('@@')[1]) delete o[p]; // remove scores
for (var p in o) if(typeof o[p] === 'object') pr(o[p],s); // recurse surviving objs
}
if( typeof obj !== 'object' ) return; // validate
suf = ( (suf || suf === 0 ) && ( suf.length || suf === parseFloat(suf) ) ? suf.toString().split(',') : []); // array|string|number|comma-separated-string -> array-of-strings
pr(obj,suf.reverse());
}
'ngconstant:development'
在'serve'
-如果你把它放在手表的config下'gruntfile'
的tasks: ['ngconstant:development']
-你不会需要重新启动grunt serve
时,您更新的gruntfile发展的变量。