Here I created a sample for a dynamic Help Context. Based on the page it will load dynamic help data which is working fine.
In local $anchorScroll is also working fine.
What I need exactly is while I click the help button I need to set anchorScroll id inside this function $scope.HelpFunction. Can any one help me on this.
example
Sample Link
Maybe you are trying to do something like in this plunk which is a fork of yours. Click home link then click the help button; it should scroll to foo4.
I changed your service from:
.service('myService', function() {
var data = {};
return{
setData: function(val){
return val;
}
}
if(data == {})
{
console.log("inside")
data = {'a':'foo1'};
}
console.log(data)
return data;
})
to
app.service('myService', function() {
var data = {};
return{
data: data,
setData: function(key, val){
data[key] = val;
}
}
});
As that was the malfunctioning part of your code (no api to access the data object).
Also since objects are passed by reference in JavaScript, you don't need a setter on data, you can do direct assignments. Moreover, instead of a service you can also just use a angular value provider and inject that as a dependency where you want to share that object between different components of your app.
Related
how can i save data from 1 view to another in angularjs?
i did $rootScope
From what I see, you use 2 different controllers for each view (or one for the view and none for the root view).
The problem is that Angular can't share data between controllers like that.
You either have to use a service/factory, or use the rootscope, but not as you did, rather with broadcast and emit
If I were you I would use a service.
EDIT Here you go, a service for you :
(function() {
'use strict';
angular
.module('YourModuleName')
.factory('CountriesService', CountriesService);
CountriesService.$inject = ['Your', 'dependencies', 'here', 'in', 'string'];
/* #ngInject */
function CountriesService(your, dependencies, here, not, in, string) {
var service = {
setCountries: setCountries,
getCountries: getCountries
};
var vm = this;
vm.countries = []; // Or maybe an object ?
// ... List of other variables you need to store.
return service;
////////////////
function setCountries(listOfCountries) {
vm.countries = listOfCountries;
}
function getCountries() {
return vm.countries;
}
}
})();
This will store your variables. In your controller you add CountriesService as a dependency, to save you use CountriesService.setCountries and to load you use CountriesService.getCountries. Be aware that refreshing the page will delete all the data !
EDIT NUMBER 2
If you're scared of John papa guidelines, here is a simple service you can use in the same file you put your controller :
app.factory('CountryControl', function(your, dependencies) {
var service = {
setCountries: setCountries,
getCountries: getCountries
};
this.countries = []; // Or maybe an object ?
// ... List of other variables you need to store.
return service;
////////////////
function setCountries(listOfCountries) {
this.countries = listOfCountries;
}
function getCountries() {
return this.countries;
}
});
I have an app that does this more or less. A service fixes this nicely AND creates a mechanism such that you can do this anywhere in your app.
First, I would recommend not trying to manage this with scope. Just put an object on your controller (myFormObj), and add the properties you want to it (name, rank, serialnumber, etc).
Then bind the input fields of the form, to the properties in that object (as opposed to scope vars). So your ng-model things would look like myCtl.formObj.name, and so on.
When the user triggers the event that changes the view, save a COPY (angular.copy) of that formObj off to the side, usually in a Service (think FormStateService or something). FormStateService could do nothing more than hold a simple array.
this.forms = { 'TheNameOfYourForm' : theFormObjToSave };
So, when the user triggers that event that leaves the form, you just do this:
formStateSvc.forms [ 'NameOfMyForm' ] = angular.copy ( theFormObj );
When the user comes back to the original view and the controller initializes, you just ask the formStateSvc:
if ( 'NameOfMyForm' in formStateSvc.forms ) {
this.formObj = formStateSvc.forms [ 'NameOfMyForm' ];
}
Voila, your old form state is restored.
More robustly, you could create "addForm, removeForm" methods etc, you could ensure against things like undefined, and you could make the rebind to the former state implicit (when your form's controller inits, just ask it to restore the state if there's any to restore). So your controller would just have:
this.formObj = formStateSvc.rebindOldDataIfItExists ( 'MyFormName' );
You get the idea.
A simple approach is to create a value provider object and publish it on scope:
//Create value provider object
app.value("FormObj", {});
app.controller("myController", function($scope, FormObj) {
//Publish on scope
$scope.FormObj = FormObj;
});
Then have the ng-model directives use that object:
Name <input ng-model="FormObj.name"><br>
Rank <input ng-model="FormObj.rank"><br>
SerialNum <input ng-model="FormObj.ssnum"><br>
The value object is a singleton which persists for the life of the application. Changes to the contents of the object will be retained and available to other controllers and will survive changes to the view.
The DEMO on PLNKR
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 using external pagination in AngularJS's ui-grid as described here:
http://ui-grid.info/docs/#/tutorial/314_external_pagination
That updates a special field of totalItems when new page is arrived:
var getPage = function() {
// skipped some part
$http.get(url)
.success(function (data) {
$scope.gridOptions.totalItems = 100;
// updating grid object with data
});
};
My only difference to the standard example is that I have gridOptions inside an object that is passed to my directive, that has a grid inside it:
// Line is called right after I receive a page of data from server
$scope.gridObj.gridOptions.totalItems= response.TotalItems;
If I initialize this field before grid is shown on screen (together with configuration) then it remains at the value that I set back then (and obviously, it is not adequate to set of data I receive later, during runtime). But when I alter it later after I call my loading method (that returns current "page" as well as value for totalItems), the change is not picked up by ui-grid. If it is not initialized at startup, again, I could not set this to any value.
How am I to successfully modify the totalItems parameter for the ui-grid during runtime? Can I possibly re-trigger some grid update manually?
I've found that assigning totalItems within a $timeout works nicely for now. This was necessary because ui-grid was updating the value, just not to what I was expecting.
Update:
In my case I need to show all filtered items across all pages, which there is no API for. So for now I've implemented an angular filter, included below. GridRow objects have a visible flag set to true when filters have not filtered them out from the result set.
Example:
angular.filter('renderableRows', function() {
return function(allRows) {
var renderables = [];
angular.forEach(allRows, function(row) {
if(row.visible) {
renderables.push(row);
}
});
return renderables;
};
});
$timeout(function() {
$scope.gridOptions.totalItems = $filter('renderableRows')($scope.gridApi.grid.rows).length;
});
Here's the workflow:
A user's account page should list all of the object owned by that user.
Next to each object is a "delete" button that opens a Bootstrap modal. The modal asks the user if they truly want to delete the object, and if they confirm, then the modal should dismiss, the object should be deleted, and the view should update to reflect the deletion.
I am dismissing the modal using the data-dismiss attribute on the confirmation button inside of the modal.
Here is the function in my controller that deletes the object and (should) update the view:
$scope.deleteObject = function(object) {
object.destroy({
success: function(object) {
$scope.$apply();
},
error: function(object, error) {
// handle error
}
});
};
However, I have to refresh the page to see the updated view with the object removed.
Is there another way I should be using $scope.$apply?
EDIT: I found a workaround by creating a new $scope level function to load my collection of objects. Previously, this was done when the controller is loaded (not attached to any particular function.
In other words, my old code did this:
.controller('AccountCtrl', function($scope) {
var query = new Query('Object');
query.find().then(function(objects) {
$scope.objects = objects;
});
$scope.deleteObject = function(object) {
object.destroy({
success: function(object) {
// do something
}
});
}
});
Now I've wrapped the find code in a $scope level function, which I can call explicitly when an object is destroyed:
.controller('AccountCtrl', function($scope) {
$scope.getObjects = function() {
var query = new Query('Object');
query.find().then(function(objects) {
$scope.objects = objects;
});
}
$scope.getObjects(); // call when the view loads
$scope.deleteObject = function(object) {
object.destroy({
success: function(object) {
$scope.getObjects(); // call again when an object is deleted
}
});
}
});
I'm still hoping there is a cleaner solution to this, i.e. one where I don't have to manually update by object collection.
In your success you have to modify the local $scope.objects.
In you last exemple you should try this (code not tested, but this is how it should look):
$scope.deleteObject = function(object) {
object.destroy({
success: function(object) {
var objectIndex = $scope.objects.indexOf(object);
$scope.objects.splice(objectIndex,1);
}
});
}
In your controller you take the responsibility for updating your model. Angular take the responsibility for updating the view. Calling again the $scope.getObjects() is a way to do it. But the cleaner way is to implements your update of the model in case of success. You should also give an error method in case the server response is an error.
If you have correctly bind the collection to your view, i should update after a delete. Tell me if it helped you out.
Background
I'm about to hook up my angular project to my first API endpoint. I've always created factories that are filled with fake JSON data, so this is new for me.
HTML
First Question:
Lets say we have a two scopes created.
1) by a parent Controller and 2) by a directive's controller. If I inject both scopes with the same Page Service will the $GET request fire twice?
angular.module('yoangApp')
.directive('searchbar', function(){
return {
restrict: 'A',
templateUrl: 'template1.html',
controller: function($scope, Pages) {
$scope.adPageData = Pages;
$scope.adpageLength = $scope.adPageData.length;
},
link: function postLink(scope) {}
}
});
angular.module('yoangApp')
.controller('PageCtrl', function ($scope, Pages) {
$scope.adPageData = Pages;
}
Page Factory:
Second Question
Is this Factory properly written? I dont have access to the api yet I feel like there's a syntax error.
var app = angular.module('yoangApp');
app.factory('Pages', function($http) {
var Pages = {};
$http.get('/api/page').success(function(pages) {
Pages = pages;
});
return Pages;
});
Sample Object from /api/page
'pages': [
{name: 'wow1', imageurl: 'images/image1.jpg'},
{name: 'wow2', imageurl: 'images/image2.jpg'}
]
I recommend that your Pages factory return an object with a single method making the http GET call:
var app = angular.module('yoangApp');
app.factory('Pages', function($http) {
return {
callApi: function(url, callback) {
$http.get(url, callback);
}
};
});
It can then be used to set the adPageData scope property like so:
angular.module('yoangApp')
.controller('PageCtrl', function ($scope, Pages) {
Pages.callApi('/api/page/', function(pagesResult) {
$scope.adPageData = pagesResult;
}
});
This will resolve #1 because the GET call isn't called immediately, and cleans up the Pages factory for point #2.
Interesting Question.
For 1
The factory function will be called only once. From ng docs,
The service factory function generates the single object or function
that represents the service to the rest of the application. The object
or function returned by the service is injected into any component
(controller, service, filter or directive) that specifies a dependency
on the service.
So, the generated object/function will be then injected into the controllers. So it will not fire twice. All Services are singletons, they get instantiated once per app
For 2 - Ideally the function will return Pages even before the http returns a response. So the Pages will be always empty. I guess not the correct way to intantiate Pages.
Here is a demo which illustrates both the points.
Solution:
And as a workaround, if you want the response to be updated automatically return an object from factory and access the pages from returned object as below.
var pages = {data:null};
$http.get(/url).success(function(data){
pages.data = data;
});
return pages;
Here is the updated demo to illustrate the same. The http call will still be made only once.
1 - Yes anything inside your Directive , that is bind to the link property , will fire once the page loads , If you haven't defiend any event like click OR mouseover OR ... to the element property of your directive .
** auto fire function :
link: function postLink(scope) {}
console.log('I'm fired when page loads');
}
** Binded to event function
app.directive('yourdirective',function(){
return{
restrict:"A" //OR "E"...
link:function(scope,element,attributes){
element.bind('click',function(){
console.log('I'm fired when Someone clicks on this directive');
});
}
}
})
2- I think that might work , BUT the conventional and preferred way to write a factory is like this :
app.factory('Pages', function($http) {
var Pages = {};
return{
getPages:function(){
$http.get('/api/page').success(function(pages) {
Pages = pages;
return Pages;
});
}
}
});