sharepoint 2013 and angular js - development tutorial
so we want to just do an SPA with angular for the "front end" site, and leave SP as a CMS
well unless you put he main html file in the root, as in <server>\index.html, you will get a download everytime you try to browse to the file. this is a security feature and can be canceled with simple PS from here https://gist.github.com/wpsmith/484eb67652b3c928940d. the example is about html files.
next thing, SP-wise, is getting your data. there you have 2 options, the "regular" rest api and the search rest api. the search, for getting data, is much faster, but you need to activate the search.
url for search will always be like this
/_api/search/query?querytext=<your query>&selectproperties='<Title,ListItemID, ect.>'
notice that in the search you dont have the standard "Id" property but ListItemID.
to map this enormous object to something normal, you can create a simple mapping function
var toNormalItem = function (cells) { var normalItem = {}; angular.forEach(cells, function (singleCell) { this[singleCell.Key] = singleCell.Value; }, normalItem);
return normalItem; };
and just call it in the success
$http.get(url,
{ headers: { "accept": "application/json; odata=verbose" } })
.success(function (data, status, headers, config) {
angular.forEach(
data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results,
function (searchItem) {
this.push(me.toNormalItem(searchItem.Cells.results));
}, myNormalArray);
}).error(function (data, status, headers, config) {
alert("AJAX failed!");
});;
on the way I showed you the MUST HEADER of accept, if you want json as a response.
when you want to call ajax to your app, always make the requests in services, initiating an empty array, and connecting it to the view via a controller
it will go like this
app.service('svc', function($http, $log){
var self = this;
self.arr = [];
//http...
});
app.controller('ctrl', function(svc, $log){
this.svc = svc; //or $scope.svc = svc; for the pre 1.4 users
});
ng-repeat="x in <ctrl allias if no $scope>svc.arr"
if you want to use the direct rest the only thing that changes here is the url structure
/_api/lists/getbytitle('" + encodeURIComponent(listTitle) + "')/items?$select=Title,Id,...
and the response object is simpler, just data.d.results and each item has a property by its name (item.Title ect.)
POST
when posting stiff, as is edit / update / add / delete stuff become a bit more complex.
1st thing is that you need to authenticate to SP, and you do this by asking for the RequestDigest.
$http.post("/_api/contextinfo",
'',
{ headers: { "accept": "application/json; odata=verbose" } })
.success(function (data, status, headers, config) {
FormDigestValue = data.d.GetContextWebInformation.FormDigestValue;
}).error(function (data, status, headers, config) {
$log.error("get FormDigestValue ajax failed ");
});
NOTICE the 2nd parameter of '', in post the 2nd param is the body, and only the 3rd is the headers...
after that you have about 30 mins before needing the refresh the digest.
so finnaly, you want to create and item, you will get this error
"A type named 'SP.Data.DemoListItemas' could not be resolved by the model. When a model is available, each type name must resolve to a valid type."
to actually know your real type just put this in your browser
http://<server>/<web>/_api/Web/Lists/GetByTitle(<list title>)?$select=ListItemEntityTypeFullName
you will see a piece of xml with "<d:ListItemEntityTypeFullName>" in the end containing you real SP.DATA type, which can be long and ugly sometimes.
now to build the request body
var requestBody = {
'__metadata': { 'type': 'SP.Data.<something>' },
'Title': "test",
'myField': "test"
};
var jsonBody = JSON.stringify(requestBody);
and your post...
$http.post(addPrivateAppUrl, requestBody,
{ headers: {
"X-RequestDigest": FormDigestValue, //instead of $("#__REQUESTDIGEST").val(),
"accept": "application/json;odata=verbose",
"content-type": "application/json;odata=verbose"
}
})
bonus
how to send lookup to sharepoint rest when creating item?
well, I cant steal the show, here, just read and find out that you need the ID of the lookup.
btw - for single lookup is just <lookupName>Id : integer;
and managed metadata fields?
also in this answer, you just send the ID with a WssId of -1.
well unless you put he main html file in the root, as in <server>\index.html, you will get a download everytime you try to browse to the file. this is a security feature and can be canceled with simple PS from here https://gist.github.com/wpsmith/484eb67652b3c928940d. the example is about html files.
next thing, SP-wise, is getting your data. there you have 2 options, the "regular" rest api and the search rest api. the search, for getting data, is much faster, but you need to activate the search.
url for search will always be like this
/_api/search/query?querytext=<your query>&selectproperties='<Title,ListItemID, ect.>'
notice that in the search you dont have the standard "Id" property but ListItemID.
to map this enormous object to something normal, you can create a simple mapping function
var toNormalItem = function (cells) { var normalItem = {}; angular.forEach(cells, function (singleCell) { this[singleCell.Key] = singleCell.Value; }, normalItem);
return normalItem; };
and just call it in the success
$http.get(url,
{ headers: { "accept": "application/json; odata=verbose" } })
.success(function (data, status, headers, config) {
angular.forEach(
data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results,
function (searchItem) {
this.push(me.toNormalItem(searchItem.Cells.results));
}, myNormalArray);
}).error(function (data, status, headers, config) {
alert("AJAX failed!");
});;
on the way I showed you the MUST HEADER of accept, if you want json as a response.
when you want to call ajax to your app, always make the requests in services, initiating an empty array, and connecting it to the view via a controller
it will go like this
app.service('svc', function($http, $log){
var self = this;
self.arr = [];
//http...
});
app.controller('ctrl', function(svc, $log){
this.svc = svc; //or $scope.svc = svc; for the pre 1.4 users
});
ng-repeat="x in <ctrl allias if no $scope>svc.arr"
if you want to use the direct rest the only thing that changes here is the url structure
/_api/lists/getbytitle('" + encodeURIComponent(listTitle) + "')/items?$select=Title,Id,...
and the response object is simpler, just data.d.results and each item has a property by its name (item.Title ect.)
POST
when posting stiff, as is edit / update / add / delete stuff become a bit more complex.
1st thing is that you need to authenticate to SP, and you do this by asking for the RequestDigest.
$http.post("/_api/contextinfo",
'',
{ headers: { "accept": "application/json; odata=verbose" } })
.success(function (data, status, headers, config) {
FormDigestValue = data.d.GetContextWebInformation.FormDigestValue;
}).error(function (data, status, headers, config) {
$log.error("get FormDigestValue ajax failed ");
});
NOTICE the 2nd parameter of '', in post the 2nd param is the body, and only the 3rd is the headers...
after that you have about 30 mins before needing the refresh the digest.
so finnaly, you want to create and item, you will get this error
"A type named 'SP.Data.DemoListItemas' could not be resolved by the model. When a model is available, each type name must resolve to a valid type."
to actually know your real type just put this in your browser
http://<server>/<web>/_api/Web/Lists/GetByTitle(<list title>)?$select=ListItemEntityTypeFullName
you will see a piece of xml with "<d:ListItemEntityTypeFullName>" in the end containing you real SP.DATA type, which can be long and ugly sometimes.
now to build the request body
var requestBody = {
'__metadata': { 'type': 'SP.Data.<something>' },
'Title': "test",
'myField': "test"
};
var jsonBody = JSON.stringify(requestBody);
and your post...
$http.post(addPrivateAppUrl, requestBody,
{ headers: {
"X-RequestDigest": FormDigestValue, //instead of $("#__REQUESTDIGEST").val(),
"accept": "application/json;odata=verbose",
"content-type": "application/json;odata=verbose"
}
})
bonus
how to send lookup to sharepoint rest when creating item?
well, I cant steal the show, here, just read and find out that you need the ID of the lookup.
btw - for single lookup is just <lookupName>Id : integer;
and managed metadata fields?
also in this answer, you just send the ID with a WssId of -1.
Thanks for sharing, nice post! Post really provice useful information!
ReplyDeleteAn Thái SÆ¡n chia sẻ trẻ sÆ¡ sinh nằm nôi Ä‘iện có tốt không hay võng Ä‘iện có tốt không và giải đáp cục Ä‘iện Ä‘Æ°a võng giá bao nhiêu cÅ©ng nhÆ° mua máy Ä‘Æ°a võng ở tphcm địa chỉ ở đâu uy tÃn.