When I click on the view details button, I want to request JSON data and then construct a modal view over the content.
HTML:
View Details 1
View Details 2
View Details 3
Get JSON:
function MyCtrl($scope){
$http.get('json/story.json').success(function(data){
console.log(data);
$scope.story = data;
});
}
What is the best practices to run this MyCtrl function using angular.js when a user clicks on the detail button? Also the HTML above is being printed out using another controller. I hope there is some way to bind the clicks versus hard-coding ID's and such in the links.
You can call methods in your controller like this:
View Details
And then in the controller:
$scope.getDetails = function() {
$http.get(...).success(function(data){
$scope.story = data;
});
}
A complete example of CRUD edit with asynchronous data (here simulated via $timeout) can be seen here: http://plnkr.co/edit/EAabx4?p=preview
It uses the $dialog service from the http://angular-ui.github.com/bootstrap/ repository. This example uses Bootstrap's CSS but a template is fully customizable.
The nice part about the $dialog service is that it nativelly works with AngularJS promises so you don't have to copy things to a scope, use watches etc.
Put your server communication code (i.e., $http stuff) into an Angular service. Inject that service into the controller that displays your HTML and into your controller that is associated with your modal view (if you have one).
Have your links invoke functions on the controller that will interact with the service to fetch the data. Have your modal controller $watch for the data.
<div ng-controller="MyCtrl">
View Details 1
Controllers:
function MyCtrl($scope, myService) {
$scope.getDataset1 = function() {
myService.getDataset1(); // populates myService.modalData
};
}
function ModalCtrl($scope, myService) {
$scope.showModal = false;
$scope.$watch(myService.modalData, function(data) {
$scope.modalData = data;
$scope.showModal = true;
});
}
Related
I have multiple controllers on a small app I'm writing, and I have successfully shared a 'selected' variable between the controllers like so.
app.service('selectedEmployee', function () {
var selected = null;
return
{
getSelected: function() {
return selected;
},
postSelected: function(employee) {
selected = employee;
}
};
});
I have a side nav bar with a list of employees. When I click on an employee I call the postSelected function then the getSelected to set $scope.selected.
$scope.selectEmployee = function(employee) {
//Calling Service function postSelected
selectedEmployee.postSelected(employee);
$scope.selected = selectedEmployee.getSelected();
if ($mdSidenav('left').isOpen()) {
$mdSidenav('left').close();
}
}
I have a third controller for my main content area, and this is where I don't understand what to do. I want information from the selected employee to be displayed, but angular is compiling the whole page before the first employee has a chance to get set as selected, and subsequent selections of an employee aren't reloading the main content page (because I haven't told them to I think). Here's my main content controller:
app.controller('mainContentController', ['$scope','selectedEmployee',
function ($scope, selectedEmployee) {
$scope.selected = selectedEmployee.getSelected();
console.log($scope.selected);
}
]);
My main content view is very simple right now
<h2>{{selected.firstName}}{{selected.lastName}}</h2>
My question is how I can tell one controller to effectively update its partial view so that when I select an employee it displays information.
GitLab repo
Don't rely on messy broadcasts if your goal is simply to display & modify the data in the controller's template.
Your controllers do NOT need to "know" when the Service or Factory has updated in order to use it in the template as Angular will handle this for you, as you access the data via dot notation. This is the important concept which you should read more about.
This Fiddle shows both ways of accessing the data, and how using the container object in the template causes Angular to re-check the same actual object on changes - instead of the primitive string value stored in the controller:
http://jsfiddle.net/a01f39Lw/2/
Template:
<div ng-controller="Ctrl1 as c1">
<input ng-model="c1.Bands.favorite" placeholder="Favorite band?">
</div>
<div ng-controller="Ctrl2 as c2">
<input ng-model="c2.Bands.favorite" placeholder="Favorite band?">
</div>
JS:
var app = angular.module("app", []);
app.factory('Bands', function($http) {
return {
favorite: ''
};
});
app.controller('Ctrl1', function Ctrl1(Bands){
this.Bands = Bands;
});
app.controller('Ctrl2', function Ctrl2(Bands){
this.Bands = Bands;
});
First of all lets start by good practices, then solve your problem here...
Good Practices
At least by my knowledge, i dont intend to use services the way you do... you see, services are more like objects. so if i were to convert your service to the way i normally use it would produce the following:
app.service('selectedEmployee', [selectedEmployeeService])
function selectedEmployeeService(){
this.selected = null;
this.getSelected = function(){
return this.selected;
}
this.postSelected = function(emp){
this.selected = emp;
}
}
You see there i put the function seperately, and also made the service an actual object.. i would reccomend you format your controller function argument like this... If you want to disuss/see good practices go here. Anways enough about the good practices now to the real problem.
Solving the problem
Ok The Andrew actually figured this out!! The problem was:that he need to broadcast his message using $rootScope:
$rootScope.$broadcast('selected:updated', $scope.selected);
And then you have to check when $scope.selected is updated.. kinda like $scope.$watch...
$scope.$on('selected:updated', function(event, data) {
$scope.selected = data;
})
After that it autmoatically updates and works! Hope this helped!
PS: Did not know he anwsered already...
So after much research and a lot of really great help from Dsafds, I was able to use $rootScope.$broadcast to notify my partial view of a change to a variable.
If you broadcast from the rootScope it will reach every child controller and you don't have to set a $watch on the service variable.
$scope.selectEmployee = function(employee) {
selectedEmployee.postSelected(employee);
$scope.selected = selectedEmployee.getSelected();
$rootScope.$broadcast('selected:updated', $scope.selected);
if ($mdSidenav('left').isOpen()) {
$mdSidenav('left').close();
}
}
And in the controller of the main content area
function ($scope) {
$scope.$on('selected:updated', function(event, data) {
$scope.selected = data;
})
}
I don't think you have to pass the data directly, you could also just as easily call selectedEmployee.getSelected()
$rootScope also has to be included in the Parent controller and the broadcasting controller.
I am trying to fetch some data from server before controller get render.
I have found many answers for it with respect to routeProvider.
But my main issue is my controller does not bound with any route.
So is there any way to make this possible?
I have controller in following ways...
<!-- HERE I WANT TO BLOCK RENDERING TILL DATA GET LOAD -->
<AppController>
<ng-view>
</AppController>
It sounds like a resolve is what you are looking for, but if you are not using a routing table for this controller, you'll not have this option. Why not just resolve an asynchronous call in your controller, and set scope variables inside the callback. This is what I interpret your desire to await controller "rendering", whereas a resolve through a route table would await controller instantiation. Observe the following...
module.controller('ctrl', function($scope, $http) {
$http.get('/uri').then(function(response) {
// set $scope variables here
});
console.log('executed first');
});
You could also set a variable to prevent the associated view from rendering if your data call is lengthy. This would prevent the UI from "dancing." Observe the following changes to the above example...
<div ng-controller="ctrl" ng-show="resolved"></div>
module.controller('ctrl', function($scope, $http) {
$http.get('/uri').then(function(response) {
$scope.resolved = true; // show rendering
});
});
JSFiddle Link - simplified demo
JSFiddle Link - demo ng-if
One idea will work
in html controller:
<p ng-if="notLoadedContent">Wait</p>
<div ng-if="!notLoadedContent">Content fetched</div>
And in Controller all controller is inside one function will start all, and the controller will be :
fetch(init)
$scope.notLoaded = true;
function init(){
$scope.notLoaded=false;
}
hope it help you
I have following 2 html pages
1. home.html
<div data-ng-controller="userComments">
<Will display all the comments>
</div>
2. comments.html
<div data-ng-controller="userComments">
<Have a comment box and submit button.
Submit button calls submit() function on ng-click>
</div>
where comments.html is pop-up which is initiated from the home page.
And controller
.controller('userComment',['$scope', function($scope){
$scope.title = 'User Comment';
$scope.comments = <db call>
$scope.cmt = '';
$scope.submit = function(){
console.log("comment just entered", $scope.cmt);
$scope.comments = $scope.comments.concat($scope.cmt);
console.log("Updated Comments", $scope.comments);
};
}])
New comments need to be updated automatically in the home.html as well. What should i do to accomplish that?
Thanks
Update:
when the comments are added in the comment.html page, ng-click triggers submit function, $scope.comments gets updated with the new comment, but what should i do to get the updated comments in the home.html too?
When you use the same controller on different views, different instances of the controller are created. You'll need a factory or service to store and share data between views.
So in your case, you'll want a comments factory, something like
myApp.factory('commentsService', function() {
return {
comments: []
};
});
Then in your controller:
.controller('userComment',['$scope', 'commentsService', function($scope, commentsService){
$scope.title = 'User Comment';
$scope.comments = commentsService.comments;
$scope.cmt = '';
$scope.submit = function(){
console.log("comment just entered", $scope.cmt);
$scope.comments = $scope.comments.concat($scope.cmt);
// store the comments for use across views
commentsService.comments = $scope.comments;
console.log("Updated Comments", $scope.comments);
};
}])
You can build out the comments service to also make the db call, as that is an angular best practice (don't fetch external data from controllers, do it from factory/service). You'd build a method called getComments() or something, then call that from the controller.
See:
https://docs.angularjs.org/guide/services
Angularjs - Updating multiple instances of the same controller
Angularjs provides two-way binding so inserting
<div data-ng-controller="userComments">
{{comments}}
</div>
would update comments.
To have same data in entire application( that one defined by ng-app directive ), define a service:
You can inject service to whatever controller
Service data is the same in entire application.
Create service using service method of module.
var app = angular.module('myApp',[]).service('myService', function() {
this.comments = [];
});
Injecting service to controller:
.controller('MyController',['myService',function(myService){
this.addComments = function(data){
myService.comments.push(data);
}
this.getComments = function(){
return myService.comments;
};
}]);
This would keep data same across aplication, and you can also inject this service to another controllers.
Invoke later controller, which uses service:
<div ng-controller="MyController as mc">
{{mc.getComments()}}
</div>
and in another view, set:
<div ng-controller="MyController as mc">
<input type="text" ng-model="myComm"/>
<button type="submit" ng-click="mc.addComment(myComm)" value="Add comment"></button>
</div>
It sets service with new comment. myComm is variable.
ng-model is set with input text, user entered, and ng-click attribute executes on user click.
As final word, there are services provided with angular.
There is $http for network calls, $timeout for invoking things after specific time. You can use them for specific operations and also you can have your own services.
You could also use the events bundled in AngularJS in order to communicate the two instances. So every time the comments array has changes, you can trigger an custom event that the other controller listens and then update the comments array of the other controller.
Let me start by saying this: this is my first forray into angularJS.
I am building an angular app with a feature where you can write sql directly against a database. Security issues aside (I am the only person who will be using this app when it is done).
I built a service on the backend that will return a json object result of whatever query is passed in as params[:query]['input'] (the backend is a rails app).
From the front end, I'd like to pass in a query parameter named input. So in the form on the angular app, I've created this form:
<form action="/path/to/service" method="post">
<textarea id="input" name="input"></textarea>
<button id="execute" name="execute">Execute Query</button>
</form>
However, this is not asynchronous, the page attempts to redirect and the data doesn't come in as it should.
Take a look at the AngularJS tutorial - REST and custom services, there you'll find what services provided by Angular you need to use to make an Ajax call. I suggest you read the whole tutorial if you didn't yet.
AngularJS is used for designing single page applications.
So once your application is loaded, the page won't get refreshed and all the server communication is done async using angular's feature $http.
If you want to submit any form, you can use $http like this
HTML:
<form ng-submit="sub()">
...
</form>
Then in your controller:
function ctrl($scope) {
$scope.sub = function() {
$http.post(url,data,headers)
.success(function(data,status,headers,config) {
//do something with data (response)
})
}
}
What you need is a controller and a factory. With the ng-click directive you call a function in the controller that calls a function in the factory. The factory returns a promise that the controller handles to put the result in the scope. Something like this:
var app = angular.module('app', []);
app.controller('mainCtrl', function($scope, dataService) {
$scope.getData = function(query){
var url = prepareYourURL(query) //HERE YOU PREPARE YOUR URL
dataService.getData(url).then(function(result){
$scope.data = result.data;
});
};
});
app.factory('dataService', function($http){
return {
getData: function(url) {
return $http.get(url);
}
};
});
And from your Html you call it like this:
<textarea ng-model="query"></textarea>
<button ng-click="getData(query)">Execute Query</button>
Then you can just present the data in the view as you like. Start by just doing this
<p>{{data}}</p>
and you will see everything you've fetched.
I have a controller like this (with a bunch of stuff removed):
function SignupController($scope) {
function isDateOfBirthValid(day, month, year) {
// Process day, month and year and return a bool...
// Also update the view model with the appropriate validation message
}
}
The function isDateOfBirthValid() is used internally by the controller, but I would also like to be able to call it from external code.
(I expect I'll be told this contravenes the Angular pattern, but it really would save me a bunch of time...)
How do I need to change the controller so that I can call this function externally? I can't just move the function outside the controller, because the function modifies the view model's state in a way which is important.
You can use angular services for example
SERVICE CODE
app.service('CommonFunctions', function() {
this.isDateOfBirthValid = function(day, month, year) {
/// your code here
};
this.function2 = function() {
// your code here
};
});
Controller CODE
Option 1
function SignupController($scope , CommonFunctions) {
$scope.isValidDOB = CommonFunctions.isDateOfBirthValid (1,2,2013);
}
Option 2
var app = angular.module('app');
app.controller('SignupController', function($scope, $location, $routeParams, CommonFunctions) {
$scope.isValidDOB = CommonFunctions.isDateOfBirthValid (1,2,2013);
});
Your function should be seperated between concerns. Its name isDateOfBirthValid really doesn't imply that it should have any side effects.
The part of the function that has side effects should be moved into a service that possess the business model. Your controller only has to reflect the content of the model. The controller is not the model.
This answers deals with how to update a service from outside of angular.