How can I include an angular module in an existing module? - angularjs

So I've defined a custom section in Umbraco 7:
namespace NZ_Realty_Ltd.CRM
{
[Application("crm", "CRM", "trayuser", 8)]
public class CrmSection : IApplication { }
}
It shows up, no worries here. But it needs a view and controller. So I made a start on the controller:
angular.module("umbraco").controller("CrmController",
function ($scope, $http) {
$http.get('backoffice/crm/ContactApi/GetAll').success(function (data) {
$scope.contacts = data;
});
});
Again, no problem. My data is being read from a C# CRUD api and being sent back to the view. But I want to paginate these results. So I'm trying to use this custom directive to do it: https://github.com/michaelbromley/angularUtils/tree/master/src/directives/pagination
Here's my html view:
<div ng-controller="CrmController">
<umb-pane>
<p>Page {{currentPage}} / {{totalPages}}</p>
<p>Showing {{pageSize}} items per page:</p>
<ul class="contacts">
<li dir-paginate="contact in contacts | itemsPerPage: 10">
<span>{{contact.name}}</span>
<p>{{contact.bio}}</p>
</li>
</ul>
<dir-pagination-controls></dir-pagination-controls>
</umb-pane>
</div>
The problem is none of these expressions are showing up (they are all blank). I've missed the step of how to include the pagination module. Actually I've been stuck on it for hours. I've tried everything from:
angular.module("umbraco", ['angularUtils.directives.dirPagination']).controller("CrmController",
function ($scope, $http) {
$http.get('backoffice/crm/ContactApi/GetAll').success(function (data) {
$scope.contacts = data;
});
});
... To just including the directive javascript file from <script> tags. But I really have no idea what I'm doing and don't understand the module syntax well enough (I've read through the pagination demo so many times but it just seems SO different to working with the umbraco angularjs app). I saw in the docs that including the second parameter means you're making a new module. But what is the relevance of the information inside the [] on the second parameter? And why would I be creating a new module? Can't I just include the existing directive?
EDIT: This is the closest in my mind to what should work. But I get no contacts listed and no pagination controls showing. My idea with it is to load the pagination module (and thus directive), and then create my controller as normal to avoid conflicts and load order stuff, but also adding default values in the controller as in the example here: http://plnkr.co/edit/Wtkv71LIqUR4OhzhgpqL?p=preview
angular.module("angularUtils.directives.dirPagination");
angular.module("umbraco").controller("CrmController",
function ($scope, $http) {
$http.get('backoffice/crm/ContactApi/GetAll').success(function (data) {
$scope.contacts = data;
$scope.currentPage = 1;
$scope.pageSize = 10;
});
});

You could add this before your controller starts:
app.requires.push('angularUtils.directives.dirPagination');
Read more at:
https://our.umbraco.org/forum/umbraco-7/developing-umbraco-7-packages/47905-Including-an-angular-module

Related

Share Variable between controllers of different routes - AngularJS

I'm new on Angularjs and I'm trying to build my first application. Let's say I have to routes that loads two different views:
127.0.0.1:8080/site
127.0.0.1:8080/site_details
Maybe having two different routes is not the right procedure but that it is another problem.
I have two controllers:
Controller 1:
app.controller('controller_1', function($scope, $http, user) {
user.set('Test Example')
});
and Controller 2
app.controller('controller_2', function($scope, $http, user) {
var xxx = user.get()
});
What I want to do is to share data between these two controllers. To do that I did a service in this way:
app.factory('user', function($rootScope) {
var savedData = {}
function set(data) {
savedData = data;
}
function get() {
return savedData;
}
return {
set: set,
get: get
}
});
By looking around it seems that having a service built like this should solve the problem. However, what I obtain with the function get() in controller 2 is always an empty return.
By setting breakpoints I can see that both set() and get() functions enters in their respective function in the service.
Is this a correct procedure to share data between controllers belonging of different routes?
EDIT1
The two views are built in the same ways and the are loaded inside ng-view
<html ng-app="app" ng-controller='controller_1'>
CONTROLLER 1
</html>
First, sharing data between a service is a correct approach.
In your case, you need to ensure the order of getting data is after setting data.
Using a $timeout is not a good approach, i think there should be another way, it depend on your detail code.
If your data is set after some event, you just need to pay attention to the order sequence like 'get after data has been set'
If you have to set data in initialization of controller_1, and controller_2 is sibling of controller_1, you can put the initialization logic of user data before bother controller_1 and controller_2 is entered.
I think you had giving factory reference to both html where first and
second controller you given have. in that case you have to give factory referee to main single page where your also loading sub pages(where you kept ng-view)
The problem occurs because, controller_1 was not created before the creation of controller_2. You can modify the controller_2 to introduce some delay using $timeout:
app.controller('controller_2', function($scope, $timeout, $http, user) {
// The time out is added to check your code working,
// You can replace the code or can use, its up to your requirement
$timeout(function(){
var xxx = user.get();
console.log(xxx);
}, 500);
});
Using $timeout will allow some time for creation of controller_1.
Also instantiate the controller_2:
<html ng-app="app">
<body>
........
<div ng-controller='controller_1'>
<div ng-controller='controller_2'>
</div>
</div>
</body>
</html>
You can use rootscope like below.
app.controller('controller_1', function($scope, $http, $rootScope) {
$rootScope.UserInfo ="Test Example";
});
app.controller('controller_2', function($scope, $http, $rootScope) {
var xxx = $rootScope.UserInfo;
console.log(xxx)
});

Angularjs passing value from one module to another module

I am using Angularjs for my application and I would like to transfer the data between the modules, tried all the ways, but the data is not getting passed. Please have a look at my code here.
I have a page called departments in frontapp module, while clicking on particular department, I want to pass the department Id to departmentapp module where each department will have its own page.
<h5>{{school.schoolName}}</h5>
<ul data-ng-repeat="department in school.departments">
<li data-ng-click="getdepartmentBydepartmentId(department.departmentId);setDepartmentId(department.departmentId)">
{{department.departName}}
</li>
The code of Controller
$scope.getdepartmentBydepartmentId = function(departmentId){
SchoolService.getdepartmentBydepartmentId(departmentId).then(function(response){
$scope.department= response.data;
});
}
$scope.getDepartmentId=function(){
SchoolService.getDepartmentId().then(function(response){
$scope.departmentId = response.data;
});
}
$scope.setDepartmentId=function(departmentId)
{
//Some authentication code...
alert(departmentId);
SchoolService.setDepartmentId(departmentId);
alert(SchoolService.getDepartmentId());
//Here I want to pass the username to homectrl.js
window.location.href="./department"
}
The code in services js file
obj.getDepartmentId=function(){
return departmentIDSchool;
};
obj.setDepartmentId=function(departmentID){
// var departmentID = null;
departmentIDSchool = departmentID;
};
obj.getdepartmentBydepartmentId=function(departmentId){
return $http.post('getdepartmentBydepartmentId?departmentId='+departmentId);
};
In departmentapp module - departmentcontroller.js
app.controller("SchoolController", SchoolController);
app.service("SchoolService",SchoolService);
angular.module("frontApp").SchoolService.getDepartmentId().then(function(response){
$scope.departmentId = response.data;
alert($scope.departmentId);
});
But here departmentId I am not getting. After going through many questions at stack overflow, I come to know that in service layer we need to have getter and setter, but even after placing that it is not working. Can some one help me here?
You can pass a variable like that easily in the router
$routeProvider.when('/:primaryNav/:secondaryNav', {
templateUrl: 'resources/angular/templates/nav/'+$routeParams.primaryNav+'/'+$routeParams.secondaryNav+'.html'
});
See the $routeParams in templateUrl, you can also inject it into your controller to get the params.

Setting up dynamic $http.get request (Angular)

I want to know how to dynamically change an $http call so that the $http request URL differs based on the element that is clicked in an ng-repeat list. But I'm stuck.
Currently, I have an ng-repeat set up on my index page:
<div ng-repeat="team in nt.getStandings">
<h2>{{team.team_name}}</h2>
<p>Team ID = {{team.team_id}}</p>
</div>
The variable getStandings is taken from an API via an $http call. Like so:
In StandingsService
return $http.get(
'http://api.com/standings/1005?Authorization=xxxx'
)
.success(function(data) {
return data;
})
.error(function(err) {
return err;
});
And then StandingsService is attached to the getStandings variable in my controller.
"1005" is a property which calls a specific array, in this case a particular sporting competition, from an array of competitions.
So, on my index page I'm using ng-repeat to list all teams within that competition.
As you can see on the html above, I have linked each team so that it dynamically generates a URL which appends the team_id to the end, which using $routeParams I define as the variable whichTeam.
Team Details Page
<h1>Dynamic Team ID = {{whichTeam}}</h1>
This works fine, the team ID is generated dynamically according the team that is clicked.
Just like 'StandingsService' above, I have another service called 'TeamService' which makes an $http request to pull team data. Currently though it is set up statically to make a call to one individual team - I want to make the service take in the whichTeam variable so that the call changes depending on which team was clicked.
This is the static team $http request (I've broken the URL apart and concatenated to make it clearer):
return $http.get(
'http://api.com/team/' + '16110' + '?Authorization=xxxx'
)
I want the 16110 part, which refers to ONE team, to be a the whichTeam variable, allowing it to pull in the correct individual team data, but I don't know how to write this (or indeed if it's possible).
I hope I've been clear - happy to clarify further if needed. Thanks in advance.
Make a factory:
app.factory("DataService", ["$http", function($http) {
return {
getTeamDetailsById: function(teamId) {
return $http.get('path/to/api' + teamId + '?Auth=xxxx')
}
};
}]);
Use it in a controller:
app.controller("MainCtrl", ["$scope", "DataService", function($scope, DataService) {
$scope.teamDetails = {};
$scope.getTeamDetailsById = function(event, teamId) {
//prevent click navigation
event.preventDefault();
//call factory service
DataService.getTeamDetailsById(teamId).then(function(response) {
//success callback
$scope.teamDetails = response.data;
}, function(response) {
//an error has occurred
});
}
}]);
In the ng-repeat element:
<div ng-repeat="team in teams">
<a href ng-click="getTeamDetailsById($event, team.team_id)">{{team.team_name}}</a>
</div>
The above assumes you have only one state and are storing in only one controller. If you want to use different states usving $stateProvider, then you'd have to use parameters, by making use of ui-sref and passing in team Id.
If indeed you are using $states and parameters, do this:
<a href ng-click="goToState($event, team.team_id)">{{ team.team_name }}</a>
$scope.goToState = function(e, teamId) {
$state.go("teamDetailsState", { "teamId": teamId });
}

How to Lazyload controller and template in one request using angular-ui-router

I'm trying to lazy-load components. The component is an html fragment with an embedded script tag that contains the controller.
<script>
... controller code .....
</script>
<div>
... template ....
</div>
The fragment is generated in ONE html request so I cannot use templateUrl AND componentURL in the state definition.
I have tried to use the templateProvider to get the component, than extract the script code for the function and register it using a reference to the controllerProvider.
I'm sure there must be a better way to do this than the ugly solution I have come up with. I make a global reference to the controllerpovider, then I read the component thru the templateProvide using a getComponent service. Next I extract the script and evaluate it, which also registers the controller.
See the plunker for the way I'm trying to solve this.
.factory('getComponent', function($http, $q) {
return function (params) {
var d = $q.defer();
// optional parameters
$http.get('myComponent.html').then(function (html) {
// the component contains a script tag and the rest of the template.
// the script tags contain the controller code.
// first i extract the two parts
var parser = new window.DOMParser();
var doc = parser.parseFromString(html.data, 'text/html');
var script = doc.querySelector('script');
// Here is my problem. I now need to instantiate and register the controller.
// It is now done using an eval which of cours is not the way to go
eval(script.textContent);
// return the htm which contains the template
var html = doc.querySelector('body').innerHTML;
d.resolve(html);
});
return d.promise;
};
})
Maybe it could be done using a templateProvider AND a controllerProvider but I'm not sure how to resolve both with one http request. Any help / ideas would be greatly appreciated.
Here's a working plunkr
You do not have access to $controllerProvider at runtime, so you cannot register a named controller.
UI-Router doesn't require named/registered controller functions. { controller: function() {} } is perfectly valid in a state definition
However, if you need the controller to be registered, you could use a tool like ocLazyLoad to register it.
UI-Router links the controller to the template, so there's no need for ng-controllersprinkled in the html.
Here's how I hacked this together. getController factory now keeps a cache of "promises for components". I retained your eval and template parsing to split the response into the two pieces. I resolved the promise with an object containing the ctrl/template.
component factory
.factory('getComponent', function($http, $q) {
var components = {};
return function (name, params) {
if (components[name])
return components[name];
return components[name] = $http.get(name + '.html').then(extractComponent);
function extractComponent(html) {
var parser = new window.DOMParser();
var doc = parser.parseFromString(html.data, 'text/html');
var script = doc.querySelector('script');
// returns a function from the <script> tag
var ctrl = eval(script.textContent);
// return the htm which contains the template
var tpl = doc.querySelector('body').innerHTML;
// resolve the promise with this "component"
return {ctrl: ctrl, tpl: tpl};
}
};
})
myComponent.html
<script>
var controller = function($scope) {
$scope.sayHi = function() {
alert(' Hi from My controller')
}
};
// This statement is assigned to a variable after the eval()
controller;
</script>
// No ng-controller
<div>
Lazyloaded template with controller in one pass.
<button ng-click="sayHi()">Click me to see that the controller actually works.</button>
</div>
In the state definition:
I created a resolve called 'component'. That resolve asks the getComponent factory to fetch the component called 'myComponent' (hint: 'myComponent' could be a route parameter)
When it's ready, the resolve is exposed to the UI-Router state subtree.
The state is activated
The view is initialized
The controller provider and template provider inject the resolved component, and return the controller/template to UI-Router.
Smell test
I should mention that fetching a controller and template in a single html file and manually parsing smells wrong to me.
Some variations you could pursue:
Improve the getComponent factory; Split the component into html/js and fetch each separately. Use $q.all to wait for both fetches.
Use ocLazyLoad to register the controller globally with $controllerProvider
Fetch and store the template in the $templateCache.

Integrating ASP.NET Webforms, WebAPI and AngularJS

I'm trying to get my first ASP.NET Webforms and AngularJS app up and running, but I'm struggling...
I created a blank, new ASP.NET 4.5.1 webforms app, and included WebAPI into the mix. I created a sample page for my list of customers, and a standard, EF6-based WebAPI CustomerController : ApiController with all the usual CRUD methods. I tested that WebAPI using Fiddler and low and behold - I get my 8 customers back from my database.
Getting this into AngularJS however has been a bit of a unsuccessful and highly frustrating experience ....
I included AngularJS from NuGet and that seems to have worked - no errors shown or anything, a pile of angular*.js files dumped into my Scripts folder.
I created a basic CustomerList.aspx page based on a master page which includes the <html lang="en" ng-app="TestAngular"> tag.
In order to get the data from the WebAPI service, I created my Angular module, and created a model inside the $scope, and created a service to fetch the data from the WebAPI:
Inside app.js:
var testModule = angular.module('TestAngular', [ ]);
testModule.controller('clientController', function ($scope, clientService) {
$scope.model = [];
clientService.getAllClients(function(results) {
$scope.model.clients = results;
});
$scope.model.clientCount = $scope.model.clients.count;
});
testModule.factory('clientService', function ($http) {
var srv = {};
srv._baseUrl = 'http://localhost:56313';
// Public API
return {
getAllClients: function(callback) {
return $http.get(srv._baseUrl + '/api/Customer').success(callback);
}
};
});
From what limited Javascript understanding I have, this should define a clientService (the testModule.factory() call) that calls my WebAPI URL, gets the JSON back, and the callback function then stuffs those customers retrieved into the $scope.model.clients property, and the $scope.model.clientCount should also be calculated.
My ASPX page looks something like this:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="CustomerList.aspx.cs" Inherits="TestAngular.CustomerList" MasterPageFile="~/Site.Master" %>
<asp:Content runat="server" ID="content" ContentPlaceHolderID="MainContent">
<h2>My customers</h2>
<div class="panel panel-default" data-ng-controller="clientController">
We have a total of {{ model.clientCount }} clients in our database table ...
<div class="list-group" data-ng-repeat="client in model.clients">
<div class="list-group-item">
<span>{{ client.Name }}</span><br/>
<span>{{ client.Name2 }}</span><br/>
</div>
</div>
</div>
</asp:Content>
So the <div> with the data-ng-controller should "connect" that DIV with the AngularJS controller, should load the customers from the WebAPI call, and contain them in the model, which would then be rendered to the ASPX page using the data binding syntax ({{ client.Name }} etc.)
Trouble is: the call to the WebAPI happens and the correct 8 customers are returned, however, when I debug into the Javascript code, the clientCount is always undefined and the ASPX page only shows two empty rows which probably would correspond to customers that have been retrieved - but why only 2, not 8 ??
I'm totally lost and stuck - can anyone spot what I'm doing wrong, what I'm missing here??
You are definately on the right track. At the moment, the problem is down to the clientService getAllClients method.
You should return the promise and then the data will chain through to the controller:
getAllClients: function(callback) {
return $http.get(srv._baseUrl + '/api/Customer').success(callback);
}
You may also want to take a look at the count line:
$scope.model.clientCount = $scope.model.clients.count;
Before the promise is resolved (and the callback is invoked), $scope.model.clients will be undefined. So, I'd expect this line to fail. Also, to get the count of an array, you need length.
You should set the clientCount inside of the callback:
clientService.getAllClients(function(results) {
$scope.model.clients = results;
$scope.model.clientCount = $scope.model.clients.length;
});
Edit:
Typically, it is favoured to use the promise returned by $http. So, the controller would slightly change to:
clientService.getAllClients().then(function(response) {
$scope.model.clients = response.results;
$scope.model.clientCount = response.results.length;
});
And then the service would change to:
getAllClients: function() {
return $http.get(srv._baseUrl + '/api/Customer');
}
Angular uses promises from $q instead of callbacks (for most apis). They make chaining and exception handling much easier.
Also, since, in this case, you know you are handling a promise from $http, you can use the success method in the controller as well:
clientService.getAllClients().success(function(results) {
$scope.model.clients = results;
$scope.model.clientCount = results.length;
});
success unwraps the response and sends only the body through to the callback.

Resources