OnsenUI can't access component in code - angularjs

I am working on an OnsenUI Cordova application, using OnsenUI 1.3.1 (installed via bower), AngularJS 1.3.15, and Cordova 5.0.0. I'm having trouble getting to the ons-navigator object within JavaScript, and all of the methods mentioned in the OnsenUI website for doing so (http://onsen.io/guide/overview.html#CallingComponentAPIsfromJavaScript) don't seem to work for me. I have my code set up as follows:
index.html:
...
<body>
<div ng-controller="MainController">
<ons-navigator id="navigator" var="myNavigator" page="{{targetPage}}">
<div>
Hello!
</div>
</ons-navigator>
</div>
...
</body>
...
MainController:
angular.module('myApp.controllers')
.controller('MainController', ['$log', '$scope',
function ($log, $scope) {
$scope.initialize = function () {
$scope.targetPage = 'views/login.html';
$log.info('scope navigator: ' + $scope.myNavigator); // comes back as undefined
$log.info('find navigator: ' + ons.findComponent('ons-navigator#navigator', document.body)); // comes back as null
};
$scope.initialize();
}]);
I'm currently not outputting the methods outlined in the OnsenUI documentation that utilize the ons.findParentComponentUntil() method or the one that involves changing the component based object, but I have looked into those, and they don't seem to work either (comes back undefined).
Does anyone have any ideas? Thanks!

You are trying to access the navigator element before it has been created, try to execute your code with ons.ready();, for example:
ons.ready(function() {
console.log("scope navigator: " + $scope.myNavigator);
});

Related

How can I implement the Plaid API using Ionic?

I was able to successfully implement the API on web, here is what it looks like, I have a button in a regular html file...
<div>
<span class="radio-button radio-left" id="sandboxLinkButton">Sandbox Mode</span>
</div>
I include the script tag...
<script src="https://cdn.plaid.com/link/stable/link-initialize.js"></script>
and I include the following javascript in the html body...
<script type="text/javascript">
var sandboxHandler = Plaid.create({
clientName: 'SUM',
env: 'tartan',
product: 'auth',
key: 'test_key',
onSuccess: function(token) {
//window.location = '/accounts.html?public_token=' + token;
console.log("yes");
},
});
// Open the "Institution Select" view using the sandbox Link handler.
document.getElementById('sandboxLinkButton').onclick = function() {
sandboxHandler.open();
};
</script>
Now I want to do the same using angular js. I'm using an ionic framework (not that it really matters). So I first display the necessary html using the following...
app.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/app');
$stateProvider
.state('app', {
url: '/app',
templateUrl: 'templates/menu.html',
controller: 'AppCtrl'
})
});
My menu.html file contains the following button...
<span ng-click="create()" class="radio-button radio-left" id="sandboxLinkButton">Sandbox Mode</span>
on ng-click it reaches the following controller. I tried to implement the API in this controller to no avail...
app.controller('AppCtrl', function($scope, $ionicModal, $timeout) {
var sandboxHandler = Plaid.create({
clientName: 'SUM',
env: 'tartan',
product: 'auth',
key: 'test_key',
onSuccess: function(token) {
console.log("yes");
},
});
$scope.create = function() {
sandboxHandler.open();
}
});
I get the error Plaid is not defined in the controller. Why is that?
EDIT
I replicated a web app version using angular and it worked. I used the following CDN instead of the ionic/angular one
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
But I still can't get it to work on my ionic web app. Has anyone else ever come across this issue?
When I included the library I immediately got the following error in the browser: Uncaught TypeError: Cannot read property 'appendChild' of null
There are similar questions about this error regarding different libraries here and here, however I was not able to solve the problem based on those answers.
The closest thing to a solution that I found was in this issue report within the Plaid repository on GitHub. It describes a problem with the library not working when placed in the <head> tag. A commit within this issue report describes that this problem can be solved by including the script inside the <body> tag instead of the <head> tag.
I would recommend subscribing to the issue report to see whether other solutions will be introduced in the future.
So tldr; Try moving the <script> tag from <head> to the <body>.
You can simply use the Plain angular wrapper that I created:
https://github.com/khashayar/ng-plaid

Angular ui.bootstrap.alert have no styling

