1

In my app I use ng-view to switch out my views. I need to manipulate elements such as change width and position. When I try to do it with on my controller I'm getting, as expected, Unable to get property 'setAttribute' of undefined or null reference.

I know this is happening since JS doesn't know the DOM has changed.

Since I'm running some heavy plugins to work with SQLite in Windows 8 I can't post too much code.

This is my template being loaded into the ng-view

<div id="productslist" class="container">

    <div class="product" ng-repeat="product in products">
        <div class="details">
            <img src="img/detail-list-back.png" class="texture" />
            <div class="heading">
                <p class="title">{{product.name}}</p>
                <img src="img/products/title-shadow.png" class="title-shadow"/>
            </div>
            <div class="text">
                <div class="text-container">
                    <p class="intro" ng-hide="product.intro == null">{{product.intro}}</p>
                    <p class="title">Curves</p>
                    {{product.prodText}}
                </div>
            </div>
        </div>

        <div class="image" style="background-image:url('img/products/nanoslim-bkg.jpg')"></div>
    </div>

</div>

some of my angular. the for loop is breaking since it doesn't know the sections exist:

var totalProd = res.rows.length;
var windowW = window.innerWidth;
var sections = document.querySelectorAll('#productslist .product');
var textArea = document.querySelectorAll('#productslist .text');
document.getElementById('productslist').setAttribute('style', 'width:' + (windowW * totalProd) + 'px');
for (var i = 0; i < sections.length; i++) {
    sections[i].setAttribute('style', 'width:' + windowW + 'px');
}

Everything else works fine. I'm trying to bypass this by using ng-style but having issues there.

dcp3450
  • 10,959
  • 23
  • 58
  • 110
  • 1
    It would be better to use $routrProvider to route through different screens. If it is just for panels or controls better make use of ng-if. By this javascript will be good enough to find DOM correctly. Better specify what is your actual issue with a bit of code. – Venu Jun 04 '15 at 05:40
  • can you put some code in plnkr.co – vijay Jun 04 '15 at 05:40
  • @Venu - I am using a `$routeProvidfer`. Basically after the new template is loaded I'm running the controller to set things like the section titles with using the controllers `$scope` and `ng-repeat`. I need to set the width of the sections based on screen size but JS doesn't see them in the dom. – dcp3450 Jun 04 '15 at 05:49
  • I've added some code – dcp3450 Jun 04 '15 at 05:54
  • Don't do DOM manipulations in controllers.This is a situation to use directives. You wouldn't need to check which pages you're on, because the pages themselves know what to do! :) – Devin H. Jun 04 '15 at 05:56
  • For reference: https://docs.angularjs.org/guide/directive#creating-a-directive-that-manipulates-the-dom – Devin H. Jun 04 '15 at 05:58
  • @DevinH. - Thanks, I'm still learning Angularjs. Any info you can share to show me how to use directives to achieve this? – dcp3450 Jun 04 '15 at 05:59

2 Answers2

1

In angular, DOM manipulations should be done in directives where possible. Here's an example of setting the width to the window width:

.directive('fullWidth', function() {

  function link(scope, element, attrs) {
     var windowW = window.innerWidth;
     element.css({width: windowW + 'px'});
  }

  return {
    link: link
  };
});

Now in your view

<div class="product" full-width ng-repeat="product in products">
Devin H.
  • 1,065
  • 1
  • 11
  • 19
  • considering I'll need to use something similar on multiple views it seems like this will allow me to do so – dcp3450 Jun 04 '15 at 06:07
  • Indeed it will. As long as the directive is loaded in the module that your controller is within, any view that uses that controller will be able to use it! – Devin H. Jun 04 '15 at 06:09
  • Yep, the app structure is fairly simple. Everything that angular is working with is in the same module and view. – dcp3450 Jun 04 '15 at 06:10
  • what does `function link()` do? – dcp3450 Jun 04 '15 at 06:13
  • It is used in the return object at the bottom of the directive function `return { link: link };` – Devin H. Jun 04 '15 at 06:14
  • Maybe that was a bit obvious - basically the link function is used in directives as a sort of controller. It has it's own scope, and also gets passed the element the directive is attached to. Highly recommend reading through the link I commented on the question, from the top to bottom. – Devin H. Jun 04 '15 at 06:18
0

Assuming that you have a different controller for each distinct view, there are possibilities that your DOM takes more time to load then your controller, leading to the issue Unable to get property 'setAttribute' of undefined or null reference.

You can instead put your DOM selector statement in a $timeout statement, with a delay of 1 or 2 seconds, that will give DOM enough time to load and prevent any such null reference issue. Something like this :

    myapp.controller("mycontroller"), function($scope, $timeout) {

        $timeout(function() {
            $("your-dom-selector").setAttribute("height", 500px);
        }, 2000);

    });

Alternatively a better approach would be to use a broadcast model, in which you can track the route change event, and as soon as the route change event succeeds, you can broadcast an event, with necessary details, that can be captured by the respective controller.

Former approach is easy to use, latter one is standard and guaranteed to be error-free.

More details upon $timeout here

Manish Kr. Shukla
  • 4,447
  • 1
  • 20
  • 35
  • Two things - 1. Use directives for DOM manipulations. 2. Always think through an Angular way before getting JQuery involved! A great guide for those with JQuery background: http://stackoverflow.com/questions/14994391/thinking-in-angularjs-if-i-have-a-jquery-background/15012542#15012542 – Devin H. Jun 04 '15 at 06:12
  • Agreed and accepted. However, this was just in case he's not going on with the learning curve of directives. I knew that answers related to directives will pop up, this is just in case if he wants a quick solution. – Manish Kr. Shukla Jun 04 '15 at 06:18
  • Ah, that makes sense. Well, now he has both solutions. I'd prefer the right solution over the quick solution to avoid maintenance issues later however. – Devin H. Jun 04 '15 at 06:20