What is needed to make angular meteor helpers work? - angularjs

this is my first question in stackoverflow!
I have troubles with angular-meteor helpers, i can't get them to work.
I defined this helper block inside my controller.
this.helpers({
testers: () => { return Testers.find() }
});
I created the "Testers" collection.
Testers = new Mongo.Collection("testers");
Also made the publication and subscription with:
$reactive(this).attach($scope);
this.subscribe('testers');
Inside the browser's console i already have access to "Testers" collection and it is reactively updating with Mongo database, i checked that already.
For testing purposes i placed inside the "testers" helper function a console.log and it is indeed getting executed but i dont know why it is not creating the "$scope.testers" variable.
I am using ui-router so i assigned the controller with:
.state('user.index', {
url: '/',
templateUrl: 'client/user/cheques/list.html',
controller: 'testCtrl',
})
I placed a regular variable inside the scope and it gets to the view without trouble as always but i can't get the helper variables.
Does my controller in router assignation matter? Im missing something?
using: angular 1.3.7, angular-meteor 1.3.6

It will create this.testers. you should use controllerAs in your router ,and then use vm.testers (or your other controller as name)

Related

Right approach to helper service (ajax) in Angular js (not dependant on scope)

I have an angular SPA with several modules, services, factories and controllers. I have written a helper service and put it in a common folder minifed js file that all my html pages reference. They contain common bits of data obtained by an AJAX call to a database. It has to run first because the rest of the app depends on values from this helper service. The issue is that the service returns out before the promise has been returned successful, so the helper service is always empty. I do not know what the best approach is to write a helper ajax call and have all files within an angular app reference values returned by it (so everything has to wait before the promise comes back and populates the helper service). I cannot put it in the scope at the top of the main controller, because factories and services cannot reference scope variables is that right? At least I dont understand how if so.
Have searched around and found lots of ways to reference a common service from multiple controllers, but little assistance on how to access that information if the data is a result of an ajax call.
Thanks in advance.
The issue is that the service returns out before the promise has been returned successful, so the helper service is always empty.
Try using resolve:
https://github.com/angular-ui/ui-router/wiki#resolve
https://docs.angularjs.org/api/ngRoute/provider/$routeProvider
Example syntax:
let routerConfig = ($stateProvider, $urlRouterProvider) => {
$urlRouterProvider.otherwise("/login");
$stateProvider
.state('login', {
url: "/login",
templateUrl: "templates/login.html",
controller: "LoginCtrl"
})
.state('home', {
url: "/home",
templateUrl: "templates/home.html",
controller: "HomeCtrl",
controllerAs: "home",
resolve: {
entries: function($rootScope, database) {
return database.getEntries($rootScope.user.uid);
}
}
})
}
There was no point displaying HomeCtrl so I ensured entries from database get resolved before loading... I think you can use similar technique in your code.
If you're using a central service that other pieces of the application depend on, you can use two approaches.
Approach one would be to give the top level controller access to the data and then pass it down to all other components.
The other approach would be giving each individual component/controller direct access to the data.
Either way the code would be kind of similar. There's plenty of ways to do it but the simplest solution I use is just taking advantage of object reference:
Factory:
//you can use factory or service doesn't matter.
.factory('dataFactory', function(){
var store = {
data: null
};
//Return the entire store object
function subscribe() {
return store;
};
function fetchData() {
$http.get('someUrl.com')
.then(function(res) {
//Set data to a property on the store.
store.data = res.data;
});
}
return {
subscribe: subscribe,
fetchData: fetchData
};
}
Controller:
.controller('someController', function(dataFactory) {
//Essentially copying over the store object in the factory by reference
this.store = dataFactory.subscribe();
//call the ajax
dataFactory.fetchData();
}
Template:
<div ng-controller="someController as ctrl">
{{ctrl.store.data}}
</div>
Pros
Any controller in the application can get access to the store in the factory
Since they are referencing the same object, you don't need to do any crazy event emitters or anything to notify all the controllers if the data gets updated in the factory.
You are also caching the data in the factory. If any controllers get destroyed and re-created they can just subscribe again. The data doesn't disappear.
Cons
It's bound by object reference, so any changes made to the store object anywhere in the application will change the object everywhere. This can be good or bad depending on what you want to happen in your application.
If you are working on a large application this can get messy, especially if you're not the only one working on it. However if this is what your application requires and you need a more robust option, i suggest checking out state management tools such as redux.
Checkout this codepen I made which implements a very simple version of redux.

Where is the first place I can call a service in angular ui-router?

I have an Angular app and I am using ui-router.
I need to call my service at the beginning of my App before anything else runs.
Is there a way I can call a service inside of my app.js file without using app.run? Or is there a better solution other than in my app.js file?
Take a look at the resolve functionality of ui-router https://github.com/angular-ui/ui-router/wiki/Quick-Reference. You just add another field to your state called 'resolve' and then can inject the service there. By using a resolve, this route will not execute until this resolve is completed.
I have something similar in an application where I created an abstract state for others to inherit and therefore, none of the states are registered until that initial resolve function is completed.
Ex.
$stateProvider.state('someState', {
template: 'someTemplate.html',
controller: 'someController',
resolve: {
authenticate: function(AuthSrv){
//Do stuff with AuthSrv in here
}
}
})

Splitting up the directive's controller into smaller parts

So I've made this Plunker that works well as a demo: http://plnkr.co/edit/Zm9d6zHhrnqDlnJsSZ1I?p=preview . It's a simple pagination with two attribute arguments that holds the model-state and some config-state. I want to end up with a directive factory like this (or something explaining how to reason differently):
angular.module('mnPagination').factory(function(model, config) {
return {
model: model,
config: config
}
})
My issue with the current plunker is that the app layer and the directive layer doesn't look alike. Since I only have one app I can use factories as singleton data providers. That's really good!
But inside the directive, I can't use any kind of provider since it will be a singleton. The scope is a new object for every declared instance so that's the only place I can put any stateful code that should be contained in the directive.
Are directives supposed to be stateless?
Another more meta question: Am I the only one freaking out about this?
It's my second SO attempt, and noone on facebook or at work are really that into MVVM/MVC or directives with isolated scope.
I'm Leya, come be my Luke!
So the way I solved this was by creating a factory inside an angular factory.
angular.module('mnPagination').factory('mnPaginationFactory', function() {
var factory = function (items, config) {
...
}
return factory
}
Now I get a new object for each directive by calling the factory from the controller which has the items and config objects on the scope.
Plunker here: http://plnkr.co/edit/DPTZUjeMihsva5nJ3IVx?p=preview .

AngularJS 2-way binding before API call is complete

I am using a basic API call to get an array of items to display in a table. My table loads perfectly fine, but I noticed that Angular is throwing a bunch of errors telling me that it can't interpolate expressions, etc.
I realized that this is because for the split second between page load and the API call response, Angular is trying to render the table and is obviously getting errors because the array used to populate the table does not exist yet.
Therefore, what is the standard way to get rid of this problem? Should I simply use ng-If or ng-hide to stop the render until the API call is complete or is there another way to tell angular to "wait" before rendering the DOM.
I was also able to fix this by initializing the array like $scope.dataArray = [], but I feel like this is a hassle when dealing with complex JSON objects that have many arrays and objects that need to be initialized.
Can someone please explain the best way to do this?
Another way this can be handled is using the resolve property on the router or state provider.
With ui-router's $stateProvider you can do this:
.state('mystate', {
controller: function($scope, data) {
$scope.data = data;
},
resolve: {
data: ['$http', function($http) {
return $http.get('/api/get-data');
}]
}
})
And the promise from the $http.get() will be resolved before the controller is instantiated. This works the same way using ngRoute.
$routeProvider (ngRoute) docs: https://docs.angularjs.org/api/ngRoute/provider/$routeProvider
video showing resolve: https://egghead.io/lessons/angularjs-resolve
another blog explaining resolve: http://odetocode.com/blogs/scott/archive/2014/05/20/using-resolve-in-angularjs-routes.aspx
The solution you've already come upon yourself is the correct one. Even if you are feeding Angular an empty scope variable or collection, that's all fine with the framework. What it won't accept is a complete null reference.
To avoid sullying your controllers, think about slurping up some some models into a parent controller, and nesting related objects, i.e. $scope.magic[], which contains $scope.magic.user, etc.
That's the gospel!

Combating AngularJS executing controller twice

I understand AngularJS runs through some code twice, sometimes even more, like $watch events, constantly checking model states etc.
However my code:
function MyController($scope, User, local) {
var $scope.User = local.get(); // Get locally save user data
User.get({ id: $scope.User._id.$oid }, function(user) {
$scope.User = new User(user);
local.save($scope.User);
});
//...
Is executed twice, inserting 2 records into my DB. I'm clearly still learning as I've been banging my head against this for ages!
The app router specified navigation to MyController like so:
$routeProvider.when('/',
{ templateUrl: 'pages/home.html',
controller: MyController });
But I also had this in home.html:
<div data-ng-controller="MyController">
This digested the controller twice. Removing the data-ng-controller attribute from the HTML resolved the issue. Alternatively, the controller: property could have been removed from the routing directive.
This problem also appears when using tabbed navigation. For example, app.js might contain:
.state('tab.reports', {
url: '/reports',
views: {
'tab-reports': {
templateUrl: 'templates/tab-reports.html',
controller: 'ReportsCtrl'
}
}
})
The corresponding reports tab HTML might resemble:
<ion-view view-title="Reports">
<ion-content ng-controller="ReportsCtrl">
This will also result in running the controller twice.
AngularJS docs - ngController
Note that you can also attach controllers to the DOM by declaring it
in a route definition via the $route service. A common mistake is to
declare the controller again using ng-controller in the template
itself. This will cause the controller to be attached and executed
twice.
When you use ngRoute with the ng-view directive, the controller gets attached to that dom element by default (or ui-view if you use ui-router). So you will not need to attach it again in the template.
I just went through this, but the issue was different from the accepted answer. I'm really leaving this here for my future self, to include the steps I went through to fix it.
Remove redundant controller declarations
Check trailing slashes in routes
Check for ng-ifs
Check for any unnecessary wrapping ng-view calls (I accidentally had left in an ng-view that was wrapping my actual ng-view. This resulted in three calls to my controllers.)
If you are on Rails, you should remove the turbolinks gem from your application.js file. I wasted a whole day to discover that. Found answer here.
Initializing the app twice with ng-app and with bootstrap. Combating AngularJS executing controller twice
When using $compile on whole element in 'link'-function of directive that also has its own controller defined and uses callbacks of this controller in template via ng-click etc. Found answer here.
Just want to add one more case when controller can init twice (this is actual for angular.js 1.3.1):
<div ng-if="loading">Loading...</div>
<div ng-if="!loading">
<div ng-view></div>
</div>
In this case $route.current will be already set when ng-view will init. That cause double initialization.
To fix it just change ng-if to ng-show/ng-hide and all will work well.
Would like to add for reference:
Double controller code execution can also be caused by referencing the controller in a directive that also runs on the page.
e.g.
return {
restrict: 'A',
controller: 'myController',
link: function ($scope) { ....
When you also have ng-controller="myController" in your HTML
When using angular-ui-router with Angular 1.3+, there was an issue about Rendering views twice on route transition. This resulted in executing controllers twice, too. None of the proposed solutions worked for me.
However, updating angular-ui-router from 0.2.11 to 0.2.13 solved problem for me.
I tore my app and all its dependencies to bits over this issue (details here: AngularJS app initiating twice (tried the usual solutions..))
And in the end, it was all Batarang Chrome plugin's fault.
Resolution in this answer:
I'd strongly recommend the first thing on anyone's list is to disable it per the post before altering code.
If you know your controller is unintentionally executing more than once, try a search through your files for the name of the offending controller, ex: search: MyController through all files. Likely it got copy-pasted in some other html/js file and you forgot to change it when you got to developing or using those partials/controllers. Source: I made this mistake
I had the same problem, in a simple app (with no routing and a simple ng-controller reference) and my controller's constructor did run twice. Finally, I found out that my problem was the following declaration to auto-bootstrap my AngularJS application in my Razor view
<html ng-app="mTest1">
I have also manually bootstrapped it using angular.bootstrap i.e.
angular.bootstrap(document, [this.app.name]);
so removing one of them, it worked for me.
In some cases your directive runs twice when you simply not correct close you directive like this:
<my-directive>Some content<my-directive>
This will run your directive twice.
Also there is another often case when your directive runs twice:
make sure you are not including your directive in your index.html TWICE!
Been scratching my head over this problem with AngularJS 1.4 rc build, then realised none of the above answers was applicable since it was originated from the new router library for Angular 1.4 and Angular 2 at the time of this writing. Therefore, I am dropping a note here for anyone who might be using the new Angular route library.
Basically if a html page contains a ng-viewport directive for loading parts of your app, by clicking on a hyperlink specified in with ng-link would cause the target controller of the associated component to be loaded twice. The subtle difference is that, if the browser has already loaded the target controller, by re-clicking the same hyperlink would only invoke the controller once.
Haven't found a viable workaround yet, though I believe this behaviour is consistent with the observation raised by shaunxu, and hopefully this issue would be resolved in the future build of new route library and along with AngularJS 1.4 releases.
In my case, I found two views using the same controller.
$stateProvider.state('app', {
url: '',
views: {
"viewOne#app": {
controller: 'CtrlOne as CtrlOne',
templateUrl: 'main/one.tpl.html'
},
"viewTwo#app": {
controller: 'CtrlOne as CtrlOne',
templateUrl: 'main/two.tpl.html'
}
}
});
The problem I am encountering might be tangential, but since googling brought me to this question, this might be appropriate. The problem rears its ugly head for me when using UI Router, but only when I attempt to refresh the page with the browser refresh button. The app uses UI Router with a parent abstract state, and then child states off the parent. On the app run() function, there is a $state.go('...child-state...') command. The parent state uses a resolve, and at first I thought perhaps a child controller is executing twice.
Everything is fine before the URL has had the hash appended.
www.someoldwebaddress.org
Then once the url has been modified by UI Router,
www.someoldwebaddress.org#/childstate
...and then when I refresh the page with the browser refresh button, the $stateChangeStart fires twice, and each time points to the childstate.
The resolve on the parent state is what is firing twice.
Perhaps this is a kludge; regardless, this does appear to eliminate the problem for me: in the area of code where $stateProvider is first invoked, first check to see if the window.location.hash is an empty string. If it is, all is good; if it is not, then set the window.location.hash to an empty string. Then it seems the $state only tries to go somewhere once rather than twice.
Also, if you do not want to rely on the app's default run and state.go(...), you can try to capture the hash value and use the hash value to determine the child state you were on just before page refresh, and add a condition to the area in your code where you set the state.go(...).
For those using the ControllerAs syntax, just declare the controller label in the $routeprovider as follows:
$routeprovider
.when('/link', {
templateUrl: 'templateUrl',
controller: 'UploadsController as ctrl'
})
or
$routeprovider
.when('/link', {
templateUrl: 'templateUrl',
controller: 'UploadsController'
controllerAs: 'ctrl'
})
After declaring the $routeprovider, do not supply the controller as in the view. Instead use the label in the view.
In my case it was because of the url pattern I used
my url was like /ui/project/:parameter1/:parameter2.
I didn't need paramerter2 in all cases of state change. In cases where I didn't need the second parameter my url would be like /ui/project/:parameter1/. And so whenever I had a state change I will have my controller refreshed twice.
The solution was to set parameter2 as empty string and do the state change.
I've had this double initialisation happen for a different reason. For some route-transitions in my application I wanted to force scrolling to near the top of the page (e.g. in paginated search results... clicking next should take you to the top of page 2).
I did this by adding a listener to the $rootScope $on $viewContentLoaded which (based on certain conditions) executed
$location.hash('top');
Inadvertently this was causing my routes to be reevaluated and the controllers to be reinitialised
My issue was updating the search parameters like so $location.search('param', key);
you can read more about it here
controller getting called twice due to append params in url
In my case renaming the controller to a different name solved the problem.
There was a conflict of controller names with "angular-ui-tree" module: I renamed my controller from "CatalogerTreeController" to "TreeController" and then this controller starts to be initiated twice on the page where "ui-tree" directive used because this directive uses controller named "TreeController".
I had the same problem and after trying all the answers I finally found that i had a directive in my view that was bound to the same controller.
APP.directive('MyDirective', function() {
return {
restrict: 'AE',
scope: {},
templateUrl: '../views/quiz.html',
controller: 'ShowClassController'
}
});
After removing the directive the controller stopped being called twice. Now my question is, how can use this directive bound to the controller scope without this problem?
I just solved mine, which was actually quite disappointing. Its a ionic hybrid app, I've used ui-router v0.2.13. In my case there is a epub reader (using epub.js) which was continuously reporting "no document found" once I navigate to my books library and select any other book. When I reloaded the browser book was being rendered perfectly but when I selected another book got the same problem again.
My solve was very simple. I just removed reload:true from $state.go("app.reader", { fileName: fn, reload: true }); in my LibraryController
I have the same issue in angular-route#1.6.7, and it because the extra slash in the end of regex route:
.when('/goods/publish/:classId/', option)
to
.when('/goods/publish/:classId', option)
and it works correctly.
Just adding my case here as well:
I was using angular-ui-router with $state.go('new_state', {foo: "foo#bar"})
Once I added encodeURIComponent to the parameter, the problem was gone: $state.go('new_state', {foo: encodeURIComponent("foo#bar")}).
What happened?
The character "#" in the parameter value is not allowed in URLs. As a consequence, angular-ui-router created my controller twice: during first creation it passed the original "foo#bar", during second creation it would pass the encoded version "foo%40bar". Once I explicitly encoded the parameter as shown above, the problem went away.
My issue was really difficult to track down. In the end, the problem was occurring when the web page had missing images. The src was missing a Url. This was happening on an MVC 5 Web Controller. To fix the issue, I included transparent images when no real image is available.
<img alt="" class="logo" src="">
I figured out mine is getting called twice is because i was calling the method twice from my html.
`<form class="form-horizontal" name="x" ng-submit="findX() novalidate >
<input type="text"....>
<input type="text"....>
<input type="text"....>
<button type="submit" class="btn btn-sm btn-primary" ng-click="findX()"
</form>`
The highlighted section was causing findX() to be called twice. Hope it helps someone.

Resources