I'm trying out Angular ui bootstrap but I'm having trouble getting it to work. I want to use the alert directive.
What I have done is added the following to my html header:
<link href="stylesheets/bootstrap.min.css" rel="stylesheet"> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.19/angular.min.js"></script>
<script src="javascripts/ui-bootstrap-tpls-0.13.4.min.js"></script>
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script src="javascripts/angularApp.js"></script>
This is my html for the alerts:
<alert ng-repeat="alert in alerts" type="{{alert.type}}" close="closeAlert($index)">{{alert.msg}}</alert>
I inject the dependency to ui.bootstrap:
var app = angular.module('e-log', ['ui.router', 'ui.bootstrap']);
My controller looks like this:
$scope.alerts = [];
var addAlert = function (type, msg) {
$scope.alerts.push({
"type": type,
"msg": msg
});
};
addAlert("success", "A message");
$scope.closeAlert = function(index) {
$scope.alerts.splice(index, 1);
}
Now when I load the page, the html looks like this:
<alert ng-repeat="alert in alerts" type="success" close="closeAlert($index)" class="ng-binding ng-scope">A message</alert>
It shows as plain text, there's no way to close it down and it has no styling at all.
What I've tried to do is add this to a plunker and that works just fine. Am I injecting the dependency correctly? Could it be that I also use regular bootstrap in my code? Any tips on what could be causing this would be greatly appriceated =)!
EDIT: edited according to the advice I've gotten so far.
Two things:
You don't use both Angular UI Bootstrap AND Bootstrap Javascript files together. It's not necessary. You almost certainly only want the Angular UI Bootstrap one.
Most importantly, you're using Angular 1.2.19 and Angular UI Bootstrap REQUIRES Angular 1.3+. See here for the minimal dependencies.
I think there are 3 problems with your setting up above
With addAlert method first parameter, alert type should be success not Success.
Also with addAlert method, it takes 3 parameters but you only give it 2 parameters when you call it - ie. (Success, A message). The most important params are type and msg but you give msg to be undefined.
You can not close the alert because you do not give your controller scope method to handle it. Add :
$scope.closeAlert = function(index) {
$scope.alerts.splice(index, 1);
}
should fix this problem.
Link fiddle for your preference

Using jquery mobile external page with a angular js controller

Hello im working on a web app project, and we are using jquery mobile for the pages ui and angular for mvc reasons,
Im having a problem using my external pages ( pages that ive load to my index.html), they dont recognize my controllers (only works the controllers that ive put in the index.html)
ill some of my code here to explain my problem
if ive put some angular values in my external page (previews declaration of the controller in my
<div data-role="page" id="ResEjecSuc" class=".paginas"
data-dom-cache="true" ng-controlles="categController" >
)
<span ng-class="sucursal.TIT_CATEGORIZACION">
{{sucursal.TIT_REALIZADA}}</span>
my app only shows: {{sucursal.TIT_REALIZADA}}
my controller init code :
app = angular.module('nevadaProt', [])
app.controller('categController', controlerCateg)
controlerCateg = function($scope, $locale) {
var sucursal = {
TIT_CATEGORIZACION:4,
TIT_REALIZADA:12
}
how ive load and after that transition to my html page:
Load:
$.mobile.pageContainer.pagecontainer("load",
"pages/RappidApps2/ResumenEjecZona.html", {});
transition:
$.mobile.pageContainer.pagecontainer("change", "#ResEjecSuc", {
transition : "pop",
reverse : false
});
how can i make work angular controllers and external jquery mobile pages?
Once you are using Angular, you may create a directive.
app.directive('externalHtml', function(){
return {
restrict : 'E'
,replace : true
,templateUrl : 'pages/RappidApps2/ResumenEjecZona.html'
};
});
then, you html should be something like:
<body>
<div data-role="content">
<external-html/>
</div>
</body>
It always works for me.

AngularJS not updating binding when using routes

I am trying to get a small angularJS app running. The app should receive asynchronous messages via STOMP / Websockets. If a message via the web socket arrives, the angular app is supposed to display a changed value in the UI. In general angular's data binding works as expected, but if the scope is updated within the callback function on_message() nothing happens in the UI. I have read various post on similar topics and tried the suggested solutions like using $apply within the callback function, but without success.
In the debugger I can see that the $scope.SoC gets assigned the correct value, but the UI remains unchanged.
If the function on_message(m) is called directly - just for testing - and not from socket-client, the UI gets updated correctly.
This is the abbreviated structure of the controller code
App.controller('showCaseDataCtrl', function($scope){
var mySoC = 0.7;
$scope.status = {statusMessage: "No Message", SoC: 0.77, power: 5.4, numDevices: 89};
function on_message(m) {
mySoC ++;
$scope.$apply(function() {$scope.status.SoC = mySoC;});
console.log(mySoC);
}
});
This is the HTML
<html ng-app="App">
...
<div class="col-md-4" >
<h1> <span ng-bind="status.SoC" /> SoC</h1>
<p>Charge Status</p>
</div>
...
</html>
Any suggestions what else to try are appreciated.
Update:
The problem has to to with mg-route and a separate view in the main HTML
in the main HTML I am using a statement like
<!-- views selected by the route will be injected here -->
<div ng-view ="" </div>
and the route provider looks like
App.config(function($routeProvider){
$routeProvider
.when('/',
{
controller: 'showCaseDataCtrl',
templateUrl: 'partials/Overview.html'
})
.when('/test',
{
controller: 'showCaseDataCtrl',
templateUrl: 'partials/Overview.html'
})
.otherwise({redirectTo: '/'});
});
Removing the route provider and putting all HTML directly in the main HTML with
<div ng-controller="showCaseDataCtrl" class="col-md-4" >
<h1> <span ng-bind="status.SoC" /> SoC</h1>
<p>Charge Status</p>
</div>
solved the problem.
--- But I have no clue why ---
Any explanation appreciated
The reason the $scope is not updating is because the callback is happening outside of Angular's $digest() cycle. Try wrapping it in $scope.$apply.
function on_message(m) {
// console.log('Received:' + m);
// var MyJSON = $.parseJSON(m.body);
mySoC ++;
$scope.$apply(function() {
$scope.SoC = mySoC;
});
console.log(mySoC);
}
});
Seems like you had it commented out, but that should work.

