I have 2 HTML Element with the same Controller but different ng-init('value').
HTML
<div id="open-status" ng-controller="MainController" ng-init="init('open')">
<div ng-repeat="status in statusList">
{{ status.name }}
</div>
</div>
<div id="closed-status" ng-controller="MainController" ng-init="init('closed')">
<div ng-repeat="status in statusList">
{{ status.name }}
</div>
</div>
<button id="reload-open-status-only">Reload</button>
Javascript/Jquery/AngularJS
.controller("MainController", function($scope, dataService) {
$scope.init = function(myType) {
dataService.ajaxThis($config).then( function mySuccess(response) {
$scope.statusList = response.data;
}, function myError(response) {
// ERROR: NO RESPONSE FROM THE AJAX URL
alert('Ajax error! Status not fetched. Please try again later.');
});
$scope.$on('reloadMe', function(e, args){
$scope.init(args);
})
});
$(document).on('click', '#reload-open-status-only', function($rootScope, $timeout){
$timeout(function() {
$rootScope.$broadcast('reloadMe', 'open');
}, 1000);
});
Now, this is working fine. The problem is both of my element are reloading. What is the right way to reload a single ng-controller without using different angular controllers.
EDIT
added a parameter to my broadcast like this
$rootScope.$broadcast('reloadMe', 'open', 'open-status');
and to my controller
angular.element($('#' + id)).scope.init(args);
but i'm having an error:
angular.element(...).scope.init is not a function
angular.element($('#' + id)).scope().init(args);
worked .. just added () after scope.
Related
I m currently really stuck on this topic.
I want to append the response data that I am retrieving from Express to my angular $scope and then redirect the user to their profile page.
My Controller Function looks like this:
$scope.login = function($event){
$event.stopPropagation();
$http({
method: 'POST',
url: '/login',
data: {
email: $scope.email,
password: $scope.password
}
}).success(function(data){
console.log(data.events)
$scope.events = data.events
$location.path('/profile');
}).error(function(err){
console.log(err)
})
Once I try to log in I get the logged data and then I'm redirected as desired. But my ng-repeat directive doesn't update.
JSON Output from my logged data:
[{
"type":"fussball",
"distance":"1",
"date":"Morgen",
"time":"10:30",
"maxAttendees":10,
"attendees:[
1345455466,
323232324,
454554534343,
898493839489,
892839283928,
283293283983]}
ng-repeat section:
<div ng-class="{'first': $first}" class="event-item"ng-repeat="event in events">
<div class="event-type {{event.type}}"></div>
<div class="event-seperator"></div>
<div class="event-body">
<div class="event-body-inner">
<span class="date"></span>
<p class="main">{{event.date}}</p>
<p class="sub">{{event.time}}</p>
</div>
<div class="event-body-inner">
<span class="attendees"></span>
<p class="main">{{event.attendees.length}}{{checkAttendees($index)}}</p>
<p class="sub">TEILNEHMER</p>
</div>
<div class="event-body-inner">
<span class="distance"></span>
<p class="main">{{event.distance}}</p>
<p class="sub">KM</p>
</div>
</div>
<div class="event-seperator"></div>
<div ng-click="selecEvent($event)" class="eventGo hvr-bounce-to-left">
<p>GO</p>
</div>
<div class="event-clicked">
<p>+ ZU DEINEN EVENTS HINZUGEFÜGT</p>
</div>
I'm actually not so familiar with asynch/synch programming so I hope someone can give me a hint ;) .
Thanks in advance!
You need to tell angular that there is an async update on the event list
}).success(function(data){
console.log(data.events)
$scope.events = data.events
$scope.$apply(); // <----- try to add this line
$location.path('/profile');
Maybe you would like to read:
When to use $scope.$apply()
and to watch:
Difference between $apply and $digest
The best way is to use a factory for this. I was about to write it but I found another question in another post.
Please up-vote the original poster if you like the comment.
https://stackoverflow.com/a/21855302/1549291
EDIT: Added plunker link for the implementation:
https://plnkr.co/edit/EimStRS1yMPbiU6PWKJN
(function () {
//factory
angular.module('app')
.factory('appFactory', ['$http', function ($http){
this.getlist = function(){
return $http.get('data.json')
.then(function(response) {
return response.data;
});
};
return this;
}]);
}());
angular.module('app').controller('appCtrl', ['$scope', '$http', '$log','$location', 'appFactory',
//controller
function($scope, $http, $log, $location, appFactory) {
// Chapters json data
appFactory.getlist()
.then(function(data) {
$scope.events = data;
});
}]);
I have a simple angularjs application, in which I want to run a slider and the slider elements come from $http request.
Here is the code for reference:
var mainApp = angular.module("myapp", []);
mainApp.run(['$rootScope', '$http',
function($rootScope, $http) {
$http.post('process.php?ajax_type=getChild').success(function(data) {
if (data.success) {
console.log(data.child); // data received...
$rootScope.categories = data.child;
}
});
}]);
mainApp.controller('gridChildController', function($scope, $http, $rootScope) {
console.log($rootScope.categories); // this is also null....
$scope.brands = $rootScope.categories;
$scope.finished = function(){
jQuery('.brand_slider').iosSlider({
desktopClickDrag: true,
snapToChildren: true,
infiniteSlider: false,
navNextSelector: '.brands-next',
navPrevSelector: '.brands-prev',
lastSlideOffset: 3,
onSlideChange: function (args) {
}
});
};
});
Here is code of the template file:
<div ng-app="myapp">
<div ng-controller="gridChildController" class="brand_slider" >
<div class='slider'>
<div class="slide col-md-5ths col-sm-4 col-xs-12 text-center" ng-repeat="x in brands" ng-init="$last && finished()">
<img class="img-responsive center-block" ng-src="{{x.image}}" title="{{x.title}}" alt="{{x.title}}">
</div>
</div>
</div>
</div>
When I run this code, I get the data from the $http but ng-repeat doesn't work, and in the controller I get data null.
If you put something on $rootScope it will be also available on the child scopes. So you can bind straight to categories (no need to copy it to $scope.brands):
ng-repeat="x in categories"
Explanation :
I created a header directive for my angular application. By using this directive we are loading template by using templateUrl property of attribute based on some conditions.
html :
<div header></div>
directive :
app.directive('header', ['LS', function(LS) {
'use strict';
var user = LS.getData() === 'true' ? true : false;
return {
restrict: 'A',
replace: true,
templateUrl: user ? 'withlogin-header.html' : 'withoutlogin-header.html',
controller: ['$scope', 'LS', function ($scope,LS) {
$scope.login = function() {
return LS.setData(true);
}
$scope.logout = function() {
return LS.setData(false);
}
}]
}
}]);
withlogin-header.html :
<div>
User is logged in !!!
<input type="button" ng-click="logout()" value="Logout"/>
</div>
withoutlogin-header.html :
<div>
Hey buddy, log in !!!
<input type="button" ng-click="login()" value="Login"/>
</div>
Requirement :
We have to display different headers based on the user roles.i.e if user was not login then we have to display normal header with login button and if user already login then we have to display different header with logout button.
Challenge we are facing :
header directive is not working properly. when we click on the login button from withoutlogin-header.html it not loading another template withlogin-header.html based on the defined condition in the templateUrl property of the directive.
I tried so far :
Plnkr : http://plnkr.co/edit/w7AyUjSNA1o5NJp6tD1p?p=preview
Any immediate help will be highly appreciable. Thanks
Checkout this plunker i have created an amazing directive for fetching dynamic header template from server.
I have made some little changes to your code as well.
Hope it will help you surely...
http://plnkr.co/edit/GWIozm?p=preview
You could put the templates into a single directive:
html:
<div ng-if="!isLoggedIn">
Hey buddy, log in !!!
<input type="button" ng-click="login()" value="Login"/>
</div>
<div ng-if="isLoggedIn">
Hey buddy, log in !!!
<input type="button" ng-click="login()" value="Login"/>
</div>
directive:
app.directive('header', function() {
'use strict';
return {
restrict: 'A',
replace: true,
templateUrl: 'withlogin-header.html',
controller: ['$scope', 'LS', function ($scope,LS) {
$scope.isLoggedIn = false;
$scope.login = function() {
LS.setData(true);
$scope.isLoggedIn = LS.getData() === 'true';
}
$scope.logout = function() {
LS.setData(false);
$scope.isLoggedIn = LS.getData() === 'true';
}
}]
}
});
EDIT: or if you want to stick to separate template files, you could use ng-include as #Stepan Kasyanenko mentioned.
I have a similar situation in that I display a certain list of URLS for anonymous users and a different list once the user is logged in. I approached this solution by adding a $watch on the user's logged in property as well as the user's selected language at login (coding for multiple countries)
$scope.$watch(function () {
return User.language;
}, function (newVal, oldVal) {
if (newVal === undefined) {
newVal = "english";
}
navBarCtrl.AnonymousUrls = [];
navBarCtrl.AuthenticatedUrls = [];
$http.get('http://localhost:#####/Urls/' + newVal)
.success(function (data) {
navBarCtrl.sortAllUrlLists(data, navBarCtrl.AnonymousUrls, navBarCtrl.AuthenticatedUrls);
});//end of http get
}, true);
$scope.$watch(function () {
return User.isAuthenticated;
}, function (newVal, oldVal) {
navBarCtrl.isUserAuthenticated = newVal;
}, true);
These two properties are set when the user logs in using the following
this.submit = function () {
var data = angular.toJson(submitForm.credentials);
$http.post("/submitLogin", data)
.success(function (data, status, headers, config) {
submitForm.user.isAuthenticated = true;
submitForm.user.username = data.username;
submitForm.user.location = data.location;
submitForm.user.dsn = data.dsn;
submitForm.user.language = data.language;
$location.path(User.intededPath);
})
.error(function (data, status, headers, config) {
submitForm.user.isAuthenticated = false;
alert("fail");
}); // end of $http.post
} // end of submit
Finally in the view I have the following to display the correct list based on whether or not the user is logged in
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li ng-class="{'dropdown':urlList.length>1}" ng-show="!navBarCtrl.isUserAuthenticated" ng-repeat="urlList in navBarCtrl.AnonymousUrls">
{{urlList[0].Label}}
<ul class="dropdown-menu" ng-if="urlList.length>1">
<li ng-repeat="url in urlList" ng-if="$index > 0">
{{url.Label}}
</li>
</ul>
{{urlList[0].Label}}
</li>
<li ng-class="{'dropdown':urlList.length>1}" ng-show="navBarCtrl.isUserAuthenticated" ng-repeat="urlList in navBarCtrl.AuthenticatedUrls">
{{urlList[0].Label}} <b class="caret"></b>
<ul class="dropdown-menu" ng-if="urlList.length>1">
<li ng-repeat="url in urlList" ng-if="$index > 0">
{{url.Label}}
</li>
</ul>
{{urlList[0].Label}}
</li>
</ul>
</div>
Hopefully that might give you an idea on how to get yours working.
I'm fairly new to Angular, and I'm trying to figure out why scope variables isn't updating after they've been set.
I'm calling a Node API returing json objects containing my data. Everything seems to work fine except setting $scope.profile to the data returned from the API.
Setup:
app.js
(function() {
var app = angular.module("gamedin", []);
app.controller('profileController', function($scope, $http, $timeout) {
$scope.profile = {};
$scope.getProfile = function() {
var vanityUrl = $scope.text.substr($scope.text.lastIndexOf('/') + 1);
$http.get('/steamid/' + vanityUrl)
.then(function(data) {
$http.get('/profile/' + data.data.response.steamid)
.then(function(data) {
console.log(data.data.response.players[0]); // Correct data
$scope.profile = data.data.response.players[0]; // View isn't updated
})
})
// Reset the input text
$scope.text = "";
}
});
...
app.directive('giHeader', function() {
return {
restrict: 'E',
templateUrl: 'components/header/template.html'
};
})
app.directive('giProfile', function() {
return {
restrict: 'E',
templateUrl: 'components/profile/template.html'
}
})
})();
components/header/template.html
<header>
<div class="header-content" ng-controller="profileController">
<div class="col-md-3"></div>
<div class="col-md-6">
<div class="header-content-inner">
<input ng-model="text" ng-keyup="$event.keyCode == 13 && getProfile()" class="form-control" type="text" placeholder="Enter Steam URL">
</div>
<p>e.g., http://steamcommunity.com/id/verydankprofilelink</p>
</div>
<div class="col-md-3"></div>
</div>
</header>
components/profile/template.html
<div class="container">
<div ng-controller="profileController">
<h3>
<strong>Username: {{ profile.personaname }}</strong>
</h3>
<p> SteamID: {{ profile.steamid }}</p>
</div>
</div>
index.html
<!doctype html>
<html ng-app="gamedin">
<head>
...
</head>
<body>
...
<gi-header></gi-header>
<gi-profile></gi-profile>
...
</body>
</html>
I've tried wrapping it in $scope.$apply, like this
$scope.$apply(function () {
$scope.profile = data.data.response.players[0];
});
... which resulted in Error: [$rootScope:inprog]
Then I tried
$timeout(function () {
$scope.profile = data.data.response.players[0];
}, 0);
and
$scope.$evalAsync(function() {
$scope.profile = data.data.response.players[0];
});
... and although no errors were thrown, the view still wasn't updated.
I realize that I'm probably not understanding some aspects of angular correctly, so please enlighten me!
The problem is that you have 2 instances of profileController, one in each directive template. They should both share the same instance, because what happens now is that one instance updates profile variable on its scope, and the other is not aware. I.e., the profileController instance of header template is executing the call, and you expect to see the change on the profile template.
You need a restructure. I suggest use the controller in the page that uses the directive, and share the profile object in both directives:
<gi-header profile="profile"></gi-header>
<gi-profile profile="profile"></gi-profile>
And in each directive:
return {
restrict: 'E',
scope: {
profile: '='
},
templateUrl: 'components/header/template.html'
};
And on a more general note - if you want to use a controller in a directive, you should probably use the directive's "controller" property.
Try using this method instead:
$http.get('/steamid/' + vanityUrl)
.then(function(data) {
return $http.get('/profile/' + data.data.response.steamid).then(function(data) {
return data;
});
})
.then(function(data) {
$scope.profile = data.data.response.players[0]; // View isn't updated
})
Where you use two resolves instead of one and then update the scope from the second resolve.
I'm creating a small event app with angular for practicing. Currently i've created a application with Laravel 5.1 and returning some URL's with JSON. I load those URL's in my Factory for the events. Those events will be rendered with Angular. After they are rendered, a button with "Show info" will be placed and when you click at it, i want to call a function in the EventController so my $scope.event will receive the new data. My Controller has a function with $scope.getEvent(eventID); but i cant figure out how to run that function on click.
//Controller
app.controller('EventController', ['$scope', 'eventFactory', '$http', function ( $scope, eventFactory, $http )
{
eventFactory.getEvents(1).then(function(response){
$scope.events = response.events;
$scope.eventPaginator = response.pagination;
});
$scope.getEvent = function(value){
console.log('trigger');
eventFactory.getEvent(value).then(function(response){
$scope.event = response;
});
};
$scope.loadPage = function(page){
eventFactory.getEvents(page).then(function(response){
$scope.events = response.events;
$scope.eventPaginator = response.pagination;
});
};
}]);
//Directive
app.directive('event', function() {
return {
restrict: 'E',
scope: {
data: '=',
loadEvent: '&',
},
templateUrl: '/assets/website/angular/views/event.html',
link: function(scope, element){
scope.loadEvent(function(){
alert('Click')
});
},
};
});
//Factory
app.factory('eventFactory', ['$http', function ( $http )
{
return {
getEvents: function (page)
{
return $http.get('/api/v1/events?page='+page)
.then(function ( response )
{
return response.data;
});
},
getEvent: function (id)
{
return $http.get('/api/v1/event/'+id)
.then(function ( response )
{
return response.data;
});
}
};
}]);
//View
<div class="event">
<h2>[[data.event.title]] ID: [[data.event.id]]</h2>
<div class="event-image">
</div>
<div ng-click="loadEvent(data.event.id)">Click voor [[data.event.id]]</div>
</div>
//HTML code for the Laravel View
<div class="col-md-9" ng-controller="EventController">
<div class="btn btn-success" ng-click="getEvent(15)">Test</div>
<div class="row">
<h1 ng-bind="showEvent.event.title"></h1>
{#<div class="col-md-12">#}
{#<h1 class="page-header">Page Heading <small>Secondary Text</small></h1>#}
{#<div ng-controller="BannerController">#}
{#<ul rn-carousel rn-carousel-auto-slide="3" rn-carousel-transition="slide" rn-carousel-duration="1000" class="image carousel">#}
{#<li ng-repeat="banner in banners">#}
{#<img ng-src="[[banner.image]]" alt="" class="img-responsive">#}
{#</li>#}
{#</ul>#}
{#</div>#}
{#</div>#}
<div class="col-sm-12">
<div class="row">
<div class="col-sm-4" ng-repeat="event in events">
<h1>Een event</h1>
<event data="event"></event>
</div>
</div>
</div>
<div class="col-sm-12">
<h3>[[eventPaginator.total]]</h3>
<div class="pagination">
<div ng-repeat="n in [] | range:eventPaginator.total">
<button ng-click="loadPage(n+1)">[[n+1]]</button>
</div>
</div>
</div>
</div>
</div>
I think you wanted to call the parent controller function from the directive template with passing id parameter to it. For that you need to pass the method reference to the directive in by adding load-event attribute with getEvent(id) on the directive element.
Also you should remove the unwanted link which has wrong code in it.
//below code should remove from the directive.
scope.loadEvent(function(){
alert('Click')
});
Directive Template
<event data="event" load-event="getEvent(id)"></event>
Template
<div ng-click="loadEvent({id: data.event.id})">Click voor [[data.event.id]]</div>