我的自定义指令中有一个隔离范围的问题。
我已经构建了一个自定义指令。当我没有使用父范围在指令中设置scope-attribute时,它工作。
它做了什么?它呈现了本机JavaScript控件。每次加载站点时,控件都被初始化,控件的数据被插入到父作用域中。
为什么我需要一个孤立的范围?因为我想将指令两次放在页面上。对于我希望放入不同变量的数据。
有什么问题?每次加载页面都可以正常工作。然后我使用ng-route切换到另一个站点。在那里我使用$ Location.Path重置到第一页的路由。我的控件初始化但似乎它没有呈现它的内容。例如,没有keydown工作。
这里有一些代码: 指令:
<people-picker instancename="peoplePicker" labeltext="Mitarbeiter" singleusermode="true" useonlyallowedusers="true"
allowedusers="AllowedUsers" selectedusers="selectedUsers" instance="instance"
callbackwhenuserchanged="dataChanged(currentSelectedUsers)">
</people-picker>
指令:
.directive('peoplePicker', function () {
return {
restrict: 'E',
templateUrl: '../DirectiveTemplates/PeoplePicker.html',
scope: {
instancename: '@',
labeltext: '@',
singleusermode: '@',
selectedusers: '=',
useonlyallowedusers: '@',
allowedusers: '=',
callbackwhenuserchanged: '&',
instance: '='
},
controller: function ($scope, utilities, spContextProvider) {
$scope.singleusermode = $scope.singleusermode !== undefined && $scope.singleusermode.toUpperCase() === 'TRUE';
$scope.useonlyallowedusers = $scope.useonlyallowedusers !== undefined && $scope.useonlyallowedusers.toUpperCase() === 'TRUE';
$scope.peoplePicker = {};
$scope.idSpanAdministrators = 'spanAdministrators' + $scope.instancename;
$scope.idInputAdministrators = 'inputAdministrators' + $scope.instancename;
$scope.idDivAdministratorsSearch = 'divAdministratorsSearch' + $scope.instancename;
$scope.idHdnAdministrators = 'hdnAdministrators' + $scope.instancename;
...
$scope.initializePeoplePicker = function () {
var context = spContextProvider.GetSharePointContext();
$scope.peoplePicker = new CAMControl.PeoplePicker(
$scope.instancename,
context,
$('#' + $scope.idSpanAdministrators),
$('#' + $scope.idInputAdministrators),
$('#' + $scope.idDivAdministratorsSearch),
$('#' + $scope.idHdnAdministrators));
...
$scope.peoplePicker.Initialize();
$scope.addSelectedUsers($scope.selectedusers);
}
$scope.addSelectedUsers = function (selectedUsers) {
if (selectedUsers !== null) {
angular.forEach(selectedUsers, function (item) {
$scope.peoplePicker.RecipientSelected(item.Login, item.Name, item.Email);
});
}
}
$scope.$watchCollection($scope.selectedusers, function () {
$scope.addSelectedUsers($scope.selectedusers);
}, true);
spContextProvider.CallSharePointWithFunction($scope.initializePeoplePicker);
}
这个peoplepicker使用一个控件,它可以处理4个html元素。遗嘱必须初始化。
对于完整代码,我将发布控件(删除了一些行,因为长度限制)。
var CAMControl;
(function (CAMControl) {
var PeoplePicker = (function () {
// Constructor
function PeoplePicker(InstanceName, SharePointContext, PeoplePickerControl, PeoplePickerEdit, PeoplePickerDisplay, PeoplePickerData) {
//public properties
this.SharePointContext = SharePointContext;
this.PeoplePickerControl = PeoplePickerControl;
this.PeoplePickerEdit = PeoplePickerEdit;
this.PeoplePickerDisplay = PeoplePickerDisplay;
this.PeoplePickerData = PeoplePickerData;
this.InstanceName = InstanceName;
// optionally show more/less entries in the people picker dropdown, 5 is the default
...
window.document[this.InstanceName] = this;
}
// Property wrapped in function to allow access from event handler
PeoplePicker.prototype.GetPrincipalType = function () {
return this.PrincipalType;
}
...
// HTML encoder
PeoplePicker.prototype.HtmlEncode = function (html) {
return document.createElement('a').appendChild(document.createTextNode(html)).parentNode.innerHTML;
}
// HTML decoder
PeoplePicker.prototype.HtmlDecode = function (html) {
var a = document.createElement('a');
a.innerHTML = html;
return a.textContent;
}
...
PeoplePicker.prototype.LoadScript = function (url, callback) {
var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.src = url;
// Attach handlers for all browsers
var done = false;
script.onload = script.onreadystatechange = function () {
if (!done && (!this.readyState
|| this.readyState == "loaded"
|| this.readyState == "complete")) {
done = true;
// Continue your code
callback();
// Handle memory leak in IE
script.onload = script.onreadystatechange = null;
head.removeChild(script);
}
};
head.appendChild(script);
}
...
// Generates the html for a resolved user
PeoplePicker.prototype.ConstructResolvedUserSpan = function (login, name) {
var login = login.replace(/\\/g, '\\\\');
resultDisplay = 'Remove person or group {0}';
if (typeof deleteUser != 'undefined') {
resultDisplay = deleteUser;
}
resultDisplay = this.Format(resultDisplay, name);
var userDisplaySpanTemplate = '<span class="peoplepicker-userSpan"><span class="entity-resolved">{0}</span><a title="{3}" class="peoplepicker-delImage" onclick="{1}.DeleteProcessedUser({2}); return false;" href="#">x</a></span>';
return this.Format(userDisplaySpanTemplate, name, this.InstanceName, "'" + login + "'", resultDisplay);
}
// Create a html representation of the resolved user array
PeoplePicker.prototype.ResolvedUsersToHtml = function () {
var userHtml = '';
for (var i = 0; i < this._ResolvedUsers.length; i++) {
userHtml += this.ConstructResolvedUserSpan(this._ResolvedUsers[i].Login, this._ResolvedUsers[i].Name);
}
return userHtml;
}
// Returns a resolved user object
PeoplePicker.prototype.ResolvedUser = function (login, name, email) {
var user = new Object();
user.Login = login;
user.Name = name;
user.Email = email;
return user;
}
// Add resolved user to array and updates the hidden field control with a JSON string
PeoplePicker.prototype.PushResolvedUser = function (resolvedUser) {
if (this.AllowDuplicates) {
this._ResolvedUsers.push(resolvedUser);
} else {
var duplicate = false;
for (var i = 0; i < this._ResolvedUsers.length; i++) {
if (this._ResolvedUsers[i].Login == resolvedUser.Login) {
duplicate = true;
}
}
if (!duplicate) {
this._ResolvedUsers.push(resolvedUser);
}
}
this.PeoplePickerData.val(JSON.stringify(this._ResolvedUsers));
}
// Function called then the clientPeoplePickerSearchUser succeeded
PeoplePicker.prototype.QuerySuccess = function (queryNumber, searchResult) {
var results = this.SharePointContext.parseObjectFromJsonString(searchResult.get_value());
var txtResults = '';
var baseDisplayTemplate = '<div class=\'ms-bgHoverable\' style=\'width: 400px; padding: 4px;\' onclick=\'javascript:{0}.RecipientSelected(\"{1}\", \"{2}\", \"{3}\")\'>{4}';
var displayTemplate = '';
if (this.ShowLoginName && this.ShowTitle) {
displayTemplate = baseDisplayTemplate + ' ({5})<br/>{6}</div>';
} else if (this.ShowLoginName) {
displayTemplate = baseDisplayTemplate + ' ({5})</div>';
} else if (this.ShowTitle) {
displayTemplate = baseDisplayTemplate + ' ({6})</div>';
} else {
displayTemplate = baseDisplayTemplate + '</div>';
}
if (results) {
...
if (results.length > 0) {
// if this function is not the callback from the last issued query then just ignore it. This is needed to ensure a matching between
// what the user entered and what is shown in the query feedback window
if (queryNumber < this._lastQueryID) {
return;
}
displayCount = results.length;
if (displayCount > this.MaxEntriesShown) {
displayCount = this.MaxEntriesShown;
}
for (var i = 0; i < displayCount; i++) {
var item = results[i];
var oldLoginName = item['Key'];
var loginName = oldLoginName.replace(/\\/g, '\\\\');
var displayLoginName = oldLoginName.split('|')[1].replace(/\\/g, '\\');
var displayName = item['DisplayText'];
var title = item['EntityData']['Title'];
var email = item['EntityData']['Email'];
txtResults += this.Format(displayTemplate, this.InstanceName, loginName, this.HtmlEncode(displayName), email, displayName, displayLoginName, title);
}
var resultDisplay = '';
txtResults += '<div class=\'ms-emphasisBorder\' style=\'width: 400px; padding: 4px; border-left: none; border-bottom: none; border-right: none; cursor: default;\'>';
if (results.length == 1) {
resultDisplay = 'Showing {0} result';
if (typeof resultsSingle != 'undefined') {
resultDisplay = resultsSingle;
}
txtResults += this.Format(resultDisplay, results.length) + '</div>';
} else if (displayCount != results.length) {
resultDisplay = "Showing {0} of {1} results. <B>Please refine further<B/>";
if (typeof resultsTooMany != 'undefined') {
resultDisplay = resultsTooMany;
}
txtResults += this.Format(resultDisplay, displayCount, results.length) + '</div>';
} else {
resultDisplay = "Showing {0} results";
if (typeof resultsMany != 'undefined') {
resultDisplay = resultsMany;
}
txtResults += this.Format(resultDisplay, results.length) + '</div>';
}
this.PeoplePickerDisplay.html(txtResults);
//display the suggestion box
this.ShowSelectionBox();
}
else {
var searchbusy = '<div class=\'ms-emphasisBorder\' style=\'width: 400px; padding: 4px; border-left: none; border-bottom: none; border-right: none; cursor: default;\'>No results found</div>';
this.PeoplePickerDisplay.html(searchbusy);
//display the suggestion box
this.ShowSelectionBox();
}
}
else {
//hide the suggestion box since results are null
this.HideSelectionBox();
}
}
// Initialize
PeoplePicker.prototype.Initialize = function () {
var scriptUrl = "";
var scriptRevision = "";
$('script').each(function (i, el) {
if (el.src.toLowerCase().indexOf('peoplepickercontrol.js') > -1) {
scriptUrl = el.src;
scriptRevision = scriptUrl.substring(scriptUrl.indexOf('.js') + 3);
scriptUrl = scriptUrl.substring(0, scriptUrl.indexOf('.js'));
}
})
// Load translation files
var resourcesFile = scriptUrl + "_resources." + this.Language.substring(0, 2).toLowerCase() + ".js";
if (scriptRevision.length > 0) {
resourcesFile += scriptRevision;
}
this.LoadScript(resourcesFile, function () {
});
// is there data in the hidden control...if so show it
if (this.PeoplePickerData.val() !== undefined && this.PeoplePickerData.val().length > 0) {
// Deserialize JSON string into list of resolved users
this._ResolvedUsers = JSON.parse(this.PeoplePickerData.val());
// update the display of resolved users
this.PeoplePickerControl.html(this.ResolvedUsersToHtml());
}
var parent = this;
this.PeoplePickerEdit.keydown(function (event) {
var keynum = event.which;
//backspace
if (keynum == 8) {
//hide the suggestion box when backspace has been pressed
parent.HideSelectionBox();
// do we have text entered
var unvalidatedText = parent.PeoplePickerEdit.val();
if (unvalidatedText.length > 0) {
// delete the last entered character...meaning do nothing as this delete will happen as part of the keypress
}
else {
// are there resolved users, if not there's nothing to delete
if (parent._ResolvedUsers.length > 0) {
// remove the last added user
parent.PopResolvedUser();
// update the display
parent.PeoplePickerControl.html(parent.ResolvedUsersToHtml());
// focus back to input control
parent.PeoplePickerEdit.focus();
// Eat the backspace key
return false;
}
}
}
// An ascii character or a space has been pressed
else if (keynum >= 48 && keynum <= 90 || keynum == 32) {
// get the text entered before the keypress processing (so the last entered key is missing here)
var txt = parent.PeoplePickerEdit.val();
// keynum is not taking in account shift key and always results inthe uppercase value
if (event.shiftKey == false && keynum >= 65 && keynum <= 90) {
keynum += 32;
}
// Append the last entered character: since we're handling a keydown event this character has not yet been added hence the returned value misses the last character
txt += String.fromCharCode(keynum);
// we should have at least 1 character
if (txt.length > 0) {
var searchText = txt;
//ensure that MinimalCharactersBeforeSearching >= 1
if (parent.GetMinimalCharactersBeforeSearching() < 1) {
parent.SetMinimalCharactersBeforeSearching(1);
}
// only perform a query when we at least have two chars and we do not have a query running already
if (searchText.length >= parent.GetMinimalCharactersBeforeSearching()) {
resultDisplay = 'Searching...';
if (typeof resultsSearching != 'undefined') {
resultDisplay = resultsSearching;
}
var searchbusy = parent.Format('<div class=\'ms-emphasisBorder\' style=\'width: 400px; padding: 4px; border-left: none; border-bottom: none; border-right: none; cursor: default;\'>{0}</div>', resultDisplay);
parent.PeoplePickerDisplay.html(searchbusy);
//display the suggestion box
parent.ShowSelectionBox();
var query = new SP.UI.ApplicationPages.ClientPeoplePickerQueryParameters();
query.set_allowMultipleEntities(false);
query.set_maximumEntitySuggestions(2000);
query.set_principalType(parent.GetPrincipalType());
query.set_principalSource(15);
query.set_queryString(searchText);
var searchResult = SP.UI.ApplicationPages.ClientPeoplePickerWebServiceInterface.clientPeoplePickerSearchUser(parent.SharePointContext, query);
// update the global queryID variable so that we can correlate incoming delegate calls later on
parent._queryID = parent._queryID + 1;
var queryIDToPass = parent._queryID;
parent._lastQueryID = queryIDToPass;
// make the SharePoint request
parent.SharePointContext.executeQueryAsync(Function.createDelegate(this, function () { parent.QuerySuccess(queryIDToPass, searchResult); }),
Function.createDelegate(this, function (a, arguments) { parent.QueryFailure(queryIDToPass, arguments); }));
}
}
}
//tab or escape
else if (keynum == 9 || keynum == 27) {
//hide the suggestion box
parent.HideSelectionBox();
}
});
}
return PeoplePicker;
})();
CAMControl.PeoplePicker = PeoplePicker;
})(CAMControl || (CAMControl = {}));
Routeprovider:
.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/summary', {
templateUrl: 'PresenceSummary.html'
})
.when('/details/:workingDay', {
templateUrl: 'PresenceDetails.html'
})
.otherwise({ redirectTo: '/summary' });
}]);
在详细信息页面中,我将使用ng-click返回摘要页面。在我称之为的地方使用:
$location.path("/");
如果这很重要,按钮是另一个指令。
people-picker-directive的parentcontroller看起来像这样:
angular.module('presenceSummary', [])
.controller('presenceSummary', function ($scope, $location, hrDbService, hrUserService, hrUiControlService) {
...
});
答案 0 :(得分:1)
我找到了答案。该指令在dom准备好之前呈现。我的控件不可用。
我设置了超时。这篇文章给了我这个提示: http://blog.brunoscopelliti.com/run-a-directive-after-the-dom-has-finished-rendering
感谢您的帮助!