Best way of loading and destroying in angular JS 1.2

I have a scenario where I need to dynamically load an Angular JS application. I have based the code on this:-
https://stackoverflow.com/a/15252490/1545858
Now, I have code that works really well with angular js 1.1.5, but in 1.2.1, no such luck.
Here is the JS code:-
$("#startMeUp").click(function() {
// Make module Foo
angular.module('Foo', []);
// Make controller Ctrl in module Foo
angular.module('Foo').controller('Ctrl', function($scope) {
$scope.data = {};
$scope.data.name = 'KDawg';
$scope.destroy = function() {
$scope.$destroy();
$('#Ctrl').remove();
};
$scope.$on("$destroy", function () {
console.log("EXTERMINATE");
});
});
// Load an element that uses controller Ctrl
$('<div ng-controller="Ctrl" id="Ctrl"> ' +
'<input type="text" ng-model="data.name"></input>' +
'{{data.name}}' +
'<button ng-click="destroy()">Destroy Me</button></div>').appendTo('#container');
// Bootstrap with Foo
angular.bootstrap($('#Foo'), ['Foo']);
});
And here is the HTML:-
<button id="startMeUp">Start Me Up!</button>
<div id="Foo">
<div id="container">
</div>
</div>
Now, if you start and destroy and start again with angular js 1.1.5, everything works fine, but in angular js 1.2.1 it does not work in on the second start. Any thought on how to make it work in 1.2.1?
Here is the js fiddle:-
http://jsfiddle.net/Y9wj2/
As charlietfl says, you don't need to bootstrap more than once. In fact, using angular.js 1.2.1, the error generated that breaks everything is telling you exactly that:
[ng:btstrpd] App Already Bootstrapped with this Element ''
You should think carefully about whether you really need this controller to be dynamic. If you can just use something like ng-include to load the extra content then you will have a much easier time and no need to worry about compiling the content.
If you find you really do need to take this HTML and load it from outside of angular context then you can use the $compile service. Bootstrap the app once somewhere first, preferably using ng-app and grab the injector.
var injector = angular.bootstrap($('#Foo'), ['Foo']);
or
<div id="Foo" ng-app="Foo"></div>
var injector = $('#Foo').injector();
Now you can insert the HTML however you like and then compile and link it using
injector.invoke(['$compile', '$rootScope', function($compile, $rootScope) {
$compile(insertedJqLiteNode)($rootScope);
});

Resources