I make a project in worklight used dojo mobile 1.8.1 and angularjs 1.0.1,but i got a strange problem.
Here is my html part:
<div data-dojo-type="dojox.mobile.ScrollableView" data-dojo-props="selected:true" id="id1" ></div>
<div class="full" data-dojo-type="dojox.mobile.View" id="id2"></div>
and my JavaScript part:
require([
"dojo", "dijit/_base/manager","dojo/parser", "dijit/registry",
], function(dojo) {
dojo.ready(function() {
// dijit.byId("id1").performTransition("id2"); //////////place I
});
});
var angularApp = angular.module('app', [])
.run(['$rootScope','$templateCache','$route',
function($rootScope,$templateCache,$route) {
dijit.byId("id1").performTransition("id2");////////place II
}]);
The problem is at place I, it works well, but when I put "dijit.byId("id1")" at place II, it shows:
dijit.byId("").is not defined
The ready function is executed after dojo parsed your document & constructed the widgets you try to get using dijit.byId.
The second part is not placed within the ready function, so dojo can't find your elements yet !
Solution: Access your elements in the ready function OR do not declare them declaratively (like you did, using html code...) !
Lucian
The dojo.ready() function registers an event-handler function (callback) which will be fired after the DOM got completely parsed.
This comes in very handy if you want to be sure that every html element got rerendered as dojo-widget before you perform operations on them.
So, in your example, Code II will be executed before the dijit.byId() function has been made available by loading the necessary modules (dijit/registry, ...). Code II would only work after the dom-ready event got fired and your "dojo.ready()" function did load the required modules.
You should definately invest 5 minutes in reading what dojo.ready() is about:
http://dojotoolkit.org/reference-guide/1.8/dojo/ready.html
Sidenote:
You shouldn't use dijit.byId() any more in Dojo 1.8.1. Try using dijit.registry.byId() (you have to require the dijit/registry module).
http://dojotoolkit.org/reference-guide/1.8/dijit/registry.html#dijit-registry-byid
Related
I am using Chartist JS for my charts in my Angular JS app. The issue is I am seeing this here. There is a JS bin that highlights the issue. The author gives a solution for it. The solution is doing DOM manipulations in Jquery which is easy to do. However with AngularJS the way you manipulate the DOM is via Directives. I have created a plunker here which highlights the same issue in Angular JS but I am confused as to how to put the solution provided by author into my Angular code.
Here is the solution
$('[data-tab]').on('toggled', function (event, tab) {
tab.find('.ct-chart').each(function(i, e) {
e.__chartist__.update();
});
});
Edit: As requested the JSFiddle is updated, so what I am trying to do is. I have three different tabs and three different graphs, whenever I click on them I should see the respective graph. To make the tab behavior possible I have written a basic code using scope and model. which facilitates the changing of tabs. The issue is that the chart is getting created for first or default tab but not for the second and third tab. There is a solution given by the author but I don't know how to implement that in AngualrJS
the jQuery solution that you post is basically finding all the chart references and then doing DOM manipulation and call the update() function.
The key is how to find the chart to update in Angular.
In this case, you can assign a variable when you create a chart. For example:
var chart4 = new Chartist.Bar('#chart4', data1);
var chart5 = new Chartist.Bar('#chart5', data2);
Now you have the reference of the chart. All you have to do is to call update() function to render the chart again.
if (value === "allDrivers") {
$scope.tab = "All";
chart4.update();
}
Here is the working plunker
One thing I like to point out is: right now you need to double click the tab in order to see the chart is being rendered or you resize the browser window. I am still trying to find a way to fix this. But at least this approach gives you an idea how to convert the jQuery solution to Angular solution.
I was able to solve this using angular.element() method. So if you wish you use jquery in your angular code. You have to do this via angular.element method. But make sure to include jquery before angular in your index.html
If jQuery is available, angular.element is an alias for the jQuery
function. If jQuery is not available, angular.element delegates to
Angular's built-in subset of jQuery, called "jQuery lite" or jqLite.
I did not know this. From here it was learning for me. Following advice of #pieterjandesmedt from this post. I was able to do this. For other people who want to learn how this works. I have created a GitHub repo which gives a solution to this issue. The link for problem is given in the question. Hope that helps
So I have been trying to implement google single sign on into my angular application; however, sometimes when I reload the page the button disappear. My angular application is using angular routing. If I were to put my button outside of this it would work as expected. It just runs into problem when its loaded through a partial. Any idea how I can fix this?
<div class="g-signin2" data-onsuccess="onSignIn"></div>
<div ng-view></div>
As #agektmr said, the problem is related to the way angular and platform.js interact with each other.
In order to use the auto rendered button you need to trigger the library when the DOM is loaded.
What I did is calling the following code in the onComplete method (I'm working with AngularMaterial dialogs, but you should be able to find a similar method quite easily):
$timeout(function() {
$window.gapi.signin2.render('g-signin2');
});
The only difference is in your html you should change your div and instead of adding it a g-signin2 class you should add an g-signin2 id:
<div id='g-signin2' data-onsuccess='yourMethod'></div>
If you're willing to learn more about Google's implementation you could take a look here.
I'd recommend using imperative approach for implementing the button for this.
<div id="signin">
<button>sign-in</button>
</div>
<script>
document.querySelector('#signin').addEventListener('click', function() {
var auth2 = gapi.auth2.getAuthInstance();
auth2.signIn();
});
</script>
Find more concrete example here
https://github.com/GoogleChrome/google-sign-in
The code you indicated didn't work because of timing. platform.js library tries to take care of it but fails because it's before angular renders DOM.
Ok -- so you might be thinking why would you want this but I am trying to render some HTML using ng-html-bind like so (in HAML):
#my-visualization-panel{'ng-bind-html' => 'htmlSource'}
the htmlSource has some html which renders a visualization using c3.js visualization library. The htmlSource looks something like this
<script>
var MY_DATA = localStorage.getItem('MY_DATA');
c3.generate({
data: {
columns: MY_DATA
}
});
</script>
So the problem is that I update the visualization by re-setting localStorage['MY_DATA']. However, while the data that MY_DATA refers to might change, the actual htmlSource does not, so the view fails to update.
Is there a way to force the view to update even if the model, ostensibly, does not?
you can use the apply method of the $scope object:
$scope.apply();
if you are still getting the digest in progress errors, you can also make use of the $timeout object that will run the function in the next digest cycle:
$timeout(function() {
//code
});
as per your latest comment in this answer it seems to be that you are looking for the $scope.watch method. You can add a watcher in order to listen when something changes.
I would recommend changing the HTML anyway. For example
<script>
var MY_DATA = localStorage.getItem('MY_DATA');// 2482486284968248968 (hash of my_data, guid, or serial number)
c3.generate({
data: {
columns: MY_DATA
}
});
</script>
This will allow your script to operate as you expect, directly, without having to do big sweeping updates or hacks. Even if you "force a refresh", if the HTML doesn't change, Angular will not re-execute your script.
Another possibility is to call localStorage.getItem('MY_DATA') in your angular controllers / directives, instead of indirectly hoping Angular will run it for you via HTML updates. That seems to be the kind of control you're looking for.
I have spent around 2-3 hours to resolve this issue. Finally I found solution
$scope.$apply();
I have some problems using Material Design Lite (getmdl.io). I followed the steps showed in the getmdl.io web in order to install it (actually I use bower), but I always have the same problem, when I change the ng-route in my web, some resources don't render properly, I need to reload the page to get it properly rendered, for example.
First I have this:
then when I reload, I get what I want:
What I cant understand is why other resources like google icons or buttons work correctly but the menu button on the nav bar and other resources like this one need to reaload the page in order to render properly.
I try to include the library using the hosted method and bower method.
Any idea what is going on?
i past in my code this function
angular.module('app', ['ngRoute']).
run(function($rootScope, xxxx, xxx){
$rootScope.$on('$viewContentLoaded', function(event, next) {
componentHandler.upgradeAllRegistered();
});
});
It worked perfect! Good luck..
Libraries like MDL work by waiting for the page to load using the DOMContentLoaded event, scanning the page for things like input elements and manipulating them with JavaScript so that they can inject the bits and pieces needed to work with their components. This works fine on static websites, but the DOMContentLoaded event only fires once, so when Angular performs a page transition, the DOM changes without MDL knowing about it.
Material Design Lite has a section in its FAQ about using MDL on dynamic websites:
Material Design Lite will automatically register and render all elements marked with MDL classes upon page load. However in the case where you are creating DOM elements dynamically you need to register new elements using the upgradeElement function. Here is how you can dynamically create the same raised button with ripples shown in the section above:
<div id="container"/>
<script>
var button = document.createElement('button');
var textNode = document.createTextNode('Click Me!');
button.appendChild(textNode);
button.className = 'mdl-button mdl-js-button mdl-js-ripple-effect';
componentHandler.upgradeElement(button);
document.getElementById('container').appendChild(button);
</script>
Of course, this probably isn't terribly easy to do in your case, since you'd have to manually find each new element and call upgradeElement on it.
Usually, instead of doing this sort of event-based DOM manipulation, Angular uses directives to initiate DOM changes. Consider using a library built to interoperate with Angular, instead, such as Angular Material.
I need to implement a browser-like banner warning system so that the user can only access certain features after they've acknowledged certain warnings. The warning messages will depend on data sent from the server and I have to implement different styles and links.
Edit: There could be any number of warnings displayed at the same time. One for each feature. The user must individually acknowledge each warning before the corresponding feature is enabled. Some of the warning texts are static, some are dynamic. Some can have different acknowledge links instead of the standard "okay".
Traditionally, I would package each kind of warning into a class (in the OO sense) and push them to screen through a centralized method, e.g.
displayWarning(new InfoBanner("You must ... before you can modify " + data.name, function onclick() { ... }));
Here the InfoBanner class will have a method that creates the banner elements and attach the event handler.
The Angular way of doing this, on the other hand, seems to be you write the banner entirely in HTML with ng-if and ng-click, etc. E.g.:
<p style="info banner" ng-if="...">You must ... before you can modify {{...}}. <a href ng-click="...">Okay</a></p>
However, this seems quite unfocused and messy because there will now be a large blob of banner code dwarfing the functional part of the page. (There are hundreds of error types defined!)
Is there any way to resolve this without reverting to the fully imperative code?
(Note: a custom directive is probably not the answer as <p style="info banner" is almost like a directive and there's little sharable code among these warnings beyond this.)
(Edit: One can see this question in another way: in the imperative world, the warning-adding logic are scattered in the code but close to the feature they're protecting, so they're easy to understand and maintain. In the declarative world, they must be centralized to the place where they're displayed. I would like a solution where they're declared close to the component they're protecting but displayed centrally.)
What I understand from your question your problem is that since you are in an Angular application you need / should include your banners as markup in your HTML view, since however whether each banner is displayed or not depends on the data you are getting from the server you only know if a specific banner should be displayed in your controller (hence the ng-if you have included in your banner HTML example).
What I would propose in this case would be to create a BannerService which would hold a list of all the banners that should be displayed at any given time. In your controller you can use the functions exposed by the service to add banners to the list when the data you got from the server indicates that you should do so. Each banner in the list would be an object containing all the information that might be different between different banners (ex. banner text, type, etc.) meaning that your HTML view doesn't really need to "know" anything about specific banner details and can just display all the banners available in the BannerService using an ng-repeat.
You can see below a quick-and-dirty example to better understand how this would work.
var app = angular.module('TestApp', [])
app.service('BannerService', [function(){
var banners = [];
this.getBanners = function() {
return banners;
}
this.addBanner = function(banner) {
banners.push(banner);
}
}])
app.controller('TestCtrl', ['BannerService', function(BannerService) {
// Add banners to the banner service depending on your data etc.
BannerService.addBanner({text: "This is a banner", type: "info"});
}])
app.controller('BannerCtrl', ['$scope', 'BannerService', function($scope, BannerService) {
$scope.banners = []
$scope.$watch('BannerService.getBanners', function (newVal, oldVal, scope) {
$scope.banners = BannerService.getBanners();
});
}])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="TestApp">
<div ng-controller="BannerCtrl">
<div ng-repeat="banner in banners"><p class="banner {{banner.type}}">{{banner.text}}</p></div>
</div>
<div ng-controller="TestCtrl">Page content</div>
</div>
In my opinion what you are looking to implement fits perfectly with Aspect Oriented Programming. If you've never used AOP before be prepared for some light reading. The concept is simple and works very well with Angular's patterns. There is an AngularAOP project, but before you dive into it I suggest running through this article first:
http://www.bennadel.com/blog/2425-decorating-scope-methods-in-the-angularjs-prototype-chain.htm