Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding Masonry to Angular without JQuery

I am trying to get Masonry working as an Angular directive, which is in part documented online, although I am having the following issues on the following code:

HTML code:

<div ng-controller="GridCtrl" class="grid-wrapper">
    <div class="masonry">
        <div ng-repeat="item in gridItems" ng-class="item.class">
            <h3>{{item.name}}</h3>
            <img ng-src="{{item.image}}"/>
            <br>
            <button ng-repeat="button in item.buttons">{{button.text}}</button>
        </div>
    </div>
</div>

Angular directive code:

'use strict';

angular.module('HomeCourtArenaApp').directive('masonry', function ($parse) {
    return {
        restrict: 'AC',
        link: function (scope, elem, attrs) {
            elem.masonry({ itemSelector: '.masonry-item', columnWidth: $parse(attrs.masonry)(scope) });
        }
    };        
});

angular.module('HomeCourtArenaApp').directive('masonryItem', function () {
    return {
        restrict: 'AC',
        link: function (scope, elem, attrs) {
            elem.imagesLoaded(function () {
               elem.parents('.masonry').masonry('reload');
            });
        }
    };        
});

SCSS code:

.grid-wrapper {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    width: auto;
    padding-left: 40%;
    overflow-x: scroll;
    overflow-y: hidden;
    .masonry {
        position: absolute;
        width: 2600px;
        max-height: 1080px;
        .masonry-item,
        .poster {
            float: left;
            width: 465px;
            padding: 15px;
            margin: 12px 12px 0 0;
            box-shadow: 1px 1px 4px 3px rgba(55,55,55,0.25);
        }
        .masonry-item {
            background: #fafafa;
            height: 295px;
            h3 {
                width: 100%;
                text-align: left;
                font-size: 28px;
                color: #3D444A;
                display: block;
            }
            img {
                display: block;
                padding: 50px 0 10px;
                margin: 0 auto;
            }
        }
        .poster {
            height: 631px;
            background: #000;
            position: relative;
            h3 {
                color: #fff;
                font-family: 'adineuebold';
                font-size: 68px;
                position: absolute;
                top: 410px;
                left: 20px;
                z-index: 2;
            }
            img {
                position: absolute;
                left: 0;
                top: 0;
                z-index: 1;
                margin: 0;
                padding: 0;
            }
            button {
                position: absolute;
                z-index: 2;
                left: 20px;
                top: 590px;
            }
        }
        button {
            padding: 12px 15px;
            font-size: 15px;
            margin-right: 10px;
            font-family: 'adihausregular';
            color: #fff;
            text-transform: uppercase;
            border: none;
            background: linear-gradient(to bottom,  rgba(57,134,202,1) 0%,rgba(3,75,146,1) 100%);
            &:after {
                content: "";
                background: url('img/sprite.png') no-repeat -156px -9px;
                width: 16px;
                height: 16px;
                margin-left: 30px;
                display: inline-block;
            }
        }
    }
}

Then also, crucially, how my scripts are layered in my index file:

<script src="scripts/app.js"></script>
<script src="scripts/directives/masonry.js"></script>
<script src="scripts/controllers/main.js"></script>

I keep getting an error in the console which would suggest, I am not defining the masonry somewhere correctly:

Uncaught TypeError: Cannot call method 'create' of undefined

Then also:

TypeError: Object [object Object] has no method 'masonry'

Can anyone see where I am going wrong?

I would like to avoid using JQuery, possibly, as there is a newer Masonry available that doesn't use it.

like image 364
JohnRobertPett Avatar asked Jun 18 '13 12:06

JohnRobertPett


2 Answers

Ok, so after a serious hack/play, I have a solution for this: Where AngularJS is creating the view elements on the fly, there are many opportunities for error if the the 'ng-repeat' loads slower than the creation of masonry elements. I am sure there are more Angular style responses to this, but this allows for clean mark up and less Javascript needing to be written to achieve what is needed. Just add this code as a directive, add your child selector and then add 'masonry' to the parent div of the grid in your view:

'use strict';

app.directive('masonry', function ($parse) {
    return {
        link: function (scope, elem, attrs) {   
            setTimeout(function() {
                $(".masonry").masonry({
                    itemSelector : ".masonry-item"
                });
            }, 0);
        }
    };        
});
like image 83
JohnRobertPett Avatar answered Nov 20 '22 08:11

JohnRobertPett


It looks like you are not actually applying the directive within your html.

<div ng-controller="GridCtrl" class="grid-wrapper">
    <div class="masonry" masonry>
        <div ng-repeat="item in gridItems" ng-class="item.class" masonry-item>
            <h3>{{item.name}}</h3>
            <img ng-src="{{item.image}}"/>
            <br>
            <button ng-repeat="button in item.buttons">{{button.text}}</button>
        </div>
    </div>
</div>

It took me a while to make isotope work in my app, let me know if this helps and if the error you are receiving changes.

Also try and add the column width statically inside your directive for now and see what happens. The error TypeError: Object [object Object] has no method 'masonry' might relate to attrs.masonry:

angular.module('HomeCourtArenaApp').directive('masonry', function ($parse) {
    return {
        restrict: 'AC',
        link: function (scope, elem, attrs) {
            elem.masonry({ itemSelector: '.masonry-item', columnWidth: 200 });
        }
    };        
});

angular.module('HomeCourtArenaApp').directive('masonryItem', function () {
    return {
        restrict: 'AC',
        link: function (scope, elem, attrs) {
            elem.imagesLoaded(function () {
               elem.parents('.masonry').masonry('reload');
            });
        }
    };        
});
like image 35
Dine Avatar answered Nov 20 '22 10:11

Dine