open external link in angulardart in new tab/window - dart

I am writing an angular dart app and the app contains a few links that point to other resources on the server.
I have a weird experience that some links are "handled" by angular and some not.
For example in the code I have these two links that are close to each other. Both are inside the same controllers scope. The first link is handled by angular, the second is opened in a separate window as configured. (Maybe due to the hashbang in the url?)
...
<div class="span4">
<div class="address">Direktlink</div>
not working link
</div>
</div>
<div class="row-fluid">
<div class="span12 modal_image">
<img src="http://localhost/modules/mod_orchit_baumodul/ajax/composite.php?haus=Aachen&variante=8&pos=0" class="ng-binding">
</div>
</div>
</div>
So I created a small directive
/**
* https://github.com/angular/angular.dart/issues/335
* https://github.com/angular/angular.dart/issues/864
*/
#NgDirective(
selector: 'a[externalLink]'
)
class ExternalLinkDirective {
Element element;
ExternalLinkDirective(this.element) {
element.onClick.listen((Event event){
window.location.assign(element.attributes["href"]);
});
}
}
but this has the disadvantage that I can't open the link in a new tab or window.
I couldn't find something that helped me with that in the API docs. :-(
Is there a way to open the first link in a new tab too?

OK, I figured it out. But it opened a new window each time, so I check for the target first.
Feel free to use this directive yourself.
/**
* https://github.com/angular/angular.dart/issues/335
* https://github.com/angular/angular.dart/issues/864
*/
#NgDirective(selector: 'a[externalLink]')
class ExternalLinkDirective {
Element element;
ExternalLinkDirective(this.element) {
element.onClick.listen((Event event) {
String target = "_SELF";
if (isValidTarget()){
window.open(element.attributes["href"], element.attributes['target']);
}
else window.location.assign(element.attributes["href"]);
});
}
bool isValidTarget(){
return element.attributes.containsKey("target") && element.attributes['target'].toLowerCase()!='_self';
}
}

Since route_hierarchical-0.4.19 this issue seems to be fixed.

Related

Intra-page link starts at URL root

