Need a static unique identifier from an angularjs controller - angularjs

I've got a series of controllers that make a common call to a function in a parent controller. The behavior of that function will depend on which controller calls it. Currently, I handle this by passing a key as an argument:
In controllerOne:
var result = $scope.getQuery('controllerOne');
In controllerTwo:
var result = $scope.getQuery('controllerTwo');
This works, but I can't help but feel like there's got to be some way to autogenerate that key so that it remains consistent every time the parent function is called. Something along these lines:
var result = $scope.getQuery(this.$id);
So that others can just drop the call into different places without worrying about what the key is. this.$id doesn't work, as it changes on each call, but something like that. Is there a way to get the controller's name, maybe? Anything that is unique and static would suffice.

Related

ng-repeat too many iterations

I am using angular-meteor and would like to perform a function on each object. I tried running this function within an ng-repeat in the view, but I am getting massive amounts of function calls and can't figure out why. I tried to make it as simple as possible to demonstrate what is going on.
constructor($scope, $reactive) {
'ngInject';
$reactive(this).attach($scope);
this.loaderCount = 0;
this.helpers({
loaders() {
return Loaders.find( {isloader:true}, {sort: { name : 1 } })
}
});
That gives me 26 Loaders. My function just adds 1 to the count every time the function is called:
displayLoaderCount()
{
return ++this.loaderCount;
}
Now in my view, I am looping through each loader, and calling the function. This should in my mind give me 26, but instead I am getting 3836.
<tr ng-repeat="loader in loaderExhaustion.loaders">
<td>{{loaderExhaustion.displayLoaderCount()}}</td>
Can anyone help explain this to me? Ideally I would like to loop over the contents in my module but as the collection is async, when the loop starts the length of the collection is 0, hence why I made the call in the view.
THANKS!
Every time angular enters a change detection cycle, it evaluates loaderExhaustion.displayLoaderCount(), to know if the result of this expression has changed, and update the DOM if it has. This function changes the state of the controller (since it increments this.loaderCount), which thus triggers an additional change detection loop, which reevaluates the expression, which changes the state of the controller, etc. etc.
You MAY NOT change the state in an expression like that. For a given state, angular should be able to call this function twice, and get the same result twice. Expressions like these must NOT have side effects.
I can't understand what you want to achieve by doing so, so it's hard to tell what you should do instead.

Call translation service from a callback registered in an app.config section

I'm relatively new to AngularJS and the problem I'm facing is one of those "I want to inject a Service into an app.config" type of scenarios, which I realise cannot be done. (I'm comfortable with the different between Service and Provider, and why a Service cannot be injected into a .config.)
What I am trying to accomplish is to use angular-schema-form together with angular-translate such that field titles in generated forms are translated.
There is an issue where someone asks how to do this, and the advice given is to take advantage of angular-schema-form's postProcess, which is a property of the Provider. This callback gives you the form object before it is rendered, giving you the opportunity to manipulate it with user code. Therefore translation could be done within here.
The postProcess method is called on the Provider, so it is done within an app.config:
app.config(function(schemaFormProvider, $translateProvider) {
schemaFormProvider.postProcess(function(form){
// within here I can inspect the form object, find all
// properties whose key is "title", and then perform
// language translation on their values.
So, that is apparently the place where I have an opportunity to manipulate control titles and so on.
Over to the angular-translate library, for me to 'manually' translate strings, I can use the $translate service. This provides both synchronous and asynchronous methods to translate a given key string. The synchronous one is $translate.instant(key).
To glue these two together, what I have tried so far (which does work) is to create a 'bridge' method like this:
var app = angular.module('myApplicationName', ['schemaForm', 'pascalprecht.translate']);
....
app.config(function(schemaFormProvider, $translateProvider) {
schemaFormProvider.postProcess(function(form){
// ... code here which iterates over properties
// and finds all control titles ...
key = app.myTranslate(key);
// ....
}
....
});
app.myTranslate = function (key) {
var service = angular.injector(['ng', 'myApplicationName']).get("$translate");
return service.instant(key);
}
This does work, but it seems ugly and unsafe (as presumably there's no guarantee $translate is ready when the callback is first invoked) and the calls to angular.injector(['ng', 'myApplicationName']).get... are presumably expensive.
Is there a better way, or is this the only way I'm going to get it done, considering the constraints of the libraries I'm working with?
I have also considered an alternative approach altogether, which would be to instead perform the translations on the schema or form objects before they are processed by angular-schema-form. This could be done from within Controllers, eliminating the problem of accessing the $translate service. I may end up going down that route, but it would still be nice to understand the best solution for the above scenario.

Reactive non-mongo variable in angular-meteor

I couldn't find an answer or a solution to a challenge yet: How can I bind a variable (Session variable, no mongo collection) reactively in angular-meteor?
I'm converting from standalone meteor. Here I could use the template.helper method. As I can't use templates (and iron:router) anymore with angular-meteor and angularui-router, I can't bind reactivity to the helper anymore (at least in my understanding).
I tried this in an meteor-angular controller, which belongs to a sentence.tpl file:
$scope.parsestring = function(input_string){
tokenizer(input_string);
};
$scope.sentence_type = Session.getJSON("current_sentence.sentence_type");
Tokenizing works (I can see it in the debugger), but the value is only displayed, when I reload the page. What I want to achieve is tokenizing a string from an input field into a JSON representation (the tokenizer takes care of that) and displaying it similtaniously from the JSON representation in a structured way (separate html input elements, which are created dynamically). sentence_type is the variable that should be used on the html-page to show and change the sentence type, which can change while typing.
Anybody has some hints? Maybe, I could also use some Angular feature that I don't know?
Cheers,
Jan
Code repo:
My current code looks like this:
My code looks similar to this:
angular.module('ngaignt').controller("InteractorCtrl", ['$scope', '$meteor', '$meteorCollection',
function ($scope, $meteor, $meteorCollection) {
// Autorun is necessary to make reactive variables out of the JSON returns
var c = Tracker.autorun(function (comp) {
$scope.verb_type = Session.getJSON("current_verb.type");
$scope.object_type = Session.getJSON("current_object.type");
$scope.verb_attributes = _.toArray(Session.getJSON("current_verb.attributes"));
$scope.object_attributes = _.toArray(Session.getJSON("current_object.attributes"));
if (!comp.firstRun) {
// only do not do aply at first run becaulse then apply is already running.
$scope.$apply();
}
});
$scope.parsestring = function (input_string) {
interactor(input_string);
};
//$scope.on('$destroy', function () {c.stop()});
}]);
To use reactive variables, you need a reactive computation. You may need to use Tracker.autorun:
$scope.parsestring = Tracker.autorun(function(someStringInSession){
tokenizer(Session.get(someStringInSession));
});
Or you can use Tracker.autorun(func) wherever you use a reactive variable to reactively rerun a function when the variable changes.
good question and the best answer depend on your needs.
There are 2 possible solutions:
If you want to bind a Session variable to a scope variable, use the $meteorSession service.
What it does is that every time the scope variable will change, it will change to Session variable (and trigger an autorun if it's placed inside one).
and every time the Session variable will change, the scope variable will change as well (and change the view that it's placed upon).
If you are using the Session variable just to get a variable reactive (meaning trigger an autorun), you should use getReactively . this just returns the already existing scope variable but trigger an autorun every time it changes. a good example of this can be found it our tutorial.
Note: In anyway, when you use Tracker.autorun inside Angular, you need to connect it to a scope. this can be easily done if you replace Tracker.autorun with the $meteorUtils autorun function
Would be great if you could share a repo so that I can look on the broader perspective and could better determine what's the best solution from the two.
Based on another answer about "session" reacitivity, I could solve the problem. Just use the approach described in the link https://stackoverflow.com/a/21046935/4035797. You have to substitute Deps.autorun by Tracker.autorun though as Deps is deprecated and you have to make the scope variables for use in the template reactive (e.g., $scope.sentence_type = Session.getJSON("current_sentence.sentence_type");) and not the tokenizer.

Difference between $scope.myFunction() and myFunction($scope)

I'm currently developping an angular app and I have found two ways to call a function that does a simple multiplication.
First
function calcul(contexte) {
contexte.proposition.marge_theorique = contexte.proposition.marge_grille * 2;
}
and call it with
calcul($scope)
Second
$scope.dynamicChange = function () {
$scope.proposition.marge_theorique = $scope.proposition.marge_grille * 2;
}
and call it with
$scope.dynamicChange()
What is the difference between those usages?
Thanks a lot
There is no execution difference between your two approaches, but I would recommend you the second one, cause passing scope in parameter is not very usual, adds nothing, and it not allows you to use method directly in your view.
Using the second way, (I mean, the $scope.dynamicChange one), is also good cause you take profit of the Angular controllers inheritance. So every child scope of your controller scope will get this method.
Conclusion, no very difference for your specific task, but I recommend you to use the more "Angular" way.
In the example above, calling the function with dynamicChange() would throw an error, since dynamicChange() is a method of $scope and not a function.
You would need to call $scope.dynamicChange
You won't be able to call the function of the first example from within your html-templates.
So you can't just use something like.
<div>
{{myFunction()}}
</div>

Unable to wrap functions assigned with events parameter in backbone model

Motivation: I want to wrap all functions within backbone model with log functionality to make it more easy to follow function calls.
I am iterating over models functions and warping them with underscores wrap().
But it looks like event function bindings from 'events' hash doesn't get wrapped as it looks like these functions are copied and stored somewhere in DOM, not sure.
Has anybody came to this problem as well or any ideas how to workaround?
Manually entering console.log() in these function is not what I am looking for.
I think you're experiencing the same problem as when people are trying to test the functionality provided with the events -hash. Spying on the event callback directly on the View doesn't work, but spying on the class prototype before instantiating works.
// This won't work
var view = new SomeView();
spyOn(view, 'onClick');
view.$el.click(); // spy isn't called
// This works
spyOn(SomeView.prototype, 'onClick');
var view = new SomeView();
view.$el.click(); // spy is called
Try iterating over the prototype of your 'class' right after creating it, because that should be where the methods called by the events -implementation should reside.

Resources