I have one controller and two views.
ClustersContorller
angular.module('app.controllers').controller('ClustersController', [
'$scope', 'ClustersService', function($scope, ClustersService) {
ClustersService.getAll().success(function(data) {
$scope.clusters = data;
});
$scope.$on('cluster:added', function(event, data) {
ClustersService.createNew(data).then(
function(res) {
$scope.clusters.push(res.data);
},
function(res) {
console.log( 'Unable to create a cluster!' );
}
);
});
}
]);
Now one view is working great when I send the HTTP request and update the scope variable by pushing to $scope.clusters:
<section class="clusters">
<h2 ng-show="clusters.length < 1">You have no clusters :(</h2>
<a class="btn btn-default btn-block" data-ng-repeat="cluster in clusters" data-template="{{cluster.templateId}}">
<h2> {{ cluster.name }} </h2>
<p> {{ cluster.description }} </p>
</a>
<add-cluster-modal></add-cluster-modal>
</section>
But the other view that is bound with this controller does not update scope.clusters in the bindings:
<ul class="dropdown-menu" role="menu" data-ng-controller="ClustersController">
<li data-ng-repeat="cluster in clusters">
<a> {{cluster.name}} </a>
</li>
</ul>
Just to be clear the first view is bound by the $routeProvider and the second one is a part of a template included directly into the app main html file by ng-include=" 'templates/partials/header.html' "
Please feel free to ask me if something is confusing...
Angular controllers are not singletons, and every time you use ng-controller in a view, you create a new instance of that controller (see documentation). This is the reason why your second controller doesn't show the data - it's scope is not aware of the scopes of other instances.
You can either save the model's data under $rootScope, or create some eventing mechanism in your controller, that would inform other instances of data change.
Related
I am having an issue displaying response data, returned from my factory,inside an ionic view page. I believe the issue has something to do with the way I am handling the promise, but I cannot pinpoint why. So to start the roadmap to my problem, here is my:
Factory
.factory('profileFactory', function ($http, $q) {
var config = {
headers: {
'MyKey': 'myvalue'
}
};
this.getEmployee = function (userId) {
return $http.get('http://someurlpathtogoto' + userId + 'etc', config).then(function (response) {
console.log(response.data)
return response.data;
})
}
return this;
})
The above code returns the JSON object I need for my controller so:
Controller
.controller('EmployeeCtrl', ['$scope', 'profileFactory', function ($scope, profileFactory) {
//Set back to false
$scope.profileLoaded = true;
$scope.serviceUnavailable = false;
$scope.setId = function (userId) {
profileFactory.getEmployee(userId).then(function(arrItems){
$scope.employee = arrItems;
$scope.firstName = $scope.employee.record[0].First_Name;
$scope.lastName = $scope.employee.record[0].Last_Name;
};
}])
Now when my page displays I want it to look like such:
Ionic View (Profile)
<ion-view view-title="Profile" ng-controller="EmployeeCtrl" hide-nav-bar="true">
<ion-pane>
<!-- TODO: BACK BUTTON, NO TITLE -->
<ion-header-bar class="bar-stable">
<h1 class="title">Ionic Blank Starter</h1>
</ion-header-bar>
<ion-content>
<div ng-show="serviceUnavailable">
Directory service unavailable
</div>
<div ng-show="profileLoaded">
<br />
<center><img class="focus-img-circle" ng-src="default.png" /></center>
<center><b>{{firstName}} {{lastName}}</b></center>
</div>
</ion-content>
</ion-pane>
<ion-view>
All of this is invoked whenever a user presses on an arrow button in the app which should take them to their profile page to view some info. The following code appears in the previous view.
Ionic View (Search Results)
<ion-item class="item item-avatar item-button-right" ng-repeat="user in results" ng-controller="EmployeeCtrl">
<img ng-src="data:image/png;base64,{{user.pic}}">
<strong>{{user.first_name}} {{user.last_name}}</strong>
<div class="buttons" style="padding:20px;">
<button type="button" class="button button-icon ion-ios-telephone-outline customSearchIcon" ng-click=""></button>
<a class="button button-icon ion-ios-arrow-right customSearchIcon" ng-click="setId(user.id)" href="#/tab/search/profile"></a>
<button type="button" class="button button-icon ion-ios-heart-outline customSearchIcon" ng-click="addFavorite(user.id, user.first_name, user.last_name, user.pic)"></button>
</div>
</ion-item>
So essentially a user clicks on a persons tab, takes them to that profile where my factory is used to make a RESTful call to return a JSON object containing data about that user. Except nothing is being displayed in the Ionic View. If I hard code a userId in the controller and take away the function setId() it works but that obviously isn't an option. Does anyone see anything specifically that I am doing wrong? Been stuck on this for hours any help would be great.
I left a lot of code out to get my point across.
EDIT
The two views you see are different ionic templates. So using ng-controller="EmployeeCtrl is not happening twice in the same view.
The mistake is, on arrow click, you call the service and store the data to scope variable and then navigates to second view. The second view though uses the same controller as the first view, a new controller is being instantiated with empty scope - and hence your view is empty.
Instead of ng-click in <a> tag, modify your profile route to pass the userid as well - tab/search/profile/:userid and in anchor tag have ng-href= "#/tab/search/profile/{{user.id})" and in your profile controller get the userid from query string ($location.search().userid) and make the Ajax call.
My data in Firebase looks like this...
evalu8er
situations
-K6rM12D-0nt4fJH_QcA
situation: "Test"
-K6rPoHiUl2N2JOSWXww
situation: "Test2"
-K6rPqbkBHJ-K8znVzay
situation: "Test3"
I have inserted the data from an HTML page via a form like this...
<div class="form-group">
<label for="situation">Add a situation</label>
<input type="text" class="form-control" id="situation" placeholder="Situation" ng-model="situation">
</div>
<button type="submit" class="btn btn-default pull-right" ng-click="add_situation()"><i class="fa fa-cog fa-spin" ng-show="loading"></i> Add </button>
</form>
The above form is calling the controller...
app.controller("situations", ["$scope", function ($scope) {
$scope.add_situation = function () {
var situation = $scope.situation;
var ref = new Firebase("https://evalu8er.firebaseio.com/situations");
ref.push().set({
situation: situation
});
};
}
]);
Now I want to get each of the situations back to the page, and this where it all goes wrong for me.
Inside the same controller as above I'm adding this....
var ref = new Firebase("https://evalu8er.firebaseio.com/user/situations/");
ref.on("value", function(snapshot) {
$scope.display_situatoins = snapshot.val();
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
And on the HTML page I have added
<li data-ng-repeat="x in display_situatoins">
{{ x }}
</li>
When the page loads it DOES NOT show me the data until I enter something in the form field,
Question 1/3)
How do I get the data to show on page load?
When it does load it looks like this...
{"situation":"Test"}
{"situation":"Test2"}
{"situation":"Test3"}
And what I want is just to show a list of the situations like this..
Test
Test2
Test3
Question 2/3)
How do I just show the situation values as above?
And when I do add a new item it gets listed at the bottom, how do I order the list with the new items at the top?
Question 3/3)
How do I order the list so the new items appear first?
Use AngularFire for synchronized collections.
angular.module('app', ['firebase'])
.constant('FirebaseUrl', '<my-firebase-app>')
.service('rootRef', ['FirebaseUrl', Firebase])
.controller('MyCtrl', MyController);
function MyCtrl($scope, rootRef, $firebaseArray) {
$scope.situations = $firebaseArray(rootRef.child('situations');
}
Then in your template for MyCtrl, you can do an ng-repeat on the situations array.
<li ng-repeat="situation in situations">
{{ situation.situation }}
</li>
The {{ situation.situation }} part looks awkward because of the naming. If you change the property situation to something like text or title it would look like:
<li ng-repeat="situation in situations">
{{ situation.title }}
</li>
I have a single page app '(Backend in Python/Django)' where my functions return json response and that json response handled by angular js in front end . I am using angular ajax call to hit the function. Now we all know that on ajax call url in address bar do not get changed. But in angular js we can set url using $location.path(). So it keeps the history of url I have visited and on browser back button it changes the url in address bar to previous one . But it do not change the content of the page.
My angular ajax call :
app.controller('myController',
function($scope,$http, $location, $route, $timeout){
$scope.formData={}
$scope.getAllBrainframes=function(id){
if(id){
$scope.url = '/get-brainframe/'+id+'/';
}else{
$scope.url = '/get-brainframe/';
}
$http.post($scope.url)
.success(function(data,status){
.success(function(data,status){
$scope.title = data[0].title
$scope.brainframes = data;
$location.path($scope.url);
$scope.parent= data[0].id;
})
.error(function(data,status){
});
}
});
As I am setting $location.path() on ajax success , so it appends the current visited url in address bar and keeps history of every url i have visited. But when I click on browser back button it changes the url in address bar to previous one but not the content.
Now is there any function that i can trigger when I click on browser back button or how I can change the content of page ?
EDIT :
above ajax success function edited .
My html :
<div class="content">
<span ng-repeat="brainframe in brainframes">
<p ng-if = "brainframe.brainframes.length > 0 ">
<ul class="list-group col-md-5">
<div data-ng-repeat="brain in brainframe.brainframes" class="child-brainframes">
<a class="my-title" ng-click="getAllBrainframes(brain.brainframe_child.pk)">
<li class="list-group-item"><span class="badge">{$ brain.count_children $}</span>{$ brain.brainframe_child.title $}</li>
</a>
</div>
</ul>
</p>
<p ng-if = "brainframe.brainframes.length < 1 ">
<span>No brainframes are available.</span>
</p>
</span>
</div>
You need to look at $routeParams and change the content in the template.
DEMO
.controller("MainCtrl",
function($scope,$http, $location, $route, $timeout, $routeParams){
$scope.formData={};
$scope.id = $routeParams.id;
$scope.getAllBrainframes=function(id){
if(id){
$scope.url = '/home/'+id+'/';
}else{
$scope.url = '/home';
}
console.log($scope.url);
$location.path($scope.url);
};
});
In your template:
Check for the $scope property and show/hide
<script type="text/ng-template" id="Home.html">
<p>This is one template</p>
<p ng-if="id">This is in next template</p>
<a ng-click="getAllBrainframes('1')">next template</a>
</script>
Check full code here
UPDATED:
If you want to persist the ajax call data between routes, you need to probably store it in a service and access it in your controller based on the $routeParams from the service.
I have a Play framework project using AngularJS for its views. The controller responsible for querying data makes 2 requests, each returning a JSON block. The first one works correctly and it's displayed properly. The second one's data is pretty much destroyed by Angular (example below).
The JSONs are created correctly prior to being rendered, as it's shown through the application's log.
This is the correct JSON (taken from the Play Framework routed method's log):
{"id":5,"name":"auditoria","url":null,"version":1,"methods":[]}
This is how AngularJS prints it. It tokenizes :
[{},{"0":"a","1":"u","2":"d","3":"i","4":"t","5":"o","6":"r","7":"i","8":"a"},{},{},{"length":0}]
And here's the controller:
app.controller("ViewCtrl", [ "$scope", "$resource", "$routeParams", "apiUrl",
function($scope, $resource, $routeParams, apiUrl) {
var ServiceList = $resource(apiUrl + "/services");
$scope.services = ServiceList.query(); //JSON is displayed properly
if ($routeParams.id) {
jsonServicoQuery = apiUrl + "/services/" + $routeParams.id
var Service = $resource(jsonServicoQuery);
$scope.currentService = Service.query(); //JSON is butchered
}
} ]);
Here's the HTML:
<div class="row">
<div class="col-md-3">
<div class="bs-sidebar hidden-print" role="complementary">
<ul class="nav bs-sidenav">
<li><i class="fa fa-plus"></i></li>
<li ng-repeat="s in services| orderBy:'name'">{{s.nome}}
<ul>
<li ng-repeat="m in s.methods| orderBy:'name'">{{m.name}}
[{{m.id}}]</li>
</ul></li>
</ul>
</div>
</div>
<div class="col-md-9" role="main">
<div class="bs-docs-section">
<div class="page-header">
<!-- displaying the whole currentService JSON for debugging purposes -->
{{currentService}}
</div>
</div>
</div>
</div>
Anybody has any clues about what am I doing wrong?
Update: Service.query() executes the method routed by Play Framework.
Route configuration:
GET /api/services/:id controllers.Services.show(id: String)
And controllers.Services.show(id: String) implementation:
public static Result show(String id) {
Service s = Service.findById(id);
//JSON displayed here is correct, log below
Logger.info("At Services show, json " + Json.toJson(s));
return ok(Json.toJson(s));
}
Log:
[info] application - At Services show, json {"id":5,"name":"auditoria","url":null,"version":1,"methods":[]}
I managed to find the problem, I should have used $scope.currentService = Service.get(); instead of $scope.currentService = Service.query();
I am generating a thumbnail list of photos (using ng-repeat), and under each one of these photos I have two buttons, one to view more details and another to purchase.
I am finding a problem how to map the buttons. Basically I want that when a user clicks on the purchase button underneath Photo A, in the booking form (which is a different view), he/she will see the details pertaining to Photo A, rather than having the user select the photo again from some drop down. The list of photos is coming from a JSON string.
Mainly the difficulty I am finding is how to pass the details of which button was clicked to the booking view so that I would be able to display the details of the selected photo immediately.
I am new to AngularJS and am not sure if there is a simple way that can be done.
My HTML is this:
<div class="col-md-4 ng-scope" ng-repeat="photo in photos">
<div class="thumbnail">
<img src="{{photo.thumbnail}}" alt="{{photo.title}}">
<div class="caption">
<h4 class="ng-binding">{{photo.title}}</h4>
<p><button type="button" class="btn btn-default">Photographer</button><br ><button type="button" class="btn btn-default">Purchase</button></p>
</div>
</div>
Angular JS:
App JS
angular
.module('app', [
'ui.router'
])
.config(['$urlRouterProvider', '$stateProvider', function($urlRouterProvider, $stateProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('photos', {
url: '/photos',
templateUrl: 'templates/photos.html',
controller: 'photosCtrl',
resolve: { photos: ['$http', function($http){
return $http.get('api/photos.json').then(function(response){
return response.data;
});
}]}
})
}]);
photosCtrl JS:
angular
.module('app')
.controller('photosCtrl', ['$scope', 'photos', function($scope, photos) {
$scope.photos = photos;
}]);
using ngClick directive is a good idea as #Ashesh suggested
Assuming the JSON containing your photos comes with a bunch of photo object, I'd rather add two functions to the scope of photosCtrl like this:
angular.module('app')
.controller('photosCtrl', ['$scope', 'photos', function($scope, photos) {
$scope.photos = photos;
$scope.showDetailsOf = function(photo) {
// photo argument is the actual object of which 'details' button was pressed
// coming from the ngRepeat in your template example
console.log('show details of ' + photo.title);
}
$scope.purchase = function(photo) {
// same as above
console.log('purchase ' + photo.title);
}
}]);
The template should look like this:
<button type="button" class="btn btn-default" ng-click="showDetailsOf(photo)">Details</button>
<button type="button" class="btn btn-default" ng-click="purchase(photo)">Purchase</button>
Furthermore you can factor this logic out to e.g. a photoService so that your controller won't contain business logic which is always preferable as both of your controller and the service can be covered by tests more easily, because you decouple them. Hope this helps.
Use ngClick:
<p><button type="button" class="btn btn-default" ng-click="photo.showPhotographer()">Photographer</button><br ><button type="button" class="btn btn-default" ng-click="photo.buy()">Purchase</button></p>
And ofcourse, photo.showPhotographer() etc. can do what you like them to do:
function Photo() { // the photo object
this.buy() {
// api call to buy with the id of the photo or window.navigate('buy_url?id=' + this.id), etc.
}
}