Elsporko
Elsporko

Reputation: 561

Mixed HTML tags with Angularjs

I am playing around with angularjs and have run into what seems like it should be a trivial issue with data binding and HTML 5.

Let's say I have an array that represents a mix of image and video files:

  .controller('Foo', ['$scope', function($scope) {
  $scope.photos = [
    {
        Url: "img/2011-04-30\ 16.51.38.jpg",
    },
    {
        Url: "img/2011-04-30\ 16.51.53.jpg",
    },
    {
        Url: "img/2011-04-30\ 17.35.58.jpg",
    },
    {
        Url: "img/video-2011-04-30-16-52-23.mp4",
    },
    {
        Url: "img/video-2011-04-30-18-01-42.mp4",
    },
    {
        Url: "img/2011-04-30\ 16.51.47.jpg",
    },
    {
        Url: "img/2011-04-30\ 16.52.40.jpg",
    },
    {
        Url: "img/2011-04-30\ 18.02.26.jpg",
    },
    {
        Url: "img/video-2011-04-30-17-36-02.mp4",
    },
]}]);

I want to turn this array of urls into a mixed media slideshow so I am trying to set up an ng-repeat loop over the array and bind the data to <img> and <video> tags.

Obviously this is not going to produce what I want but here is my sample hg-repeat loop:

<div  ng-controller="Foo" ng-repeat = "photo in photos">
    <img ng-src="{{ photo.Url }}" class="photos" width="200" height="200" />
    <video src="{{ photo.Url }}" class="video" controls/>

Each iteration through the loop I want either an OR a but not both. One option is to create an array of html tags in the controller and use that array as the source of the ng-repeat but that is blurring the line between view and controller which does not feel right.

Is there a way to handle this natively in Angularjs?

Upvotes: 0

Views: 435

Answers (3)

Wesley Wigham
Wesley Wigham

Reputation: 261

Option three: Write a directive which displays only the correct tag based on the URL's extension.

For example:

<div ng-controller="SlideController">
  <div ng-repeat="slide in data">
    <media-slide url="slide.Url"></media-slide>
  </div>
</div>

And the js:

app.directive('mediaSlide', [function() {
  return {
    restrict: 'E',
    scope: {
      url: '='
    },
    link: function(scope, element, attrs, controllers) {
      var updateTag = function() {
        var parser = document.createElement('a');
        parser.href = scope.url;
        var parts = parser.pathname.split('.');
        if (parts.length<=1) {
          scope.extension = "";
          return;
        }
        scope.extension = parts[parts.length-1];
      }
      scope.$watch('url', updateTag);
      updateTag();
    },
    template: '<img ng-if="extension == \'jpg\'" ng-src="{{url}}" height="200" width="200"/>' +
              '<video ng-if="extension == \'mp4\'" src="{{url}}" height="200" width="200" controls/>' +
              '<span ng-if="extension == \'\'">{{url}}</span>'
  }
}]);

View it on plnkr

Any time you feel like you need to introduce a tad too much logic into a view, a directive is probably the way to go.

If you were struggling with figuring out how to select a specific tag in the view based on your data, most of the time conditional formatting is best accomplished with ng-show/ng-hide or ng-if.

Upvotes: 1

dddd1919
dddd1919

Reputation: 888

Lazy mode:

<div  ng-controller="Foo" ng-repeat = "photo in photos">
  <img ng-src="{{ photo.Url }}" ng-show="{{photo.Url.indexOf('jpg') > -1}}" class="photos" width="200" height="200" />
  <video src="{{ photo.Url }}" ng-show="{{photo.Url.indexOf('mp4') > -1}}" class="video" controls/>
<div>

Or

<div  ng-controller="Foo" ng-repeat = "photo in photos">
  <img ng-src="{{ photo.Url }}" ng-if="!{{photo.Url.indexOf('jpg') > -1}}" class="photos" width="200" height="200" />
  <video src="{{ photo.Url }}" ng-if="!{{photo.Url.indexOf('mp4') > -1}}" class="video" controls/>
<div>

Upvotes: 0

PSL
PSL

Reputation: 123739

One bad way to handle would be to check the url to see the extension.

<div  ng-controller="Foo" ng-repeat = "photo in photos" ng-init="isVideo=photo.Url.indexOf('.mpg') == photo.Url.length-4">
    <img ng-src="{{ photo.Url }}" class="photos" width="200" height="200" ng-if="!isVideo"/>
    <video src="{{ photo.Url }}" class="video" controls ng-if="isVideo" />

If you have to check for more extensions (ex: png, mp3 etc..) types this will get even uglier.

So you can move the logic to a function in the controller as well, but then it is still not a good way. Instead have your model do the work of proper data representation. Add properties to your model based on the mime type.

   $scope.photos = [{ //photos is probably not a good name for storing list of mixed medias
        Url: "img/2011-04-30\ 16.51.38.jpg",
        Type: 'image' //<-- add a property that exactly represents what type it is or just add a flag 
        isVideo: false
    },{
        Url: "img/2011-04-30\ 16.51.53.jpg",
        Type: 'video', 
        isVideo: true //<-- Derive this value based on some logic
    },

and apply that on your view.

Even better would be to have a directive return a specific template based on the type.

Upvotes: 1

Related Questions