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
Related
I'm looking for a pattern in order to have globals constant in my application. But not with a controller or a factory. (so not with app.constant() too)
I just want to set a variable but I didn't find something good.
I wanted to set this var in my rootScoop but without success.
with something like
myApp.run(function($rootScoop){
$rootScoop.global = {};
});
When I use that code, an arror occurs for nothing (transtateFilterProvider). When I delete this code, the translateService works,
I MUST have access in all html view, I don't want to always use a controller (useless in this case), I just want to set a global variable in rootScoop.
Thank you.
You are getting an error because it is :
$rootScope
And not
$rootScoop
Anyway, correct way to do this is to add a constant module to your app like :
angular.module('yourapp', []).constant('Constants', {
foo: 'bar'
});
But you'll have to call Constants in controllers.
If you use $rootScope, remember you will need to call $root.global in templates.
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
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
I recently 'discovered' that Javascript is a "Call by sharing" (wikipedia explanation) language, meaning that in essence, everything is passed by value however the original contents of an object can still be changed. A quick example:
function changeObjectProperties(obj) {
obj.p = 20;
}
var obj = { p: 10; }
changeObjectProperties(obj);
console.log(obj.p); // will print 20; it's changed!
This made me wonder if this can be used within Angular to 'watch' variables without using $scope.$watch. The following works.
controllers.js:
.controller('Listener', function($scope, UserService) {
$scope.user = UserService.getUser();
})
.controller('Changer', function($scope, UserService) {
// let's imagine the UI has some button that changes the e-mailadres
$scope.buttonClick = function() {
UserService.setEmail('foo#bar.com');
}
});
services.js:
.factory('UserService', function() {
var user = {
name: 'Foo',
email: 'example#example.com'
};
return {
getUser: function() { return user; }
setEmail: function(email) { user.email = email; }
};
});
The $scope.user variable within the Listener controller is updated when the user clicks the button within the Changer controller. This change is visible would this variable be displayed in the HTML.
The most obvious pitfall of course is that the object itself can not be changed, as the object reference is then changed and the Listener controller is listening to the wrong reference.
I've searched around to find if this is being done and if its considered good coding. I haven't been able to find anything, possibly because I don't know the correct terms to use. So, is this technique actively being used? Is it considered a good practice or is there some pitfall I'm not aware of?
You can certainly employ the base language to avoid excessive watches. Keep in mind there is no way to avoid watch altogether, because watch is fundamentally how Angular tracks the model. In other words, if you do nothing else, putting something on scope is going to cause Angular to watch it so they it knows when to update the rendered UI. This is the essence of the digest loop. To the extent you can manage your internal communication around the way this digest loop operates, you can reduce or eliminate the overhead of extraneous watchers.
So in essence you can use a JavaScript-first approach to coordinate state and internal object transitions and avoid explicit watchers because Angular will pick up the changes via its own watches that are part of the digest loop.
I think what you are talking about is in line with what I wrote about in my 2nd most common mistake AngularJS developers make, which is abusing $watch: http://csharperimage.jeremylikness.com/2014/11/the-top-5-mistakes-angularjs-developers_28.html
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.