AngularJs and two Controllers - angularjs

I am writing an application that is running AngularJs & Bootstrap on the frontend - and using angularjs' $http to make calls to an API...I have two controllers within one app -- the first controller displays the person's name and the second controller displays a list of performances that this person has seen...
<html lang="en" ng-app="myapp">
...
<div class="panel-body" ng-controller="Ctrl">
<div class="row">
<div class="col-sm-3" style="height: 50px;">
<div class="well well-sm">
First Name:
{{ user.fname || "empty" }}
</div>
</div>
<div class="col-sm-3" style="height: 50px;">
<div class="well well-sm">
Last Name:
{{ user.lname || "empty" }}
</div>
</div>
</div>
</div>
<div class="list-group" ng-controller="Ctrl2">
<div ng-repeat="obj in performances" >
<div class="list-group-item">
<h4 class="list-group-item-heading">
<span class="badge">
{{$index+1}}</span> {{ obj["Perf_name"] || "empty" }}</h4>
<p class="list-group-item-text">
Date: {{ obj["Perf_dt"] || "empty" }}<br />
Theater: {{ obj["Theater"] || "empty" }}<br />
</p>
</div>
</div>
</div>
...
</html>
In the JS file I have the following code
Declare my variables and module
var app = angular.module("myapp", ["xeditable"]);
var q = getUrlParameter("q"); // get the query param
var baseurl = "https://some.domain/service/getCRM";
var baseurl2 = "https://some.domain/service/getPerformances";
Create a service to be used by both controllers
app.service('crmService', function () {
this.id = 0;
this.getID = function () { return this.id };
this.setID = function (newid) { this.id = newid; };
});
Define Controller #1
app.controller('Ctrl', function ($scope, $http, crmService) {
$http({
method: 'GET',
url: baseurl,
respondType: 'json',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
params: {
phone_no: q
}
}).then(function successCallback(response) {
$scope.user = {
fname: response.data[0]["FirstName"],
lname: response.data[0]["LastName"]
}
crmService.setID(response.data[0]["CRM_ID"]);
console.log("*** OUTPUT LINE 1: " + crmService.getID());
}, function errorCallback(response) {
console.log("**ERROR");
console.log(response);
});
});
Define Controller #2
app.controller('Ctrl2', function ($scope, $http, crmService) {
console.log("*** OUTPUT LINE 2: " + crmService.getID());
var promise = $http.get(baseurl2 + "?crm_id=" + crmService.getID());
promise.then(
function (response) {
$scope.performances = response.data;
});
});
Console output
*** OUTPUT LINE 2: 0
*** OUTPUT LINE 1: 123456
Questions:
Why is the second controller outputting before the first, I assume it is because AngularJs by default executes its code asynchronously, and if that's the case, why did the "promise" not work?
What I need to happen is, I make my GET call in the first controller block and assign the customer ID using my service...I then go on to the second controller block and using the customer ID, from my service (which was set within the first controller block) - get the customer's performances
So I have to first controller working fine, but the second controller keeps thinking that the customer ID is 0 - when it should be 123456
What am I missing?
FYI: when I hard code an ID for the second controller to use, I get actual performance records back - so I know everything will work once I figure out how to share values between my controllers

