AngularJS: Auto-scroll to anchor point after state change - angularjs

I am looking to have a user click a link on a page, this will trigger a stateChange and lead them to a new state. what I want to achieve is when the state is finished loading it then scrolls to an anchor on the page specified by the link on the previous page.
to Do this I am using a combination of passing a $stateParam through a ui-sref like so:
<a ui-sref="stateParent.stateChild({id: 'practiceParam'})">goToPage</a>
then when the page is reached there is a div sitting on it with a directive attached that activates on $viewContentLoaded, so the DOM is rendered and I can search for an ID. the HTML div looks like this:
<div scroll-after-load ></div>
and my scrolling directive is as follows:
angular.module( 'app' ).directive('scrollAfterLoad', function() {
return {
restrict: 'A',
link: function(scope, $elm, attrs, $stateParams) {
scope.$on('$viewContentLoaded', function() {
console.log('scrollAfterLoad Directive Loaded, $stateParams are: ', $stateParams );
var idToScroll = attrs.href;
var $target;
if (idToScroll) {
$target = $(idToScroll);
// the -50 accounts for the top navbar which is fixed on the page and removed from pageflow
$("html,body").animate({scrollTop: $target.offset().top - 50}, "slow");
}
});
}
};
});
I have not bothered with setting the href on the div yet as I cannot access the passed parameter, am I accessing it incorrectly? I have also tried using a more standard state.go() to pass a stateParam but my result is still null.
Once I can pass a stateparam the Idea is just to add an href to the params that gets injected into the div on the newly loaded page and autoscroll to another div on that page with an ID that matches my passed parameter.
Also a side note, on my ui-view's i have autoscroll set to true so the pages automatically load at the top, I like this behaviour and it is the reason I need the state to complete loading before the scoll is activated.

Your problem is that you are trying to inject the $stateParams service into the link function. However, the correct place for this kind of injection is at the factory method, means the definition of the directive. A factory method is registered with a module - as in your case with the scrollAfterLoad directive. For more information check AngularJS Dependency Injection.
So back to your question: If you actually inject $stateParams into your factory method of your directive, means at the top you will be able to easily access the params. Your directive could look like this:
angular.module( 'app' ).directive('scrollAfterLoad', ['$stateParams', function($stateParams) {
return {
restrict: 'A',
link: function(scope, $elm, attrs) {
scope.$on('$viewContentLoaded', function() {
console.log('scrollAfterLoad Directive Loaded, $stateParams are: ', $stateParams );
var idToScroll = attrs.href;
var $target;
if (idToScroll) {
$target = $(idToScroll);
// the -50 accounts for the top navbar which is fixed on the page and removed from pageflow
$("html,body").animate({scrollTop: $target.offset().top - 50}, "slow");
}
});
}
};
}]);
Notice that I have removed the $stateParams from your link function and put it at the top. This way the service will be already available within the link function - actually within the whole directive.
I reproduced your scenario in this Plunker.

