Monday, December 28, 2015

Post ngRepeat Code

all those questions,
How to run function after ngRepeat is done
Calling a function when ng-repeat has finished
ng-repeat finish event

all the answers in stackoverflow goes around
<ul>
    <li ng-repeat="item in items" 
        ng-init="$last ? doSomething() : angular.noop()">{{item}}</li>
</ul>
which are nice and are a real solution, while each answer eventually give the same solution a bit more generic.

one blog puts it quite better, while also enabling to choose the digest cycle stage, with the exception that you better use $parse instead of $eval:
http://www.bennadel.com/blog/2592-hooking-into-the-complete-event-of-an-ngrepeat-loop-in-angularjs.htm

but in the end we usually just do some simple JS or JQ code or plugin, and i think that the real solution here is a simple setTimeout since the setTimeout function gets pushed to the end of the queue of the browser, its always right after everything is done in angular, usually ngReapet which continues after its parents postLinking function

angular.module('myApp', [])
.directive('pluginNameOrWhatever', function() {
  return function(scope, element, attrs) {        
    setTimeout(function doWork(){
      //jquery code and plugins
    }, 0);        
  };
});

for whoever wondering that in that case why not to use $timeout, its that it causes another digest cycle that is completely unnecessary

Tuesday, November 17, 2015

Create Visual Studio project for Sharepoint

Many time we, Developers, gets a dev environment with a SP farm and need to get to work.

it usually comes in 2 stages, 1st is to create a new WebApplication up to having an actual site running, and that is covered here.

this is the 2nd part where we create a new Visual Studio project for Sharepoint and connect it to our brand new site :).

1. Open Visual Studio (da...)

2. Click "New Project", enter "sharepoint" in the search box, and choose "SharePoint 2013 - Empty Project"

3. in the opened dialog box, enter the domain url for your new site, and choose "Farm Solution"

4. Create new item

5. in the search box enter "webpart" to see available webparts templates, in this tutorial we will add a visual webpart. notice that in SP2010 the resutls will be a bit different. BTW, i prefer the UC/SmartPart template like in SP2010 and still use it since its much easier to change an .ascx file, while with this template even the ".ascx" is actually managed code, meaning you need to rebuild after every change.


6. lets put an asp:label and set the text to "Good Morning Dear!"


7. add the webpart to a feature


8. deploy the solution. VS gives you a shortcut, instead of creating a .wsp file, powershells, ect. ect.

9. when this is all over, go to a page in the site and edit it, make sure its a layout with a webpart-zone, and click "Add a Web Part"

10. Choose your brand new WP form the gallery

11. the end :)


12. bonus
       a. if you are writing code you should always ask VS to auto-copy your .dll's to the gac/bin
       b. sometimes your feature fails to deploy, so you can simple ask VS to Force-Install it



GOOD LUCK!









Create New Sharepoint Web Application all the way to the site tutorial

Many time we, Developers, gets a dev environment with a SP farm and need to get to work.

it usually comes in 2 stages, 1st create a new WebApplication up to having an actual site running, and thats whats this post is about.
2dn part here.

1. Open Central Admin

2. Go to "Manage Web Applications"

3. Click in the ribbon on "New" to create a new WebApplication

4. Fill the following

and when you scroll down also


    and after a long wait...
5. Click "Create Site Collection:


so that you get this window, and fill the relevant inputs



6. while this is going, you'll need to teach your local DNS the address for you new site, so open the "Run" window by pressing the window key + R and input "drivers"

7. open the "etc" folder

8. edit the "hosts" file. note that in some versions of Window you must either open "notepad" as administrator or copy the file outside this directory, edit it, and copy it back.

9. Add the new domain as 127.0.0.1 if you are going browse it in the local machine, or get the right IP for another machine.

10. back to the central admin, there will be a "created" screen, with the new domain url


11. Click it and get to work :)



GOOD LUCK!











Thursday, September 3, 2015

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.