This is so simple I don't get how it could possibly go wrong. I'm trying to get a simple intra-page link to behave.
(If it's relevant, this is an angular 2 app, using routing.)
Here is a typical page:
Skip to main content
<div class="page-body">
<main>
<div class="content-body" id="content-start">
<h1>Employee Search</h1>
</div>
<main>
</div>
The URL of this page (in my dev env) is
http://localhost:49974/app/employee/search
When I click (or focus then press enter) on Skip to main content it should go to
http://localhost:49974/app/employee/search#content-start
but instead goes to
http://localhost:49974/app#content-start
(and then immediately switches to
http://localhost:49974/app/#content-start
)
I can't have messed up the linking itself; this must have something to do with how the routing is working.
It looks like I have to do it this way:
<a href="app/request/timeoff#content-start">
But that doesn't seem correct.
I don't know if this is a the correct way, or the angular way, but it works:
app.component.html:
Skip to main content
app.component.ts
export class AppComponent {
pageURL: string;
ngDoCheck() {
this.pageURL = window.location.href;
var link = this.pageURL.indexOf('#content-start');
if (link > -1) {
console.log(this.pageURL.substring(0, link));
this.pageURL = this.pageURL.substring(0, link);
}
}
}
every-page.html:
<h1 class="content-body" id="content-start">Page</h1>
Not sure if ngDoCheck is the appropriate event use.
Full disclosure: app.component is a wrapper - with header and the 'skip to content' link that contains all the pages and their content.
So, the logic for the 'skip to content' link exists once, while the actual content target #content-start lives in the top of each content page.

Angular 1.5.7 Controller Scope Failing in iOS 9.3+

I've been struggling a bit to successful get the following Angular 1.5.7 app to properly display user data injected into the Scope of my Controller. It works great on all browsers within a Windows Desktop environment but fails to display the data (it merely displays the template bindings) in iOS 9.3.5.
The Router:
routingApp
.config(function ($routeProvider) {
$routeProvider
.when('/user', {
controller: 'userController',
templateUrl: 'app/partials/userPartial.html'
})
.when('/contacts', {
controller: 'contactController',
templateUrl: 'app/partials/contactPartial.html'
})
.otherwise('/');
});
The Controller:
routingApp
.controller('userController', function ($scope, User) {
//Specify accessible controller attributes
$scope.User = new User();
});
The Partial:
<div class="user" id="userWrapper">
<h1 class="header">Contact Information</h1>
<span class="userField">Name: {{User.name}}</span>
<span class="userField">Phone: {{User.phone}}</span>
<span class="userField">Email: {{User.email}}</span>
<h1 class="header">Address Information</h1>
<span class="userField">Street: {{User.address.street}}</span>
<span class="userField">Suite: {{User.address.suite}}</span>
<span class="userField">City: {{User.address.city}}</span>
<span class="userField">Zipcode: {{User.address.zipcode}}</span>
</div>
See the example here: http://changelib.com/routing/
Full code is here: https://github.com/Thoughtscript/demo-angular_routing
Comments: I've heard that ng-App requires some additional configuration for iOS. Not sure if that's true but I've tried a number of things including directly bootstrapping the application (not sure if I did this correctly), switching to another router library (am using angular-route.min.js), and a couple smaller changes that didn't improve the situation. Any help is much appreciated! Thanks!
Was an idiot - some unsupported ES6 code and uncommitted changes to router dependencies fixed the issue. To be clear, the code above was fine (the problem lay in the factory and service). One take away, double-check ES6 support in iOS.

Binding lost (after click) in angular js small example

I have a very small application in Angular JS. It's placed inside a bigger rails application, but I don't see too much interaction. The angular application, allows the user to interact with a group of categories. As easy as:
var angular_app = angular.module('angular_app', []);
angular_app.config(['$httpProvider', function($httpProvider, $cookieStore) {
//Protection
}]);
angular_app.controller('CategoriesController', function ($scope, $http) {
$scope.isEditing = false;
$scope.categoryName = '';
$http.get('/api/categories').success(function(data) {
//We use this to data-bind with the HTML placed below
$scope.categories = data;
});
$scope.addNewCategory = function() {
...
}
$scope.editCategory = function(index) {
if (!index)
return;
var selectedCategory = $scope.categories[index];
// With ng-show, we make visible the part of the UI
// that should be used for editing
$scope.isEditing = true;
}
$scope.cancelEditCategory = function() {
$scope.isEditing = false;
}
$scope.deleteCategory = function(index) {
...
}
});
angular.element(document).ready(function() {
angular.bootstrap(document, ['angular_app']);
});
The idea is that the information is shown in a list, and we have an 'edit' button that allows the user to see other part of the UI that will let him perform changes.
<div ng-controller="CategoriesController">
<div ng-show='isEditing' class="popup_menu">
DIV FOR EDITING
</div>
<ul>
<li ng-repeat="category in categories">
<a href="#" ng-click='deleteCategory($index)'>[X]</a>
<a href="#" ng-click='editCategory($index)'>[E]</a>{{ category.name }}
</li>
</ul>
<input type="text" id="categoryTextBox" ng-model="categoryName"/>
<button id="submit" ng-click='addNewCategory()'>New category</button>
</div>
When I'm clicking the edit button, the corresponding part of the UI gets visible, but just after that, something happens, and the ul that should render the list, looses completely the binding, just showing something like:
[X] [E]{{ category.name }}
When it must be showing:
[X] [E]computer science
[X] [E]politics
[X] [E]news
(Which is what I have in the scope). It happens a few after the click (and works for a sec). No errors on the console, no interactions with other libraries (as far as I can see).
Thanks!
Turbolinks
I have no experience with Angular, but perhaps your problem could be to do with Turbolinks - this is a way of Rails loading the <body> tag of a page only - keeping the <head> intact.
Turbolinks is notorious for Javascript on Rails, as each time you reload your <body> without reloading the <head> part of your page, all your JS bindings are going to disappear. The solution to this, in normal JS, is to use JQuery / Javascript delegation, and delegate from the document object:
$(document).on("action", "delegated_object", function(){
...
});
Apologies if this does not work - it's a common issue for us, but as I have no experience with Angular, I don't know if it's going to help you or not.
It seems that I should have been more careful with the links:
<a href="#" ng-click='deleteCategory($index)'>[X]</a>
<a href="#" ng-click='editCategory($index)'>[E]</a>{{ category.name }}
Don't know exactly how this works, but seems that if the link has his href attribute, a GET request is made against 127.0.0.1, breaking in some way the angular code. If you put them like:
<a ng-click='deleteCategory($index)'>[X]</a>
<a ng-click='editCategory($index)'>[E]</a>{{ category.name }}
The problem will be solved. Thanks all for reading and helping!

Preserve the opened/closed state of my sub-menus when navigate to another page

I have an asp.net mvc4 solution.
I have a menu on the left side. By default this menu is closed.
If I click on an item, a sub-menu is showed.
The problem is when I navigate to another page, the opened/closed state of sub-menus are forgotten. The new page is showed and all sub-menus are still closed. I would like to preserve the opend/closed state of these sub-menus. How can I proceed?
Here is a portion of my left side menu:
<div class="page-sidebar">
<ul>
<li class="dropdown" data-role="dropdown">
<a><i class="icon-flip-2"></i> Transports</a>
<ul class="sub-menu light sidebar-dropdown-menu">
<li>#Html.ActionLink("En cours", "SearchTransportsAA", "Transp")</a></li>
<li>#Html.ActionLink("Passés", "SearchTransportsBB", "Transp")</a></li>
<li>#Html.ActionLink("Factures", "SearchTransportsCC", "Transp")</a></li>
</ul>
</li>
<li class="dropdown" data-role="dropdown">
<a><i class="icon-drawer-2"></i> Autorisations</a>
<ul class="sub-menu light sidebar-dropdown-menu open">
<li>#Html.ActionLink("Valides", "SearchAutorisAA", "Transp")</a></li>
<li>#Html.ActionLink("Périmés", "SearchAutorisBB", "Transp")</a></li>
<li>#Html.ActionLink("Recherche", "SearchAutorisCC", "Transp")</a></li>
</ul>
</li>
...
...
As you can see above, when 'open' is added to the class, the menu is marked to be open.
One way you could do it, is to have your Views that have this side menu inherit from a model that have property that contains the selected sub-menu item
public class ViewWithSideMenu
{
public ViewWithSideMenu(string menuItem)
{
MenuItem = menuItem;
}
public string MenuItem { get;set; }
}
ViewModel:
public class MyViewmModel : ViewWithSideMenu
{
public MyViewmModel() : base("someMenu") {}
}
View:
<ul class="sub-menu light sidebar-dropdown-menu#(Model.MenuItem == "someMenu"? " open" : "")">
this approach will only open a menu based on the page we are on, it doesn't really remember what the user clicked on, if it's very important for you to keep the user selections, you have 2 options,
keep sending them back to the server with every request (not very good, too much overhead)
keep updating the content using ajax requests and not a full reload of the page, in which case you keep the menu unchanged, and convert all your requests to ajax, and replace the content with the ajax response
You can try this, it worked for me :D
<script type="text/javascript">
$(document).ready(function () {
$("a[href='"+ window.location.pathname +"']").parents(".hidden-ul").css("display", "block");
});
</script>

changePage "jumps" back to old page

I've a big problem with a jQuery Mobile Application:
I'm using custom functions (they are triggered by onClick) to switch the page with currentPage.
It only happens on Android-Devices on sites in which has changed (due to ajax requests) with the integrated browser. iOS and Chrome works nice.
After clicking on an element, the animation started but just before it ends, it switches back to the old page. After a half second, it switches back to the new.
I made a movie of the bug here: http://www.youtube.com/watch?v=sXxvVUxniNg
Thank you very much
Code (CoffeeScript):
class Guide
#categoriesLoaded = false
#loadSearch: ->
$.mobile.changePage $("#guide"),
transition: 'slide'
changeHash: false
if !#categoriesLoaded
#categoriesLoaded = true
GuideApi.getCategories (data) ->
output = Mustache.render $("#tmpl-guide-categories-select").html(),
categories: data
$("#guide-search-category").append output
$("#guide-search-category").val($("#guide-search-category option:first").val());
window.WgSwitchGuide = ->
Guide.loadSearch
I was having the same issue. And I tried everything, I finally end with the solution. What I found was the error was principally within the browser. So I set the configuration of the pushStateEnabled as false. I did it by doing the following, adding this script.
<script type="text/javascript">
$(document).bind("mobileinit", function(){
$.mobile.pushStateEnabled = false;
});
</script>
It should be add before the jquery-mobile script is call, for more information you could see it on JQuery description
And it solved the problem no more jumping back.
I was having the exact same issue on both android and ios. For me, it was happening for heavy pages, i.e., pages with complex elements etc. Looks like you are using "slide" transition, which was what I was using as well. Taking out the page transitions (i.e., $.mobile.changePage("page.html", { transition: "none" })) for those pages resolved this issue for me. Hope this helps.
If you want to retain the transition, you can try preloading the page first when the previous page is being shown, by using the $.mobile.loadPage, and then show the transition. I am myself exploring this route, but it is probably worth trying.
Edit: OK - I explored the last suggestion and this doesn't seem to be working. Will stick with the first option.
Would you try to add the event stopPropagation and preventDefault methods on the first page's click event? This way the default action of the click event will not be triggered. Moreover the stopPropagation prevents the event from bubbling up the DOM tree, preventing any parent handlers from being notified of the event.
event.stopPropagation();
event.preventDefault();
Example:
$("p").click(function(event){
event.stopPropagation();
event.preventDefault();
// change page
});
After trying for weeks to find a solution to this, I ended up doctoring the JQM library to disable page transitions one right after another. It's not a good solution, but it's the only thing I could get to work.
I was getting pages jumping back on both $.mobile.changePage and on anchor links. I used the slide transition, but removing it did not fix the problem. Setting pushStateEnabled to false did not work either. The jumps were happening on all devices and browsers (that I tested, anyway).
So here's what I did to the JQM library (v1.3.2).
Before the $.mobile.changePage function is defined, I added:
var justChangedPage = false;
Then within the function there's a line that goes:
if ( pbcEvent.isDefaultPrevented()) {
return;
}
which I changed to:
if ( pbcEvent.isDefaultPrevented() || justChangedPage) {
return;
}
Then right after this part of the $.mobile.changePage function:
if ( toPage[ 0 ] === $.mobile.firstPage[ 0 ] && !settings.dataUrl ) {
settings.dataUrl = documentUrl.hrefNoHash;
}
I added:
justChangedPage = true;
setTimeout(function() {
justChangedPage = false;
}, 500);
(Putting that earlier in the function didn't work -- all that stuff executes more than once within a single page transition. And half a second seemed to be the minimum timeout that prevented the page jumps.)
I hope this helps someone even if it is a hack...
What is your JQM and Android version?
I'm not sure If I understand correctly. I think transition flicker maybe come from the following assumption.
Heavy page DOM transition.
Using "translate3d" somewhere in css file.
Not using "H/W Acceleration" feature. Enable by add this line to your AndroidManifest.xml in <application>
android:hardwareAccelerated="true"
I encountered exactly the same behaviour and it seems that few people are having the same issue. At first I thought it is caused by jQuery mobile library. Later on, I manage to find where the problem came from and it is a bug in my own code.
I made a demo to explain the issue.
http://jsfiddle.net/pengyanb/6zvpgd4p/10/
Hopefully, this can be hint for people having the same problem.
$(document).on('pagebeforeshow', '#page2', function(){
console.log('Page2 before show');
var htmlGeneratedOnTheFly = '<ul data-role="listview" data-inset="true">';
for(var i=0; i<4; i++)
{
htmlGeneratedOnTheFly += '<li><a>Random html element</a></li><li data-role="list-divider"></li>';
}
htmlGeneratedOnTheFly += '</div>';
$('#page2UiContent').empty();
$('#page2UiContent').append(htmlGeneratedOnTheFly);
$('#page2UiContent').trigger('create');
//////////////////////////////////////////////////
//The following section is where the bug is generated.
//Each on "page2 before show event" will add a OK Button click handler.
//The handlers never get cleared.
//More and more handler is added to the Page2 OK button as pages going back and forth.
//Open the browser's console window to see multiple "Page 2 OK Button clicked!!!" lines on one button click.
//To fix the bug, move the following section out of the $(document).on('pagebeforeshow', '#page2', function(){});
//////////////////////////////////////////////////
$('#page2OkButton').click(function(){
console.log("Page 2 OK Button clicked!!!");
$.mobile.changePage('#page1', {transition:"flip"});
});
//////////////////////////////////////////////
//////////////////////////////////////////////
});
<link href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.css" rel="stylesheet"/>
<script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div data-role="page" id="page1" data-theme="a">
<div data-role="header" data-position="fixed">
<h5>Demo Page 1</h5>
</div>
<div data-role="main" class="ui-content">
<h2>jQuery mobile changepage jumps back to old page demo</h2>
<p>Click "Go To Page 2" button to go to page2</p>
<p>On Page2 click Ok Button to come back to page1</p>
<p>Keeping going back forth between two pages for few times.</p>
<p>Eventually, you will find that clicked on "Go To Page2" button to flip to Page2 but it soon jumps back to page1 automatically. </p>
<h2>Please read the comments in the javascript for explaination</h2>
Go To Page 2
</div>
</div>
<div data-role="page" id="page2" data-theme="a">
<div data-role="header" data-position="fixed">
<h5>Demo Page 2</h5>
</div>
<div id="page2UiContent" data-role="main" class="ui-content">
</div>
<div data-role="footer" data-position="fixed" style="text-align:center;">
<div data-role="navbar">
<ul>
<li><a id="page2OkButton" class="ui-btn ui-icon-check ui-btn-icon-left">OK</a></li>
</ul>
</div>
</div>
</div>

Resources