Private variables in the controller scope - javascript

In angular, following the demos, I can define a controller as:
function TodoCtrl($scope) {
$scope.todos = [
{text:'learn angular', done:true},
{text:'build an angular app', done:false},
{text:'empty dishwasher', done:false}];
$scope.oldtodos = [];
var oldtodos2 = [];
...
You'll note that I have two oldtodos. One on the $scope and one that is just a local var. I think the latter approach is the way to go if you want to encapsulate this variable - i.e. no-one other than the controller has any interest in it and the former is good if you want to pass the variable back to the model
Am I correct?

It looks like you just want to keep a private copy of oldTodos around in the event you have to either refer back to them or resurrect one of them or something. In that case, it probably makes sense not to put the oldTodos into the scope. As Maxim said, scope is relevant if you want to bind values to the view. If you just want to save the oldTodos so keep a reference to them, a normal variable is fine.
Then, if you want to bring one of them back, just copy it into the $scope.todos and back it comes.
Note that you can also abstract all of the todos into a service and inject the service into your controller for another level of encapsulation and better testing strategy. It will also enable you to share the todos across controllers if that's necessary.

Long story short, if you want AngularJS app to interact with the application scope, then you need to add the data in the scope. That's it.
$scope.oldtodos is the correct way to add data to the scope and it can be referred by name oldtodos in HTML template. While var oldtodos2 is private in your controller, so angular will not be able to access this data in the template since it is not in the scope.

Related

Do you always have to inherit service variable in controller in Angular?

i'm current starting to wrap my head around Angular, and i'm struggling with 2 things:
The first problem:
I have an controller 'AboutCtrl', which uses variables from a service 'Abstract'. Now what i want to do is set some basic variables in this service like: 'mobileMenuOpen'.
Now what i want is that 'AboutCtrl' uses the value from 'Abstract', now i've been searching around and i found this way to do it:
constructor (private $scope: IAboutScope, $rootScope, Abstract) {
console.log(Abstract);
$scope.mobileMenuOpen = Abstract.mobileMenuOpen;
}
Now what i'm wondering is, is there a way to automatically inherit all of these variables?
The second problem:
Is there a way to display a variable from 'Abstract' service in a view/index.html?
Example: in my about view i want to show mobileMenuOpen's value, now when i inherit the variable in the AboutCtrl like shown above it works, but how would i be able to do this without inheritance?
I've done some research about best practise and using services/factory seems best practise, if not some other examples would be greatly appreciated.
Let's say your service looks like this:
.service('abstract', function abstract(){
this.mobileMenuOpen = false;
this.someMethod = function(){
//do the magic
return 'cowabunga';
};
})
If you assign the service to $scope by reference like this:
$scope.myAbstract = abstract
then all the values of your service would be assigned to myAbstract and accessible in the templates as well, also it will be two-way binded
<p> Is the menu open: {{ myAbstract.mobileMenuOpen }} </p>
<p> {{ myAbstract.someMethod() }} was the ninja turtles war cry! </p>
Assignment like
$scope.mobileMenuOpen = abstract.mobileMenuOpen;
Would be assigning it by value so whatever change you make later in service will have no effect on $scope.mobileMenuOpen

Is it possible to change where AngularJS looks for variables?

Now, I know this is an off-the-wall question and that there is probably not going to be any API level access for doing this. I also understand that doing this is completely unnecessary. However, the implementation that I am aiming for requires for me to be able to make {{ variable }} look inside of the $scope.object instead of the $scope itself.
For example:
Controller($scope) {
$scope.active = ...;
}
In this case you can get the active object through {{active}} and any child elements through {{active.broken}} however for the sake of humoring me, lets assume that all of the variables I'm ever going to have to obtain is going to be part of that active object. So I'll be typing things like.. (Data not related)
{{active.title}}
{{active.author}}
{{active.content}}
You could just say "Well why not just move the title/author/content into the $scope and keep it outside of the active object, as that would achieve the desired result of this:
{{title}}
{{author}}
{{content}}
Well, that's where the problem comes in. This is a controller that is not exposed to my end-user, however the end-user does have a completely mutable object (in this example: active) that they can modify. This object has many [optional] listeners and callbacks that are invoked by the application controller when necessary.
The user's that have used my application during testing have commented on what a drag it is to have to type in active. before everything in order to get the data they wanted. Considering data from the $scope is never rendered to the screen, and only data from active is, I was wondering if perhaps there was a way to change where AngularJS looks when parsing/binding data.
If the goal is to evaluate expressions in a different context than $scope, that can be done with the $parse service.
app.controller("myVm", function($scope, $parse) {
var vm = $scope;
vm.active = { a: 5,
b: 3
};
var.myFn = $parse("a+b");
vm.total = myFn(vm.active);
console.log(vm.total);
});
The above example shows how to evaluate the expression a+b using the properties of the $scope.active object.
The DEMO on JSFiddle

Angular nested-scopes and nested-views retaining model data

I currently have an application with a rather complex wizard to create a data record. The wizard consists of 3 steps, each associated with a nested view and a controller. Only the data record itself is shared among all three scopes and each controller contributes additional data to the main record.
But they also have scope specific data, that will be used to render additional fields which are only relevant to that nested scope.
I want to be able to go back and forth between the wizard steps but currently it looks like the nested scopes get discarded as soon as I move on to another nested view. I looked up the scope lifecycle in the developer guide: https://docs.angularjs.org/guide/scope#scope-life-cycle
But I does not really tell me how the scope lifecycle applies to nested scopes and how I can prevent these scopes from being discarded. Of course I could move all the data of the nested scopes into the parent scope, but to me that would just feel like a workaround, because actually that data is only relevant to the individual scopes.
I'll try to give a short example:
angular.module('app').controller('ParentCtrl', function ($scope) {
...
$scope.dataRecord = {};
}
angular.module('app').controller('Child1Ctrl', function ($scope) {
...
$scope.dataRecord.test = 'a';
$scope.childScope1SpecificData = '123';
}
angular.module('app').controller('Child2Ctrl', function ($scope) {
...
$scope.dataRecord.test2 = 'b';
$scope.childScope2SpecificData = '456';
}
When I now switch back and forth between the two childscopes, the dataRecord will be adjusted properly, but changes to childScope1SpecificData (via an input field from the template) will be discarded as soon as I switch to Child2Ctrl and back.
Is there a way to persist this data which switching the scope or is it meant to be discarded and I am simply using it wrong?
Thanks
EDIT:
Ok I looked into the factory approach. Maybe to make it more plastic: The additional data, that belongs to each child scope, is a fileuploader with its associated upload queue. Only in a later validation step these pictures actually become part of the datarecord, but until then I don't want the uploaded images to get lost upon switching views.
So what I could do is to externalize the whole fileupload logic into a factory that returns fileuploaders associated to IDs. Whenever a child scope requests the same id the factory will return the same fileuploader. Different Ids will return different uploaders or new ones. That would pretty much solve the problem but would also mean that the data never gets discarded at all unless I really close the browser, because the factory now is absolutely independent of any scope. Since I only want to retain the data in the context of that wizard, I want the data to be discarded, as soon as I leave the wizard.
So after having looked into these other approaches, it seems like I have to go with the original idea: I have to attach the uploaders to the parent scope. So they will continue to exist when switching to other child views, but they will also be discarded as soon as I leave the wizard.
I hope that was correctly summarized
If you are using 'controller as' syntax, you can use this variant.
angular.module('app').controller('ParentCtrl', function ($scope) {
...
$scope.dataRecord = {};
}
angular.module('app').controller('Child1Ctrl', function ($scope) {
...
$scope.ParentCtrl.dataRecord.test = 'a';
$scope.ParentCtrl.childScope1SpecificData = '123';
}
angular.module('app').controller('Child2Ctrl', function ($scope) {
...
$scope.ParentCtrl.dataRecord.test2 = 'b';
$scope.ParentCtrl.childScope2SpecificData = '456';
}
So, you are changing ParentCtrl object in you parent scope, not for every instance.
Sorry, if it was no understandable

Why i should use isolated scope directives in AngularJS?

I'm wondering why i should use isolated scope directives? I can always get some data with service inside my controller and those data will be available inside directive that don't have isolated scope, if i want to use that directive in other place, i can just send request and get the data.... Right?
When you create directive with isolated scope, you must get the data and pass it to directive..
What is the advantage of using isolated scope directives?
Why i should use it and when?*
Because it makes your directive an own module(design-wise, Im not talking about angular.modules ;-) with a clear defined self-contained interface, which means that it is reusable in any context. It also makes its code readable, as everything that the directive works with is in the directives code and not in some magic parent scope that it relies on. Lets look at an example without an isolated scope:
Controller:
angular.module("carShop",[])
.controller("CarStorefrontController",function(){
//For simplicity
this.cars = [
{ name: 'BMW X6', color: 'white' },
{ name: 'Audi A6', color: 'black' }
];
});
Directive:
angular.module("carShop")
.directive("carList",function(){
return {
template: ['<ul>',
'<li ng-repeat="car in vm.cars">',
'A {{car.name}} in shiny-{{car.color}}',
'</li>',
'</ul>'].join("")
};
});
Page:
<div ng-app="carShop" ng-controller="CarStorefrontController as vm">
<h2>Welcome to AwesomeCarShop Ltd. !</h2>
<p>Have a look at our currently offered cars:</p>
<car-list></car-list>
</div>
This works, but is not reusable. If I want to display a list of cars somewhere else in my application, I need to rename my controller there to vm and have it have a field named cars containing my array of cars for it to work. But if I change my directive to
angular.module("carShop")
.directive("carList",function(){
return {
scope: { cars: '=' },
template: [ /* same as above */ ].join("")
};
});
and change <car-list></car-list> on my storefront page to <car-list cars="vm.cars"></car-list>", I can reuse that directive everywhere by just passing in any array of cars without caring where that array came from. Addiionally, I can now replace my Controller on the storefront page with a completely different one, without having to change my directive definition(and without changing all the other places where I use car-list).
It really comes down to the same reason why you should not put all your javascript variables into one global scope so they are easily accessible from everywhere: reusability, readability, maintainability - that is what you get by modularizing and encapsulating your code, by going for low coupling and high cohesion following the black-box-principle.
Directive with isolated scope basically used when you want to create a component that can be reusable throughout your app, so that you can use it anywhere on you angular module.
Isolated scope means that you are creating a new scope which would not be prototypically inherited from the parent scope. But you could pass values to directive that you want to required from the parent scope. They can be pass in the various form through the attribute value, there is option that you can use scope: {} to make directive as isolated scope.
# - Means interpolation values like {{somevalue}}
= - Means two way binding with parent scope variable mentioned in
attribute {{somevalue}}
& - Means you can pass method reference to directive that can be call from the directive
Isolated scope created by using $scope.$new(true); where $scope is current scope where the directive tag is placed.
As your saying, you are going to store data in Service, rather than make an isolated scope. But this approach never going to work for you as service is singleton thing which is having only one instance. If you want to use your directive multiple times then you need to make another variable in service that would take the data required for other element. If that directive count increased to 10, then think how you service will look like, that will look really pathetic.
By using isolated scope, code will look more modularize, code re-usability will be improved. Isolated scope variables never going to conflict with the other variables which is of same name in parent scope.

AngularJS - Correct way to share data between two controllers

I'd like some advice on how to share some data between two or more controllers in AngularJS.
For now I'm just dealing with two controllers, but in the future I will have more controllers that will also want to use this same data. Right now I have a navigation-controller which is controlling the side navigation and the header. And for ease of understanding, let's say the second controller is called content-controller which is responsible for dealing with all the content.
I want to dynamically load the content based on whatever the user searches for and the search bar is in the side navigation, so this searchTerm needs to be accessible by both controllers. In the future, I would also implement some other features which would probably need to access this searchTerm as well.
In terms of the HTML structure, the content-controller is inside the navigation-controller.
My first thought was to make searchTerm globally available by sticking it in $rootScope, but I'm unsure if this is an efficient/secure way to do it.
My second thought was to take the searching aspects and put them into a service. Inside this service I would put functions which would speak to the API in order to get the necessary data. This would mean on the search bar, I can make the submit search button access the service and run something like FooService.update(searchTerm).
What do you think the best way to deal with this scenario is?
Sharing data between controllers has always been a prominent requirement. You have a couple of options out there :
Factory
Services
You can refer to this answer, for more details upon the differences.
Using services is definitely the better option, since you won't be polluting the root scope with extra variables [That are destined to grow in numbers as your have already mentioned].
A possible way to store your data in services, and access them in controllers and HTML effortlessly can be described as :
Create a service, that will hold all the model variables.
angular.service("dataService", function() {
this.value1 = "";
this.value2 = "";
});
reference that service in your controllers, saving their reference in the scope.
angular.controller("myCntrl1", function($scope, dataService) {
$scope.dataService = dataService;
});
angular.controller("myCntrl2", function($scope, dataService) {
$scope.dataService = dataService;
});
Now in your html, you refer all your modal variables using the service reference :
// Controller 1 view
<div ng-controller="myCntrl1">
<input type="text" ng-model="dataService.value1" />
</div>
// Controller 2 view
<div ng-controller="myCntrl2">
The value entered by user is {{dataService.value1}}
</div>
First of all i don't know whether it's gonna work for you.
You can use local storage.
By using this the same data can be accessed in any controller
Here's an example how it worked for me.
app.controller("loginCtrl", function($scope, $window){
$scope.submit = function(){
$window.localStorage.setItem = ("username", $scope.username);
};
});
app.controller("homeCtrl", function($scope, $window){
$scope.logout = function(){
$window.localStorage.getItem = ("username");
};
});

Categories

Resources