2

I am relatively new to AngularJS.

I have a series of DIVs in a partial view. Each of the DIVs has a unique ID. I want to show / hide these DIVs based on a scope value (that matches one of the unique ID).

I can successfully write out the scope value in the view using something like {{showdivwithid}}

What would be the cleanest way to hide all the sibling divs that dont have an ID of {{showdivwithid}}

dave walker
  • 3,058
  • 1
  • 24
  • 30
eat-sleep-code
  • 4,753
  • 13
  • 52
  • 98
  • I posted an answer, but I'm wondering if it's really what you're getting at. If not, please provide some pseudocode to demonstrate more clearly what you'd like to see. – Marc Kline May 21 '14 at 23:11
  • maybe you can template a – dandavis May 22 '14 at 02:26
  • Your question title is a little misleading. I would recommend: Using Angular, how can I show a DOM element only if its ID matches a scope variable? – dave walker May 22 '14 at 05:46

2 Answers2

2

I think you are approaching the problem with a jQuery mindset.

Easiest solution is to not use the id of each div and use ngIf.

<div ng-if="showdivwithid==='firstDiv'">content here</div>
<div ng-if="showdivwithid==='secondDiv'">content here</div>
<div ng-if="showdivwithid==='thirdDiv'">content here</div>

If you don't mind the other elements to appear in the DOM, you can replace ng-if with ng-show.

Alternatively use a little directive like this:

app.directive("keepIfId", function(){
    return {
        restrict: 'A',
        transclude: true,
        scope: {},
        template: '<div ng-transclude></div>',
        link: function (scope, element, atts) {
            if(atts.id != atts.keepIfId){
                element.remove();
            }
        }
    };
});

HTML

<div id="el1" keep-if-id="{{showdivwithid}}">content here</div>
<div id="el2" keep-if-id="{{showdivwithid}}">content here</div>
<div id="el3" keep-if-id="{{showdivwithid}}">content here</div>
dave walker
  • 3,058
  • 1
  • 24
  • 30
  • You are definitely correct, I am thinking in jQuery mindset. I used your first example, but it doesn't appear to be working. You can look at http://eat-sleep-code.com/#!/blog/ in Chrome developer tools and see my logic. Click the blog titles. I have something like: `
    ` so that the post should show if the scope variable (called post) matches the ID or if it is empty. But it is showing no matter what. Even when the other item is clicked.
    – eat-sleep-code May 22 '14 at 04:48
  • You may be accessing the wrong scope. Find the ngIf directive in the angular source (search for ngIfDirective) and put a break point in the link function. Have a look at what is being passed in. – dave walker May 22 '14 at 05:12
  • I tried implementing the directive example and it didn't work either. No errors. Just doesn't hide any of the divs. Here are the files: * https://github.com/eat-sleep-code/eat-sleep-code.github.io/tree/master/scripts/blog.js * https://github.com/eat-sleep-code/eat-sleep-code.github.io/tree/master/scripts/app.js * https://github.com/eat-sleep-code/eat-sleep-code.github.io/blob/master/views/blog.html * https://github.com/eat-sleep-code/eat-sleep-code.github.io/blob/master/index.html – eat-sleep-code May 22 '14 at 18:08
  • Looks like you are dynamically generating Angular directives with jQuery in `blog.js`. This is a bad idea. It is not surprising they are not working as expected. Read [this post](http://stackoverflow.com/questions/14994391/how-do-i-think-in-angularjs-if-i-have-a-jquery-background/15012542#15012542) and rethink your architecture. – dave walker May 23 '14 at 01:13
  • Thanks @david004, as I stated right in my first sentence -- I am relatively new to AngularJs. But, I guess "It is fully extensible and works well with other libraries." from the AngularJS home page should be taken with a grain of salt. :-) I will read up on how to replace the jQuery pieces with AngularJS. – eat-sleep-code May 23 '14 at 01:48
  • It does work well with jQuery, you just need to know where to put that code. Usually in a directive. – dave walker May 23 '14 at 01:50
  • @eat-sleep-code, I updated my answer with an explanation of how to solve your problem. With plenty of warnings that it is not a good way to do things. – GregL May 23 '14 at 01:51
1

First, I want to echo @david004's answer, this is almost certainly not the correct way to solve an AngularJS problem. You can think of it this way: you are trying to make decisions on what to show based on something in the view (the id of an element), rather than the model, as Angular encourages as an MVC framework.

However, if you disagree and believe you have a legitimate use case for this functionality, then there is a way to do this that will work even if you change the id that you wish to view. The limitation with @david004's approach is that unless showdivwithid is set by the time the directive's link function runs, it won't work. And if the property on the scope changes later, the DOM will not update at all correctly.

So here is a similar but different directive approach that will give you conditional hiding of an element based on its id, and will update if the keep-if-id attribute value changes:

app.directive("keepIfId", function(){
    return {
        restrict: 'A',
        transclude: true,
        scope: {
          keepIfId: '@'
        },
        template: '<div ng-transclude ng-if="idMatches"></div>',
        link: function (scope, element, atts) {
            scope.idMatches = false;
            scope.$watch('keepIfId', function (id) {
              scope.idMatches = atts.id === id;
            });
        }
    };
});

Here is the Plunkr to see it in action.

Update: Why your directives aren't working

As mentioned in the comments on @david004's answer, you are definitely doing things in the wrong way (for AngularJS) by trying to create your article markup in blog.js using jQuery. You should instead be querying for the XML data in BlogController and populating a property on the scope with the results (in JSON/JS format) as an array. Then you use ng-repeat in your markup to repeat the markup for each item in the array.

However, if you must just "get it working", and with full knowledge that you are doing a hacky thing, and that the people who have to maintain your code may hate you for it, then know the following: AngularJS directives do not work until the markup is compiled (using the $compile service).

Compilation happens automatically for you if you use AngularJS the expected, correct way. For example, when using ng-view, after it loads the HTML for the view, it compiles it.

But since you are going "behind Angular's back" and adding DOM without telling it, it has no idea it needs to compile your new markup.

However, you can tell it to do so in your jQuery code (again, if you must).

First, get a reference to the $compile service from the AngularJS dependency injector, $injector:

var $compile = angular.element(document.body).injector().get('$compile');

Next, get the correct scope for the place in the DOM where you are adding these nodes:

var scope = angular.element('.blog-main').scope();

Finally, call $compile for each item, passing in the item markup and the scope:

var compiledNode = $compile(itm)(scope);

This gives you back a compiled node that you should be able to insert into the DOM correctly:

$('.blog-main').append(compiledNode);

Note: I am not 100% sure you can compile before inserting into the DOM like this.

So your final $.each() in blog.js should be something like:

var $compile = angular.element(document.body).injector().get('$compile'),
    scope = angular.element('.blog-main').scope();
$.each(items, function(idx, itm) {
      var compiledNode = $compile(itm)(scope);
      $('.blog-main').append(compiledNode); 
      compiledNode.readmore();
});
GregL
  • 37,147
  • 8
  • 62
  • 67