Why is the second controller outputting before the first
Because the second controller logs the ID as soon as it's instanciated, whereas the first one logs it once it has received, asynchronously, a response to an HTTP request. The promise did work, since the output is logged in the console.
What I need to happen is, I make my GET call in the first controller block and assign the customer ID using my service...I then go on to the second controller block and using the customer ID, from my service
You're doing your job more complex than it should be: using a single controller would be much easier here, since the model of the second one depends on what the first one does.
You could broadcast an event from the first controller to signal that the ID has been set, and listen to the event in the second one to get the data at tis time.
app.controller('Ctrl', function ($scope, $http, $rootScope, crmService) {
$http({
...
}).then(function successCallback(response) {
...
crmService.setID(response.data[0]["CRM_ID"]);
console.log("*** OUTPUT LINE 1: " + crmService.getID());
$rootScope.$broadcast('crmIdChanged');
};
});
app.controller('Ctrl2', function ($scope, $http, crmService) {
function getPerformances() {
var id = crmService.getID();
if (id != 0) {
var promise = $http.get(baseurl2 + "?crm_id=" + crmService.getID());
promise.then(
function (response) {
$scope.performances = response.data;
});
}
}
getPerformances();
$scope.$on('crmIdChanged', getPerformances);
});
You could even broadcast the even from the service itself.

Related

html data get from $http GET is not showing properly in Angular js..?

I have defined a controller like this :
app.controller("home", function ($scope, $http, $common) {
$http({
method: "GET",
url: '/posts/loadData'
}).then(function (response) {
//console.clear()
if (typeof response.data.posts != 'undefined') {
console.log(response.data.posts);
$scope.posts = $common.arrangePosts(response.data.posts);
}
});
})
and a service to arrange data :
app.service('$common', function ($timeout, $sce, $httpParamSerializerJQLike) {
var that = this;
this.arrangePosts = function (rawPosts) {
var posts = [];
$.each(rawPosts, function (key, value) {
posts.push({
postId: value.postId,
postLink: '/post/' + that.cleanString(value.title) + '/' + value.postId,
title: value.title,
summary: $sce.trustAsHtml(value.summary)
});
});
return posts;
}
});
using values in html like this :
<div class="widget fullwidth post-single">
<h4 class="widget-title">Latest</h4>
<div class="widget-content">
<ul>
<li ng-repeat="post in posts">
<h4 class="list-title">{{post.title}}</h4>
{{post.summary}}
</li>
</ul>
</div>
</div>
Data coming from server in JSON form :
Object { postId="4", title="asdf", summary="<p>asdf</p>"}
but all the html tags are printing on my page as it is (like a text) in summary.
In many SO posts people suggested to use $sce.trustAsHtml but its not working for me. Please suggest anyway to solve my problem.
Any help will be appreciated..!!
have you tried this?
<div ng-bind-html='post.summary'></div>
You could solve this over a directive. Did you know, that you can use JQuery Lite inside AngularJS to manipulate the DOM?
Here a quick example:
angular.module("PostsDirective",[])
.directive("posts", function($sce){
return {
link: function($scope, $element, $attrs){
//the HTML you want to show
var post = "<div>hello world</div>";
var posts = [post,post,post,post];
//iterating through the list (_.each is a function from underscore.js)
_.each(posts, function(element){
//if you want to save the trusted html string in a var you can do this with getTrustedHtml
//see the docs
var safeHtml = $sce.getTrustedHtml($sce.trustAsHtml(element));
//and here you can use JQuery Lite. It appends the html string to the DOM
//$element refers to the directive element in the DOM
$element.append(safeHtml);
});
}
};
});
And the html
<posts></posts>
This also pretty nice for the readability for your HTML code. And you can use it everywhere on your page.
BTW:
As i can see, you get the HTML elements directly from a REST-Service. Why don't you get just the data and insert it into the ng-repeat? If you transfer all the HTML you get a pretty high overhead if you have loads of data.

angular ng-repeat to always show even on empty object

Hi I want to post item to server, and with each successful addition, automatically add it to DOM with ng-repeat
<div class="" ng-repeat="book in books" >
<div id="eachBook">{{book.title}}</div>
</div>
to POST the data and also to upload an image file, I use Jquery ajax, and $state.go(".") to reload the current page:
var fd = new FormData();
fd.append("file", bookImage);
$http({
method: 'POST',
url: "/someurl,
data: fd,
headers: {
'Content-Type': undefined
}
}).success(function(Image){
var book_obj = {
bookTitle: bookTitle,
bookImage: Image._id
};
$http.post("url to owner book", book_obj)
.success(function(data){
$scope.bookImage = data.bookImage;
$timeout(function(){
alert("success", "successfully added your book");
$state.transitionTo('book', {}, { reload: true });
},2000);
})
})
The problem is with first addition, the DOM is still empty, and even though I use $state to reload the page, it still not working for the first addition. In the end I need to refresh the page manually by clicking refresh.
after the first addition, it works fine. With each book added, it automatically added to DOM..
Any idea how to automatically start the first one without manually rendering the page? using $timeout to delay the refresh has no effect.
Is it not just a simple post to list on success?
var app = angular.module('myApp', []);
app.controller('bookCtrl', function($scope, $http, $timeout) {
$scope.init = function(){
$scope.title = 'initial book?'
postBook();
};
$scope.books = [];
$scope.post = function() {
postBook();
};
function postBook(){
if (!$scope.title) return;
// timeout to simulate server post
$timeout(function() {
$scope.books.push({title:$scope.title});
$scope.title = null;
}, 1000);
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="bookCtrl" ng-init="init()">
<div class="" ng-repeat="book in books">
<div class="eachBook">{{book.title}}</div>
</div>
<input type="text" ng-model="title" /><button ng-click="post()">save</button>
</div>
EDIT: Not sure why your DOM isn't ready but how about ng-init to accomplish an initial book post?

How to get value in another controller - Angular JS

I am creating some divs using ng-repeat.
See code below :
.controller('meditationsController', function ($scope, $state, $rootScope, $http) {
var req = {
method: 'POST',
url: 'http://example.com/demo/',
headers: {
'Content-Type': 'application/json'
}
}
// Call API
$http(req).then(function(result) {
var rawData = result.data;
$scope.meditationByCategory = {};
for (var i = 0; i < rawData.length; i++) {
var meditation = rawData[i];
if ($scope.meditationByCategory[meditation.main_title] == undefined) {
$scope.meditationByCategory[meditation.main_title] = {};
$scope.meditationByCategory[meditation.main_title].name = meditation.main_title;
$scope.meditationByCategory[meditation.main_title].meditations = [];
}
$scope.meditationByCategory[meditation.main_title].meditations.push(meditation);
}
});
})
<div ng-repeat="(categoryName, category) in meditationByCategory">
<div class="peacefulness"><p class="para-text">{{category.name}}</p></div>
<a href="" ng-click="goToDetailPage()" class="customlink">
<div class="item-content" ng-repeat="meditation in category.meditations">
<span class="leftSpanStyle">{{meditation.title}}</span>
<span class="rightSpanStyle">
<i class="icon ion-ios-information-outline icon-size"></i>
</span>
</div>
</a>
</div>
I have successfully created the list of divs dynamically according to service response.
Now i want to apply click to each div. and the data that i am getting in service response want to bind the next page. I mean the data on the next page will be dynamic and depend upon cliked div.
please help me to bind the data into another page..
Add a button to your view. Something like:
<span class="rightSpanStyle">
<i class="icon ion-ios-information-outline icon-size"></i>
</span>
<button ng-click="doSomeThing($index)"></button>
And in your controller:
$scope.doSomeThing = function(index){
//do something with category.meditations[index]
//or go to another state: $state.go("myState", {item: category.meditations[index]})
}
Edit:
As you say "want to bind to next page" I assume that you want to navigate to another page. Assuming also that you do so by using angulars ui-router, that means that you want to change state. In that case don't forget to define:
url: /myUrl/:item
for that state in question. You can access the item in the target state / controller with $stateParams.item

Angular Nested Controller Data access

I don't know how to access the data from a nested ( child ) controller.
<form ng-controller="TestController as test" ng-submit="submit()">
<h5>Employee name :</h5>
<div ng-controller="mainCtrl" class="row-fluid">
<form class="row-fluid">
<div class="container-fluid">
<input type="text" ng-model="name" typeahead="name for name in names | filter:$viewValue" />
</div>
</form>
</div>
<h5>Comment : </h5>
<textarea ng-model="test.test_content"></textarea>
<input type="submit" id="submit" value="Submit" />
</form>
As you can see i have 2 controllers.The main one is a form , the second one is an input box that allows the user to search the name in a list using typeahead.
I want to be able to acces the content of ng-model="name" in my child controller (mainctrl) in my TestController.
I've tried to access it directly with $scope.name but it doesn't work.
I've also tried test.name as the ng-model and it didn't work either.
I'm sending the data of my TestController to my server and would like to send the data ( name ) from my mainCtrl aswell, directly from TestController. So that when my user click submit it send both the name + the test_content in $http.post request.
Anyone know how to do that ?
Thanks
I've found this but it didn't really help.. https://fdietz.github.io/recipes-with-angular-js/controllers/sharing-models-between-nested-controllers.html
edit:
my search controller
.controller("mainCtrl", function ($scope, $http) {
$scope.selected = '';
$http.get('/feedbacks/search.json').
then(function(response) {
$scope.succes = " ok "
$scope.names = response.data;
// this callback will be called asynchronously
// when the response is available
}, function(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
$scope.succes = " error"
});
});
my form controller :
angular.module('FeedbackTool')
.controller('TestController',['$scope', '$http', function($scope,$http) {
$http.get('/feedbacks.json').
then(function(response) {
$scope.succes = " ok "
$scope.list = response.data;
// this callback will be called asynchronously
// when the response is available
}, function(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
$scope.succes = " error"
});
$scope.submit = function() {
$http.post('/feedbacks.json', { data:this.test }).
then(function(response) {
$scope.succes = 'sent';
// this callback will be called asynchronously
// when the response is available
}, function(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
$scope.succes = 'fail';
});
};
}]);
The parent scope is accessible from within the child scope, but the child scope is not accessible from the parent. Typically the way to deal with this is to assign values from the child scope to properties already defined in the parent. In your case I think you just need to use test.name as the ng-model expression.

$http.get() loops with ngrepeat

I have 2 entities as below
person {
personID,
personName,
Array of Cars (Person Car)
}
Cars {
carID,
carName
}
And then the relational entity for Person-Car as below
PersonCar {
personID,
CarID,
relatedProperty1,
relatedProperty2
}
Now, to represent this on data on a HTML page, I'm doing the below
<div class="row" ng-repeat="person in persons">
<div class="col-xs-6">
{{person.personID}} - {{person.personName}}
</div>
<div class="col-xs-6">
<div class="row">
<div class="col-xs-12" ng-repeat="car in cars">
{{person.carID}} - {{getCarName(person.carID)}} -
{{person.relatedProperty1}} - {{person.relatedProperty2}}
</div>
<!-- Each Car-->
</div>
<!-- Array of Cars-->
</div>
<!-- Array of Cars Section -->
</div>
<!-- Person Row-->
Further, the data is being retrieved as below
modelInfo.controller('modelInfoController', function ($scope, $http, $filter) {
$http.get('http://localhost/person/123').
success(function(data) {
$scope.persons = data
}
$scope.getCarName = function(carID) {
$http.get('http://localhost/car/54545').
success(function(data) {
$scope.persons = data.carName
}
}
};
For some unknown reason, the script loops infinitely over the getCarName function. Please help me understand the issue. Also, please let me know if this is a good REST architecture.
You were missing a couple closing parenthesizes
modelInfo.controller('modelInfoController', function ($scope, $http, $filter) {
$http.get('http://localhost/person/123').success(
function(data) {
$scope.persons = data
}
); // <-- here, closing success function call
$scope.getCarName = function(carID) {
$http.get('http://localhost/car/54545').success(
function(data) {
$scope.persons = data.carName;
}
); // <-- here too
};
}); // <-- and here, closing the controller call
As for REST, it depends on how your backend server endpoints are defined
Although on the client side, you could define services for your resources, i.e. a wrapper for $http with REST methods (get, put, delete, update)

Resources