With angular-scroll you could do it like this:
Pass your scroll target id as state parameter to child state controller and then scroll to the element with scrollToAnimated(element).
If you'd like to have it reusable you could create a factory of that controller and inject it into resolve of child state. That should work too but I haven't tested it.
You probably need to retrigger the scrolling if you're on the child state and click on the state change link again. That's why I've added the method checkState that reloads the current state if we're on child state to repeat the scrolling.
Please have a look at the demo below or in this fiddle.
I think your directive is not working because you're not passing the href attribute to your directive.
You could fix this by passing href="targetId" to your directive where targetId is added with $scope.targetId = $stateParams.id in you child controller. Or you could access the targetId of $scope directly if you're not using an isolated scope.
Anyway I would do it like I did it in the demo or with a factory inside resolve.
angular.module('demoApp', ['ui.router', 'duScroll'])
.controller('MainControlller', MainController)
.config(Config);
function MainController($scope, $state) {
$scope.checkState = function() {
//console.log($state.current);
if($state.current.name == 'parent.child') {
//console.log('parent.child state');
// reload to retrigger scrolling again
$state.reload();
}
};
}
function Config($urlRouterProvider, $stateProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('parent', {
url: '/',
templateUrl: 'home.html'
})
.state('parent.child', {
url: 'child/:id',
templateUrl: 'child.html',
controller: function($scope, $stateParams, $document) {
var scrollElement = angular
.element(document.getElementById($stateParams.id));
console.log(scrollElement);
$document.scrollToElementAnimated(scrollElement);
}
});
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-scroll/0.7.3/angular-scroll.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.js"></script>
<div ng-app="demoApp" ng-controller="MainController">
<script type="text/ng-template" id="home.html">
<a ui-sref="parent.child({id: 'practiceParam'})" ng-click="checkState()">go to practice</a>
<div ui-view=""></div>
</script>
<script type="text/ng-template" id="child.html">
<!--scroll to practice
--><p>
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
</p><p>
Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
</p><p>
Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.</p>
<p>
Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
</p>
<p>
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
</p>
<h1 id="practiceParam">practice</h1>
<p>
Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
</p><p>
Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.</p>
<p>
Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
</p>
<a ui-sref="parent">back</a>
</script>
<div ui-view=""></div>
</div>

Related

how to convert component to pdf using jspdf and htm2canvas

i am trying to build cv maker web application.and i need to convert componet to pdf file.
here my code
const handelDownload = () => {
html2canvas(componentRef.current).then(function (canvas) {
const divImage = canvas.toDataURL("image/png");
const pdf = new jsPDF();
pdf.addImage(divImage, "PNG", 0, 0);
pdf.save("download.pdf");
});
};
return (
<>
<h1>hello world</h1>
<div ref={componentRef}>
<h1 className="text-4xl bg-red-500">My Component</h1>
<p className="text-lg">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vel
ipsum et velit posuere tempus sed ac ex. Vestibulum ante ipsum primis
in faucibus orci luctus et ultrices posuere cubilia curae; Integer
eget augue faucibus, placerat leo vitae, dignissim mi. Fusce id nibh
id risus congue consequat ac ut libero. Duis ut tincidunt turpis. Nam
scelerisque tortor in risus interdum consequat. Ut vel lectus sem.
Nulla hendrerit erat vel massa finibus, eget laoreet tellus ultrices.
Integer rhoncus lectus quis libero imperdiet, et viverra turpis
fermentum. Donec ullamcorper nisl eget felis dictum, at suscipit dolor
viverra. Etiam ullamcorper sem sit amet ante bibendum iaculis.
</p>
</div>
</>
problem is qulity of pdf is wrost and elements are not in the same position in the PDF as they are in the original component.
i also try reactpdf.

Multi-Column Page Layout using react-pdf

The above picture is an example of a two-column page layout system in MS Word. On the web, a similar layout can be achieved using the following code:
<style>
.two-col {
column-count: 2;
}
</style>
<div class="two-col">
Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum.
</div>
How can it be done with react-pdf?
I tried with flexbox styling but it only works for a single page; continuous text wrapping or page breaking is impossible.

Read more option in angularjs

I'm using this directive to implement a read-more in my ionic app.
My HTML looks like this:
<ion-list>
<ion-item class="item item-thumbnail-left item-text-wrap" ng-repeat="sh in schedule_hosts" type="item-text-wrap">
<img ng-src={{sh.imageURL}}>
<h2>{{sh.firstName}} {{sh.lastName}}</h2>
<p>{{sh.profession.EN}}</p>
<p read-more ng-model="content" words="true" length="5"></p>
</ion-item>
</ion-list>
And in my controller:
$scope.content = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi commodo, ipsum sed pharetra gravida, orci magna rhoncus neque, id pulvinar odio lorem non turpis. Nullam sit amet enim. Suspendisse id velit vitae ligula volutpat condimentum. Aliquam erat volutpat. Sed quis velit. Nulla facilisi. Nulla libero. Vivamus pharetra posuere sapien. Nam consectetuer. Sed aliquam, nunc eget euismod ullamcorper, lectus nunc ullamcorper orci, fermentum bibendum enim nibh eget ipsum. Donec porttitor ligula eu dolor. Maecenas vitae nulla consequat libero cursus venenatis. Nam magna enim, accumsan eu, blandit sed, blandit a, eros." +
"Quisque facilisis erat a dui. Nam malesuada ornare dolor. Cras gravida, diam sit amet rhoncus ornare, erat elit consectetuer erat, id egestas pede nibh eget odio. Proin tincidunt, velit vel porta elementum, magna diam molestie sapien, non aliquet massa pede eu diam. Aliquam iaculis. Fusce et ipsum et nulla tristique facilisis. Donec eget sem sit amet ligula viverra gravida. Etiam vehicula urna vel turpis. Suspendisse sagittis ante a urna. Morbi a est quis orci consequat rutrum. Nullam egestas feugiat felis. Integer adipiscing semper ligula. Nunc molestie, nisl sit amet cursus convallis, sapien lectus pretium metus, vitae pretium enim wisi id lectus." +
"Proin at eros non eros adipiscing mollis. Donec semper turpis sed diam. Sed consequat ligula nec tortor. Integer eget sem. Ut vitae enim eu est vehicula gravida. Morbi ipsum ipsum, porta nec, tempor id, auctor vitae, purus. Pellentesque neque. Nulla luctus erat vitae libero. Integer nec enim. Phasellus aliquam enim et tortor. Quisque aliquet, quam elementum condimentum feugiat, tellus odio consectetuer wisi, vel nonummy sem neque in elit. Curabitur eleifend wisi iaculis ipsum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In non velit non ligula laoreet ultrices. Praesent ultricies facilisis nisl. Vivamus luctus elit sit amet mi.";
This works fine, but as you can see, the content is now the same for every item.
I looking for a way to create a read more functionality that allows me to add the text in HTML, like this:
<p read-more ng-model="content" words="true" length="5">{{sh.content}}</p>
I'm very (very) new to Angularjs and Ionic, any help is appreciated!
It seems you looking for directives. The directive is a feature same as web components.
You issue may be solved like that.
read-more-content.html
<p ng-if="content" read-more ng-model="content" words="true" length="length">{{content}}</p>
read.more.content.js
angular.module('App')
.directive('readMoreContent', function () {
return {
restrict: 'E',
transclude: true,
scope: {
content: '=',
length: '='
},
templateUrl: 'app/components/_read-more.html'
};
});
And usage can be similar like that
<read-more-content content="content" length="5"></read-more-content>
My personal advice split your own code in angular on small components/directives. That allow you avoid lot's of headacke in nearest refactoring. I'm looking forward doing something similar to http://bradfrost.com/blog/post/atomic-web-design/ but in angular.

Angular Noob: $resource is not returning the record?

I'm on Rails + Angular. I created a route in rails
http://localhost:3000/api/products/30 that returns JSON:
{"id":30,"name":"blue / red / sky blue square eyeglasses","active":true,"page_title":"","meta_description":"","created_at":"2014-05-27T20:26:03.000Z","updated_at":"2014-07-25T14:42:19.000Z","vendor_id":4,"short_description":"Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum do","long_description":"Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum do","shape_id":4,"material_id":1,"category_id":3,"taxable":false,"products_colors":[{"id":31,"color_id":4,"product_id":30,"mens":true,"womens":true,"created_at":"2014-05-27T20:26:03.000Z","updated_at":"2014-05-28T19:05:13.000Z","mens_sort_order":1,"womens_sort_order":0,"color":{"id":4,"name":"sky blue","created_at":"2014-05-16T18:11:03.000Z","updated_at":"2014-05-16T18:11:03.000Z","image":{"url":"/images/no-image.png"}}},{"id":36,"color_id":1,"product_id":30,"mens":true,"womens":true,"created_at":"2014-05-28T20:19:40.000Z","updated_at":"2014-05-28T20:19:40.000Z","mens_sort_order":null,"womens_sort_order":null,"color":{"id":1,"name":"blue","created_at":"2014-05-16T17:01:26.000Z","updated_at":"2014-07-25T18:49:54.000Z","image":{"url":"/uploads/color/1/blackt.jpg"}}},{"id":37,"color_id":3,"product_id":30,"mens":false,"womens":true,"created_at":"2014-05-28T20:20:15.000Z","updated_at":"2014-05-28T20:20:15.000Z","mens_sort_order":null,"womens_sort_order":null,"color":{"id":3,"name":"red","created_at":"2014-05-16T18:10:25.000Z","updated_at":"2014-07-25T18:59:17.000Z","image":{"url":"/images/no-image.png"}}},{"id":38,"color_id":5,"product_id":30,"mens":true,"womens":true,"created_at":"2014-05-28T20:21:58.000Z","updated_at":"2014-05-28T20:21:58.000Z","mens_sort_order":null,"womens_sort_order":null,"color":{"id":5,"name":"pitch black","created_at":"2014-05-22T18:04:25.000Z","updated_at":"2014-05-22T18:04:25.000Z","image":{"url":"/images/no-image.png"}}}]}
However, when I try to retrieve this JSON in angular by using $resource, it's returning a Resource:
provider (should I be using a provider?):
app.provider("SingleProduct", function() {
this.$get = ['$resource', function($resource) {
return {
find: function(path) {
var Product = $resource("/api" + path);
return Product;
}
}
}];
});
controller:
app.controller('ProductCtrl', ['$scope', '$location', '$resource', 'SingleProduct', function($scope, $location, $resource, SingleProduct) {
// $location.path is equal to /products/30
var product = SingleProduct.find($location.path());
$scope.title = "fff";
$scope.product = product;
}]);
view:
<div ng-controller="ProductCtrl">
{{ title }} // prints out fff (CORRECT)
{{ product.name }} // prints out Resource (INCORRECT, should be "blue / red ...")
</div>
What's going on ? This technique worked when I did it for /api/products where it returned an array of products in JSON format.
You never actually call any method on the resource. Your .find returns a resource object with a specified path, now do something with said resource:
var product = SingleProduct.find($location.path());
var products = product.get(); //something like this

Angular Kendo UI Tabstrip active state for dynamic tabs

I am trying to implement dynamic tabs using Kendo UI Tabstrip in Angular JS. How to indicate the active state? For example I want the second tab to be active by default.
Markup with directives
<div data-kendo-tab-strip="" data-k-animation="false" k-data-source="panes" k-data-text-field="'title'" k-data-content-field="'content'" ></div>
//Tabs controller
ngUI.controller('nguiTabsController', function($scope) {
$scope.panes = {
data : [ {title:"First", content:"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vitae dolor sed lacus interdum rutrum. Mauris mauris dui, rhoncus ut magna vitae, faucibus elementum lectus. "},
{title:"Second", content:"Sed auctor, turpis at scelerisque dapibus, dolor quam laoreet nibh, in pulvinar augue est a est. Nulla eu nulla nunc. Donec eu augue placerat, tincidunt diam vel, fringilla velit. Ut convallis faucibus neque. Nullam pellentesque, nisi quis facilisis tincidunt."},
{title:"Third", content:"Maecenas posuere tellus vel elit cursus porttitor. Proin auctor lorem risus, sit amet blandit ligula ultricies sit amet. Pellentesque eget velit ut libero faucibus lobortis."},
{title:"Fourth", content:"Suspendisse sodales consequat aliquet. Mauris ultricies nisl a metus convallis, at iaculis elit scelerisque. Nullam dignissim convallis lectus eu malesuada. Etiam libero mi, suscipit at auctor id, porta eu justo. Nullam a ipsum dictum, gravida erat vitae, sollicitudin justo."},
{title:"Fifth", content:"Phasellus suscipit ipsum molestie augue interdum sodales. Sed sit amet eros in odio viverra aliquam vitae nec odio. Nulla condimentum eleifend tortor, non malesuada purus placerat non. Mauris porttitor odio tortor."}]
};
});
Change the div to define the widget reference;
<div data-kendo-tab-strip="tabStrip"...
In the controller, create the tabStrip scope variable and place a watch on it. When it is set, select the desired tab by number.
ngUI.controller('nguiTabsController', function($scope) {
$scope.tabStrip = null;
$scope.$watch('tabStrip', function() {
$scope.tabStrip.select(0);
});
$scope.panes...
}

Resources