我有一个指令,负责向服务器请求数据并定义要在视图上显示的范围属性,该指令还具有创建新请求的功能以及是否响应了新请求,视图应该更新为新数据..但问题依赖于第二步,即更新视图
所以指令的构造如下:
app.directive("restBatch", ["restFactory", "utilityFactory", function($rest, $util){
function batch(uri, cb){
$rest.batch(uri)
.then(function(data){
cb(data);
});
};
return {
restrict: "A",
controller: ["$scope", function($scope){
//Create a new request and update the view with the new data
$scope.$search = function(data){
var uri = this.$uri + "?q" + $util.querySearch(data);
batch(uri, function(data){
$scope[this.$name] = $scope.$inline ? $util.group(data, $scope.$inline) : data;
});
}
}],
link: function(scope, element, attr){
var call = $util.parseAlias(attr.restBatch || scope.$data.$uri);
var uri = $util.resolveUri(call.name, attr.uid ? attr.uid : null);
var name = call.alias ? call.alias : "$batch";
scope.$inline = attr.restInline || null;
batch(uri, function(data){
scope[name] = scope.$inline ? $util.group(data, scope.$inline) : data;
scope.$uri = uri;
scope.$name = name;
});
}
}
}]);
link
上的所有内容都能正常运行,但问题发生在范围内的$search
上,视图未更新,即使我使用$apply()
,也会抛出$digest
错误。在$search
内记录范围会显示新数据,但视图未更新
如果需要视图模板来查看它是如何工作的,那么它是:
<div layout="column" layout-fill rest-batch rest-inline="3">
<div layout="row">
<div flex="5"></div>
<div flex="10" layout layout-align="center center">
<md-button class="md-warn md-raised" ui-sref="plans.create">Create</md-button>
</div>
<div flex="70" layout layout-align="center center">
<md-button ui-sref="plans">Plans</md-button>
<md-button ui-sref="plans.workouts">Workouts</md-button>
<md-button ui-sref="plans.meals">Meals</md-button>
</div>
<md-input-container flex class="search">
<label>Search Plans</label>
<input ng-model="searchText" ng-keyup="$search(searchText)">
</md-input-container>
</div>
<md-content flex ui-view="plans"></md-content>
</div>
更新 我将添加其他工厂和服务,以清除有关其在指令中使用的原因的所有问题
restFactory:
app.factory("restFactory", ["Restangular", "userService", "$q", "utilityFactory", function($rest, $user, $q, $util){
return {
batch: function(uri, params){
return $rest.all(params ? $util.parseParams(uri, params) : uri).getList({access_token: $user.getToken()});
},
detail: function(uri, id){
return $rest.one(uri, id).get({access_token: $user.getToken()});
},
severalDetail: function(uri){
var deferred = $q.defer();
var self = this;
var collection = [];
self.batch(uri).then(function(batch){
for (var i = 0; i < batch.length; i++) {
self.detail(uri, batch[i].id).then(function(data){
collection.push(data);
if(batch.length == collection.length){
deferred.resolve(collection);
}
});
}
});
return deferred.promise;
}
};
function parseUri(uri){
var regex = /\w+\/+\:+\w{2,}/g;
var matches = uri.match(regex);
matches.forEach(function(element, index){
var res = resolveResource(element);
if(res.resource == 'accounts'){
res.value = $user.getId();
}
});
}
function resolveResource(uri){
var arr = uri.split('/');
return {
resource: arr[0],
parameter: arr[1] || null
};
}
}]);
utilityFactory:
app.factory("utilityFactory", ["userService","$httpParamSerializerJQLike", function($user, $serial){
return {
group: function(content, inline){
var arrExt = [], index = 0;
if(content.length <= inline){
arrExt.push(content);
}
else{
for(i = 0; i <= content.length / inline; i++){
var arrTemp = [];
for(e = 1; e <= inline; e++){
if(content[index])
arrTemp.push(content[index]);
index++;
}
arrExt.push(arrTemp);
}
}
return arrExt;
},
parseAlias: function(exp){
var regex = /\s+as\s+/;
var obj = {
name: null,
alias: null
};
if(exp.match(regex)){
var data = exp.split(regex);
obj.alias = data[1];
obj.name = data[0];
}
else{
obj.name = exp;
}
return obj;
},
resolveUri: function(uri, id){
return uri.replace("@id", id || $user.getId());
},
parseParams: function(uri, params){
if(Object.prototype.toString.call(params) == "[object Object]"){
for(p in params){
var param = ":" + p;
uri.replace(param, params[p]);
}
}
return this.resolveUri(uri, params["@id"] || null);
},
querySearch: function(query){
return $serial(query);
}
}
}])
userService
app.service('userService', ['$rootScope', '$resource', '$window', '$cookies', 'Restangular', function($rootScope, $resource, $window, $cookies, $rest){
var self = this;
var apiHost = 'http://' + $window.location.hostname + ':3000';
/**
* User cache data to access globally
*/
self._data = {
id: null,
username: null,
email: null,
accessToken: null
};
/**
* All messages for user
*/
self._messages = {
login: {
error: {
credentials: 'name/email or password is invalid.',
accessToken: 'Provided token is invalid'
}
},
register: {
error: {
credentials: 'Username is already taken.'
},
success: {
credentials: 'User successfully created.'
}
},
main: {
error: {
connection: 'Service error, please try again later.',
}
}
};
self.isGuest = true;
/**
* Populate all necesary data user cache
* @param {Object} data The data to populate
*/
self._populate = function(data){
self._data.id = data.id;
self._data.username = data.username;
self._data.email = data.user_email;
self._accessToken = data.access_token;
};
/**
* Get the name of the user from user cache
* @return {String} The name of the user
*/
self.getName = function(){
return self._data.username;
};
/**
* Get the id of the user from user cache
* @return {Integer} The id of the user
*/
self.getId = function(){
return self._data.id;
};
/**
* Get the access token of the current logged user
* @return {String} Access token data
*/
self.getToken = function(){
return self._accessToken;
};
/**
* Register a user with the given credentials and returns the result
* @param {Object} credentials The credentials of the user
* @param {Function} callback The callback to run when tries to register
*/
self.register = function(credentials, callback){
$rest.all('accounts').post(credentials).then(function(data){
if(!data.passed){
callback({
passed: false,
message: self._messages.register.error.credentials
});
}
else{
self.login(credentials, function(){});
callback({
passed: true,
message: self._messages.register.success.credentials
});
}
});
};
/**
* Login the user with the given credentials and returns the result
* @param {Object} credentials The credentials to send to server and login
* @param {Function} callback The result of the server response
*/
self.login = function(credentials, callback){
$rest.all('accounts/login').post(credentials).then(function(data){
var result = {
success: false,
error: null
};
if(typeof data.id !== "undefined"){
self._populate(data);
if(credentials.remember){
self.storeAccessToken(data.access_token);
}
self.isGuest = false;
result.success = true;
$rootScope.$broadcast('userLogged', (typeof credentials.token !== 'undefined'));
}
else{
// Find if token was provided
for(k in credentials){
// Return error message for token provided
if(k == 'token'){
result.error = self._messages.login.error.accessToken;
break;
}
}
// Return error message for invalid credentials
if(!result.error)
result.error = self._messages.login.error.credentials;
}
callback(result.success, result.error);
})
.catch(function(err){
callback(false, self._messages.main.error.connection);
});
};
/**
* Logs out the user by removing cache data and cookie data
* @return {boolean} Either if operation was successfull or not
*/
self.logout = function(){
if(!self.isGuest){
self.removeToken();
for(prop in self._data)
self._data[prop] = null;
return true;
}
return false;
}
/**
* Store the access token of the user for auto re-login
* @param {String} token The hash of the token to store in cookie
* @return {void}
*/
self.storeAccessToken = function(token){
var time = new Date();
$cookies.put('accessToken', token, {
expires: new Date(time.getFullYear(), time.getMonth() + 1, time.getDate())
});
};
/**
* Remove the token stored in cookies if exists
* @return {void}
*/
self.removeToken = function(){
if(self.hasToken)
$cookies.remove('accessToken');
};
/**
* Logs the user with a token stored in cookies
* @param {function}
* @return {void}
*/
self.logByToken = function(callback){
if(self.hasToken()){
self.login({token: $cookies.get('accessToken')}, function(success, error){
callback(success, error);
});
}
else
callback(false, self._messages.login.error.accessToken);
}
/**
* Check if client has token to access
* @return {void}
*/
self.hasToken = function(){
if($cookies.get('accessToken'))
return true;
return false;
}
}]);