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.
}
}
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.
I have a problem. I'm trying to include a button that's on a html template page on to my index.html page. I do it like this
<ng-include src="'logout/logout.template.html'"></ng-include>
The page is:
<button type="button" class="btn btn-default btn-sm" ng-click="$ctrl.lala()">
<span class="glyphicon glyphicon-log-out" ></span> Log out
</button>
The problem is, the thing doesn't work. It won't access the ctrl function. As I read, ng-include doesn't work well with other angular directives so my question is, how else can I include my button template in the index.html without copy pasting the code because it's connected to a componenet an dservice so I can't break it.
The componenent:
'use strict';
angular
.module('logout')
.component('logout', {
templateUrl: 'logout/logout.template.html',
controller: ['$scope', '$location', '$localStorage', 'Logout',
function LogoutController($scope, $location, $localStorage, Logout) {
this.lala = function () {
console.log("doing logout");
}
}
]
});
I know it sounds like an overkill, but I would just create another component for it. If lala() is the function that actually logs the user out then it's best to include that in the component too, so you don't have to repeat it in every parent component.
Take a look at this jsfiddle for a complete sample. I wrote everything in the html, but I copied the important part below:
https://jsfiddle.net/6cjd5ggq/1/
<logout></logout>
angular.module('logoutapp')
.controller('logoutController', logoutController)
.component('logout', {
// you can use templateUrl, but it's easier this way in jsfiddle
template: `<button type="button" class="btn btn-default btn-sm" ng-click="$ctrl.lala()">
<span class="glyphicon glyphicon-log-out"></span> Log out
</button>`,
controller: 'logoutController'
});
function logoutController() {
this.lala = function() {
alert("logout!");
};
}
What I want
I have 3 views. All views have their own controller. The first view is home. From the home view the user goes to view 2 by clicking on a div element. On view 2 the user opens view 3 by clicking on a button. On view 3 the user clicks on a button and goes back to view 2.
The problem I am facing is that I want to know if the user came from view 2. In that case I want to show different content then when the user came from the home view.
Create another view
Can it be done without creating another view?
My try
I created a service that keeps track of if I came from view 3, but the controller of view 2 isn't executed anymore after view 2 got opened from the home view. So basicly $scope.fromViewThree isn't updated and still false.
I added $route.reload() before $window.location = "#/app/viewtwo"; because I thought it would reinitialise the controllers(source) and then $scope.fromViewThree should have been updated. I also tried adding it below $window.location = "#/app/viewtwo";.
Controller view 2
.controller('ViewTwoCtrl', function($scope, $window, fromViewThree) {
$scope.fromViewThree = fromViewThree.getBoolean();
fromViewThree.setBoolean(false);
$scope.goToViewThree = function() {
$window.location = "#/app/viewthree";
};
})
Controller view 3
.controller('ViewThreeCtrl', function($scope, $window, fromViewThree) {
$scope.goToViewTwo = function() {
fromViewThree.setBoolean(true);
$window.location = "#/app/viewtwo";
};
})
Directives.js
.service('fromViewThree', function () {
var b = false;
return {
getBoolean: function() {
return b;
},
setBoolean: function(value) {
b = value;
}
};
})
HTML view 2
<div ng-if="fromViewThree == false">
<p>You came from view home!</p>
</div>
<div ng-if="fromViewThree == true">
<p>You came from view three!</p>
</div>
<div>
<button ng-click="goToViewThree()" ng-if="fromViewThree == false">Go to view 3</button>
<button ng-click="goToViewThree()" ng-if="fromViewThree == true">Go again to view 3</button>
</div>
HTML view 3
<div class="row">
<button class='button' ng-click="goToViewTwo()">Lets go to view two!</button>
</div>
Try implementing the views and controllers using the UI router. Once you have the states setup, accessing the previous state will be easy Angular - ui-router get previous state
Solution
Manu Antony pushed me in the right direction towards $rootScope(more info). I added code in app.js which stores the previous state/view name into $rootScope.fromViewThree. I can now access the previous state/view name in the HTML of view 2 and the previous state/view name will be updated when switching state/view has been successful.
Warning:
All scopes inherit from $rootScope, if you have a variable
$rootScope.data and someone forgets that data is already defined
and creates $scope.data in a local scope you will run into problems.
Controller view 2
.controller('ViewTwoCtrl', function($scope, $window) {
$scope.goToViewThree = function() {
$window.location = "#/app/viewthree";
};
})
Controller view 3
.controller('ViewThreeCtrl', function($scope, $window) {
$scope.goToViewTwo = function() {
$window.location = "#/app/viewtwo";
};
})
Directives.js
I no longer need the service because I use $rootScope now to monitor which view is my previous view.
HTML view 2
<div class="row" ng-if="fromViewThree != 'app.viewthree'">
<p>You came from view home!</p>
</div>
<div class='row' ng-if="fromViewThree == 'app.viewthree'">
<p>You came from view three!</p>
</div>
<div class="row">
<button id="activateCameraBtn" class='button' ng-click="goToViewThree()" ng-if="fromViewThree != 'app.viewthree'">Go to view 3</button>
<button id="activateCameraBtn" class='button' ng-click="goToViewThree()" ng-if="fromViewThree == 'app.viewthree'">Go again to view 3</button>
</div>
HTML view 3
The HTML for view 3 hasn't changed.
App.js
angular.module('myApp', [])
.run(function($rootScope) {
$rootScope.$on('$stateChangeSuccess', function (ev, to, toParams, from, fromParams) {
$rootScope.fromViewThree = from.name; //e.g. app.home
});
})
....
I'm having difficulty figuring this out. I have a directive building html from promise data. For each row, it's adding buttons for CRUD operations. I do not know how to get the button event to trigger in my controller. Regardless of how my controller is set up, how can I get the event to register in my controller? I am currently trying $emit, but nothing seems to happen.
Directive generated html:
controls = controls+'<button type="button" data-tooltip-placement="bottom" data-tooltip="'+action.name+'" ng-click="$emit(''+action.broadcaster+'','+rowId+')" name="'+action.name+'" class="btn btn-xs btn-default ng-scope"><i class="'+action.icon+'"></i> </button>'
How it looks in Chrome tools:
<button type="button" data-tooltip-placement="bottom" data-tooltip="delete" ng-click="$emit('removeOrgCourse',134)" name="delete" class="btn btn-xs btn-default ng-scope"><i class="fa fa-trash-o"></i> </button>
and my controller listener:
$scope.$on('removeOrgCourse', function( event, data ){
console.log(data);
});
UPDATE:
Just changed the ng-click to console.log("click") and nothing happened. So the issue is that ng-click is not registering;
While you can use events in angular to achive that, one other option is to use & expression scope binding to pass controller method to directive. Example code (from egghead.io)(see working code at jsbin):
<div ng-app="phoneApp">
<!-- we've replaced the use of $scope with the preferred "controller as" syntax. see:http://toddmotto.com/digging-into-angulars-controller-as-syntax/ -->
<div ng-controller="AppCtrl as appctrl">
<div phone dial="appctrl.callHome(message)"></div>
<div phone dial="appctrl.callHome(message)"></div>
<div phone dial="appctrl.callHome(message)"></div>
</div>
Controller:
app.controller("AppCtrl", function() {
var appctrl = this;
appctrl.callHome = function(message) {
alert(message);
};
});
And directive:
app.directive("phone", function() {
return {
scope: {
dial: "&"
},
template: '<input type="text" ng-model="value">' +
'<div class="button" ng-click="dial({message:value})">Call home!</div>'
};
});
Maybe you have to use $broadcast, depends if the controllers are on the same level or who's over who.
Working with $scope.$emit and $scope.$on
http://toddmotto.com/all-about-angulars-emit-broadcast-on-publish-subscribing/
in the secon link you can put the $emit and $broadcast in the same controller and see whad do you catch in you $on
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.