在 Selectize.js 项中呈现 angular 指令
Render angular directives in Selectize.js items
我在我的 angular 项目中使用 angular-selectize to use Selectize.js。
要在 Selectize.js 选择器中使用自定义项,我使用 Selectize.js' render 选项:
render: {
item: function(item, escape) {
var avatar = '<div>' +
'<span avatars="\'' + escape(item._id) +'\'" class="avatars">' +
'</span>' +
escape(item.nick) +
'</div>';
var compiledAvatar = $compile(avatar)($rootScope);
$timeout();
return compiledAvatar.html();
},
其中 avatars 是具有异步行为的自定义指令
问题是 render.item
函数需要一个 HTML 字符串作为输出 但是:
- 无法按
render.item
方法预期的同步方式返回渲染或“$compile
ed”HTML 字符串。
- 当项目的元素已添加到 DOM 时,我不知道如何在之后呈现该项目的元素。
注意虽然调用了$compile,但由于$compile的异步特性,返回的字符串不是预期的编译结果,而是编译前的字符串。
一个想法是使用 DOM 操作,这不是最推荐的 Angular 方式,但我明白了 working on this plunker. and a second one with custom directive and randomized data to simulate your compiled avatar.
为了模拟你的异步调用,我使用了ngResource。我的渲染函数 return 是一个带有特殊 class 标记 compiledavatar
的字符串 "<div class='compiledavatar'>Temporary Avatar</div>"
。在一两秒钟内,您会看到 select 元素时的临时头像。当 ngResource 调用完成时,我查找带有 class compiledavatar
的元素,然后将 html 替换为我下载的内容。这是完整的代码:
var app = angular.module('plunker', ['selectize', 'ngResource']);
app.controller('MainCtrl', function($scope, $resource, $document) {
var vm = this;
vm.name = 'World';
vm.$resource = $resource;
vm.myModel = 1;
vm.$document = $document;
vm.myOptions = [{
id: 1,
title: 'Spectrometer'
}, {
id: 2,
title: 'Star Chart'
}, {
id: 3,
title: 'Laser Pointer'
}];
vm.myConfig = {
create: true,
valueField: 'id',
labelField: 'title',
delimiter: '|',
placeholder: 'Pick something',
onInitialize: function(selectize) {
// receives the selectize object as an argument
},
render: {
item: function(item, escape) {
var label = item.title;
var caption = item.id;
var Stub = vm.$resource('mydata', {});
// This simulates your asynchronous call
Stub.get().$promise.then(function(s) {
var result = document.getElementsByClassName("compiledavatar")
angular.element(result).html(s.compiledAvatar);
// Once the work is done, remove the class so next time this element wont be changed
// Remove class
var elems = document.querySelectorAll(".compiledavatar");
[].forEach.call(elems, function(el) {
el.className = el.className.replace(/compiledavatar/, "");
});
});
return "<div class='compiledavatar'>Temporary Avatar</div>"
}
},
// maxItems: 1
};
});
为了模拟 JSON API 我刚刚在 plunker 中创建了一个文件 mydata
:
{
"compiledAvatar": "<div><span style='display: block; color: black; font-size: 14px;'>an avatar</span></div>"
}
当然你的编译函数应该return你每次调用都有不同的东西。我给了我同样的原理来证明。
此外,如果您的动态代码是 Agular 指令,这里有一个 second plunker 带有自定义指令和随机数据,因此您可以更好地查看解决方案:
数据包含自定义指令my-customer
:
[{
"compiledAvatar": "<div><span style='display: block; color: black; font-size: 14px;'>an avatar #1 <my-customer></my-customer></span></div>"
},
{
"compiledAvatar": "<div><span style='display: block; color: black; font-size: 14px;'>an avatar #2 <my-customer></my-customer></span></div>"
},
(...)
指令定义为:
app.directive('myCustomer', function() {
return {
template: '<div>and a custom directive</div>'
};
});
与应用程序的主要区别在于,您必须在替换 HTML 时添加 $compile,并且文本应显示 An avatar #(number) and a custom directive
。我得到一个 json 值的数组,并使用一个简单的随机数来选择一个值。替换 HTML 后,我会删除 class,因此下次只会更改最后添加的元素。
Stub.query().$promise.then(function(s) {
var index = Math.floor(Math.random() * 10);
var result = document.getElementsByClassName("compiledavatar")
angular.element(result).html($compile(s[index].compiledAvatar)($scope));
// Remove class
var elems = document.querySelectorAll(".compiledavatar");
[].forEach.call(elems, function(el) {
el.className = el.className.replace(/compiledavatar/, "");
});
});
此外,我查看了 selectize 库,你不能 return 一个承诺...因为它对值 return 进行了 html.replace 渲染.这就是为什么我去了带有 class 的临时字符串的路由以便稍后检索和更新。
让我知道是否有帮助。
此答案基于@gregori 的有用答案,但有以下区别:
- 考虑 Selectize.js' 渲染缓存。 Selectize.js 的标准行为是项目在渲染函数返回时被缓存,而不是我们对它们所做的修改。添加和删除一些元素后,如果我们不相应地更新渲染缓存,将显示缓存而不是修改后的版本。
- 使用随机 id 来识别要从 DOM 操作的 select 元素。
- 使用观察者知道编译何时完成
首先,我们定义一个修改selectize.js渲染缓存的方法:
scope.selectorCacheUpdate = function(key, value, type){
var cached = selectize.renderCache[type][key];
// update cached element
var newValue = angular.element(cached).html(value);
selectize.renderCache[type][key] = newValue[0].outerHTML;
return newValue.html();
};
那么,render函数定义如下:
function renderAvatar(item, escape, type){
// Random id used to identify the element
var randomId = Math.floor(Math.random() * 0x10000000).toString(16);
var avatar =
'<div id="' + randomId + '">' +
'<span customAvatarTemplate ...></span>' +
...
'</div>';
var compiled = $compile(avatar)($rootScope);
// watcher to see when the element has been compiled
var destroyWatch = $rootScope.$watch(
function (){
return compiled[0].outerHTML;
},
function (newValue, oldValue){
if(newValue !== oldValue){
var elem = angular.element(document.getElementById(randomId));
var rendered = elem.scope().selectorCacheUpdate(item._id, compiled.html(), type);
// Update DOM element
elem.html(rendered);
destroyWatch();
}
}
);
});
return avatar;
}
注意:渲染缓存的关键是selectize项的valueField
,在本例中,_id
最后,我们将此函数作为 selectize 渲染函数添加到 selectize 配置对象中:
config = {
...
render: {
item: function(i,e){
return renderAvatar(i, e, 'item');
},
option: function(i,e){
return renderAvatar(i, e, 'option');
}
},
...
}
有关更多详细信息,请参阅如何将此解决方案添加到引发此问题的应用程序中:https://github.com/P2Pvalue/teem/commit/968a437e58c5f1e70e80cc6aa77f5aefd76ba8e3。
我在我的 angular 项目中使用 angular-selectize to use Selectize.js。
要在 Selectize.js 选择器中使用自定义项,我使用 Selectize.js' render 选项:
render: {
item: function(item, escape) {
var avatar = '<div>' +
'<span avatars="\'' + escape(item._id) +'\'" class="avatars">' +
'</span>' +
escape(item.nick) +
'</div>';
var compiledAvatar = $compile(avatar)($rootScope);
$timeout();
return compiledAvatar.html();
},
其中 avatars 是具有异步行为的自定义指令
问题是 render.item
函数需要一个 HTML 字符串作为输出 但是:
- 无法按
render.item
方法预期的同步方式返回渲染或“$compile
ed”HTML 字符串。 - 当项目的元素已添加到 DOM 时,我不知道如何在之后呈现该项目的元素。
注意虽然调用了$compile,但由于$compile的异步特性,返回的字符串不是预期的编译结果,而是编译前的字符串。
一个想法是使用 DOM 操作,这不是最推荐的 Angular 方式,但我明白了 working on this plunker. and a second one with custom directive and randomized data to simulate your compiled avatar.
为了模拟你的异步调用,我使用了ngResource。我的渲染函数 return 是一个带有特殊 class 标记 compiledavatar
的字符串 "<div class='compiledavatar'>Temporary Avatar</div>"
。在一两秒钟内,您会看到 select 元素时的临时头像。当 ngResource 调用完成时,我查找带有 class compiledavatar
的元素,然后将 html 替换为我下载的内容。这是完整的代码:
var app = angular.module('plunker', ['selectize', 'ngResource']);
app.controller('MainCtrl', function($scope, $resource, $document) {
var vm = this;
vm.name = 'World';
vm.$resource = $resource;
vm.myModel = 1;
vm.$document = $document;
vm.myOptions = [{
id: 1,
title: 'Spectrometer'
}, {
id: 2,
title: 'Star Chart'
}, {
id: 3,
title: 'Laser Pointer'
}];
vm.myConfig = {
create: true,
valueField: 'id',
labelField: 'title',
delimiter: '|',
placeholder: 'Pick something',
onInitialize: function(selectize) {
// receives the selectize object as an argument
},
render: {
item: function(item, escape) {
var label = item.title;
var caption = item.id;
var Stub = vm.$resource('mydata', {});
// This simulates your asynchronous call
Stub.get().$promise.then(function(s) {
var result = document.getElementsByClassName("compiledavatar")
angular.element(result).html(s.compiledAvatar);
// Once the work is done, remove the class so next time this element wont be changed
// Remove class
var elems = document.querySelectorAll(".compiledavatar");
[].forEach.call(elems, function(el) {
el.className = el.className.replace(/compiledavatar/, "");
});
});
return "<div class='compiledavatar'>Temporary Avatar</div>"
}
},
// maxItems: 1
};
});
为了模拟 JSON API 我刚刚在 plunker 中创建了一个文件 mydata
:
{
"compiledAvatar": "<div><span style='display: block; color: black; font-size: 14px;'>an avatar</span></div>"
}
当然你的编译函数应该return你每次调用都有不同的东西。我给了我同样的原理来证明。
此外,如果您的动态代码是 Agular 指令,这里有一个 second plunker 带有自定义指令和随机数据,因此您可以更好地查看解决方案:
数据包含自定义指令my-customer
:
[{
"compiledAvatar": "<div><span style='display: block; color: black; font-size: 14px;'>an avatar #1 <my-customer></my-customer></span></div>"
},
{
"compiledAvatar": "<div><span style='display: block; color: black; font-size: 14px;'>an avatar #2 <my-customer></my-customer></span></div>"
},
(...)
指令定义为:
app.directive('myCustomer', function() {
return {
template: '<div>and a custom directive</div>'
};
});
与应用程序的主要区别在于,您必须在替换 HTML 时添加 $compile,并且文本应显示 An avatar #(number) and a custom directive
。我得到一个 json 值的数组,并使用一个简单的随机数来选择一个值。替换 HTML 后,我会删除 class,因此下次只会更改最后添加的元素。
Stub.query().$promise.then(function(s) {
var index = Math.floor(Math.random() * 10);
var result = document.getElementsByClassName("compiledavatar")
angular.element(result).html($compile(s[index].compiledAvatar)($scope));
// Remove class
var elems = document.querySelectorAll(".compiledavatar");
[].forEach.call(elems, function(el) {
el.className = el.className.replace(/compiledavatar/, "");
});
});
此外,我查看了 selectize 库,你不能 return 一个承诺...因为它对值 return 进行了 html.replace 渲染.这就是为什么我去了带有 class 的临时字符串的路由以便稍后检索和更新。 让我知道是否有帮助。
此答案基于@gregori 的有用答案,但有以下区别:
- 考虑 Selectize.js' 渲染缓存。 Selectize.js 的标准行为是项目在渲染函数返回时被缓存,而不是我们对它们所做的修改。添加和删除一些元素后,如果我们不相应地更新渲染缓存,将显示缓存而不是修改后的版本。
- 使用随机 id 来识别要从 DOM 操作的 select 元素。
- 使用观察者知道编译何时完成
首先,我们定义一个修改selectize.js渲染缓存的方法:
scope.selectorCacheUpdate = function(key, value, type){
var cached = selectize.renderCache[type][key];
// update cached element
var newValue = angular.element(cached).html(value);
selectize.renderCache[type][key] = newValue[0].outerHTML;
return newValue.html();
};
那么,render函数定义如下:
function renderAvatar(item, escape, type){
// Random id used to identify the element
var randomId = Math.floor(Math.random() * 0x10000000).toString(16);
var avatar =
'<div id="' + randomId + '">' +
'<span customAvatarTemplate ...></span>' +
...
'</div>';
var compiled = $compile(avatar)($rootScope);
// watcher to see when the element has been compiled
var destroyWatch = $rootScope.$watch(
function (){
return compiled[0].outerHTML;
},
function (newValue, oldValue){
if(newValue !== oldValue){
var elem = angular.element(document.getElementById(randomId));
var rendered = elem.scope().selectorCacheUpdate(item._id, compiled.html(), type);
// Update DOM element
elem.html(rendered);
destroyWatch();
}
}
);
});
return avatar;
}
注意:渲染缓存的关键是selectize项的valueField
,在本例中,_id
最后,我们将此函数作为 selectize 渲染函数添加到 selectize 配置对象中:
config = {
...
render: {
item: function(i,e){
return renderAvatar(i, e, 'item');
},
option: function(i,e){
return renderAvatar(i, e, 'option');
}
},
...
}
有关更多详细信息,请参阅如何将此解决方案添加到引发此问题的应用程序中:https://github.com/P2Pvalue/teem/commit/968a437e58c5f1e70e80cc6aa77f5aefd76ba8e3。