Expanding a Factory Object in AngularJS Version 1.4x

SOLUTION

Big shoutout to hege-hegedus for the helpful answer below. Implemented it in my code and it's working perfectly.

// NOTE: This function is called from another service, but I've removed some parts for brevity
// The serverObject being used is referred to as 'article'
var articleInstance = new Article(article);
console.log(articleInstance instanceof Article)
// true
console.log(articleInstance.isProduct(article))
// true (in my case)

/*
* Creating a constructor from the server article object using lodash
*/
ArticleConstructor.$inject = [];
function ArticleConstructor() {
    return function(data) {
        var keys = ['app_id', 'body', 'headline', 'object_type', 'url', 'status'
        ];
        _.assign(this, _.pick(data, keys));
    };
}

/*
* Extending the iief constuctor - ArticleConstructor
*/
Article.$inject = ['ArticleConstructor'];
function Article(ArticleConstructor) {

    function ArticleExtended(data) {
        ArticleConstructor.call(this, data);
    }

    // creating a new Article object with the ArticleConstructor prototype object and properties
    ArticleExtended.prototype = Object.create(ArticleConstructor.prototype);

    // making sure that Article inherits a constructor property from its prototype which is ArticleConstructor
    ArticleExtended.prototype.constructor = ArticleExtended;

    ArticleExtended.prototype.isProduct = function () {
        return this.object_type == 3;
    };

    ArticleExtended.prototype.hasImage = function () {
        return _.has(this, 'image');
    };

    return ArticleExtended;
}

I'm having trouble extending the factory object below. Although I'm using lodash to auto hydrate the factory constructor successfully, none of my original methods like isIcon() are executing properly. It gives an error message saying "isIcon is not a function." I've searched for solutions, but most of the constructor examples out there use the traditional return service; approach at the end of the object, which works fine but forces me to go back to a more manual way of building the constructor. I feel like I'm missing something obvious here.

I'm Using AngularJS 1.4.8

FACTORY OBJECT TO EXTEND

// This AJS factory seems to be causing issues
ImageUnableToExtendFn.$inject = ['IMG_TYPE'];
function ImageUnableToExtendFn(IMG_TYPE) {

  Image.prototype.isIcon = function (img) {
    return img.type === IMG_TYPE.ICON;
  };

 return function(data) {
    var keys = ['id', 'src', 'alt', 'type'];
       _.assign(this, _.pick(data, keys));
    };
});

I've attempted to extend the IIEF factory with angular.extend(), however, that also doesn't work as expected (example provided below):

angular.extend(imageOfUnableToExtendFn, {
    isIcon: function(img) {
        return img.type === IMG_TYPE.ICON;
    }
})

MORE DETAILED INFORMATION FOR REFERENCE

define([
   'angular',
   'lodash'

], function(angular, _) {
'use strict';

ImageService.$inject = ['ImageClassicFn', 'ImageUnableToExtendFn'];
function ImageService(ImageClassicFn, ImageUnableToExtendFn) {

    var imageService = {
        images: null,

        createInstance: function(serverImageObject) {
            var self = this,
                imageOfClassicFn,
                imageOfUnableToExtendFn,
                isIcon;

            if (angular.isDefined(serverImageObject)) {

                imageOfClassicFn = new ImageClassicFn();
                isIcon = imageOfClassicFn.isIcon(serverImageObject);
                console.log('IS ICON', isIcon);
                

                imageOfUnableToExtendFn = new ImageUnableToExtendFn(serverImageObject);
                
                isIcon = imageOfClassicFn.isIcon(serverImageObject);
                

                angular.extend(imageOfUnableToExtendFn, {
                    isIcon: function(img) {
                        return img.type === IMG_TYPE.ICON;
                    }
                })

                isIcon = imageOfClassicFn.isIcon(serverImageObject);
                
            }
        }
    };

    return imageService;
}

// Factory for Classic Image Functionality
ImageClassicFn.$inject = ['IMG_TYPE'];
function Image(IMG_TYPE) {

  function Image(id, src, alt, type) {
    this.id = id;
    this.src = src;
    this.alt = alt;
    this.type = type;
  }

  Image.prototype.isIcon = function (img) {
    return img.type === IMG_TYPE.ICON;
  };

    return Image;
});

// Factory for Unable To Extend Image Functionality
ImageUnableToExtendFn.$inject = ['IMG_TYPE'];
function Image(IMG_TYPE) {

  Image.prototype.isIcon = function (img) {
    return img.type === IMG_TYPE.ICON;
  };

return function(data) {
    var keys = ['id', 'src', 'alt', 'type'];
       _.assign(this, _.pick(data, keys));
    };
});


return angular.module('content.images', [

    ])
     .constant("IMG_TYPE", {
        "ICON": 1,
    })
    .factory('ImageClassicFn', ImageClassicFn)
    .factory('ImageUnableToExtendFn', ImageUnableToExtendFn)
    .service('ImageService', ImageService);

});

Answer №1

If you're looking to dive into subclassing in javascript, it can be a bit of a challenge. Check out this Stack Overflow post discussing javascript inheritance.

Typically, this is how subclassing is done, especially when using angular 1.x modules:

ImageClassicFactory.$inject = ['IMG_TYPE'];
function ImageClassicFactory(IMG_TYPE) {

  function ImageClassic(id, src, alt, type) {
    this.id = id;
    this.src = src;
    this.alt = alt;
    this.type = type;
  }

  ImageClassic.prototype.isIcon = function (img) {
    return img.type === IMG_TYPE.ICON;
  };

  return ImageClassic;
});
module.factory('ImageClassic', ImageClassicFactory);



ImageExtendedFactory.$inject = ['IMG_TYPE', 'ImageClassic'];
function ImageExtendedFactory(IMG_TYPE, ImageClassic) {

  function ImageExtended(id, src, alt, type) {
      ImageClassic.call(this, id, src, alt, type);
  }
  ImageExtended.prototype = Object.create(ImageClassic.prototype);
  ImageExtended.prototype.constructor = ImageExtended;

  ImageExtended.prototype.isIcon = function (img) {
    return img.type === IMG_TYPE.ICON;
  };

  return ImageExtended;
});

module.factory('ImageExtended', ImageExtendedFactory);

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Unable to render page with scrapy and javascript using splash

I am currently trying to crawl this specific page. Following a guide on Stack Overflow to complete this task, I attempted to render the webpage but faced issues. How can I resolve this problem? This is the command I used: scrapy shell 'http://local ...

jQuery functions seamlessly on Chrome, Firefox, and Internet Explorer, unfortunately it does not work on iPhone

The code functions properly on desktop browsers, but encounters issues on iPhone devices. I would greatly appreciate it if you could take a look and help me identify the problem. What do you think could be causing this discrepancy? <ol style="margin: ...

Angular: monitoring changes in HTML content within a Component or Directive

I have a situation where I am retrieving HTML content from a REST endpoint using a directive and inserting it into a div element using [innerHTML]. Once this HTML content is rendered, I would like to manipulate it by calling a global function. My approach ...

What is the best way to navigate users to a different page using AJAX upon receiving a successful response from PHP?

I am currently using a JavaScript function that is functioning correctly. It retrieves all error messages from the PHP file and displays them in a span with the ID "resto" without any issues. However, I now have a requirement where if the PHP file return ...

Steps to show a jquery modal popup when a button is clicked in mvc 4

In my MVC project, I have a button that should trigger a popup with a textbox and another button when clicked. However, my current implementation is causing the textbox and button to be displayed on the page initially instead of appearing in the popup as i ...

I am facing an issue where my simple three.js code is not displaying properly

I'm relatively new to three.js and HTML, but I have experience creating other JS files that import into HTML pages without any issues. However, I am confused as to why my basic box code is not showing up on the page when I import the JS file in the sa ...

The timestamp is currently displaying as 2014-11-02T05:00:00.000Z rather than the expected 2014-11-02 00:00:00

Issue: The SELECT * query is returning dates in the wrong format. I am using the mysql2 module to run connection.query() and pass data to a server-side variable, then accessing it on the client-side with AJAX. router.post('/applicants', functi ...

A guide to finding identical strings in JavaScript

I've got two arrays with the names of premier league players. I'm trying to connect them based on name as the player objects lack unique identifiers. Any tips on how I can perform a string comparison that will correctly match Zlatan Ibrahimovic ...

Unlocking the potential of Three.js with mouse picking

I am looking to implement object picking in the following code snippet: var Three = new function () { this.scene = new THREE.Scene() this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000) this.camera.po ...

The browser is throwing a TypeError indicating that the button is null, despite it appearing to

Would you like to create a webpage where the background color changes when a button is clicked? There seems to be a TypeError when loading in the browser, but everything works fine when the same JavaScript code is pasted into the console. Check out the c ...

The current Webpack configuration for production fails to account for importing CSS files

I am struggling to figure out how to properly load a static site that is not located in the root folder: let HWPTest = new HtmlWebpackPlugin({ template: __dirname + "/src/artists/test.html", filename: 'artists/test.html', favicon: &apos ...

Error message: Type validation failed - anticipated a string for built-in components or a class/function for composite components, instead received undefined

When trying to create a Navigation bar and running npm dev, it renders perfectly but the console displays the following warning: Warning: React.jsx: type is invalid -- expected a string (for built-in components) or a class/function (for composite componen ...

The bidirectional functionality of ng-model is not working as expected in my specific application

UPDATE: It seems that setting the controller in the view like ng-controller="SomethingController as Ctrl" and then using it in the model ng-model="Ctrl.myModel" actually works. How surprising! I have been wanting to integrate this particular directive int ...

What is the process for editing a JSON file and preserving the modifications?

I have a JSON file with a key (food_image) and I am looking to dynamically assign the index value in a loop to it, such as food_image0, food_image1 ... food_image{loop index}. After that, I aim to save this modified JSON file under a new name. All values ...

Combining arrays in a nested array with the help of Javascript/Vue.Js

I am faced with a JSON array containing 3 arrays that I need to merge into one array after fetching the JSON data. Although I attempted to do so using Vue.JS, the resulting array appears to be empty. MY FIRST ATTEMPT this.items = items; MY LATEST ATTEMP ...

Something seems off with the color of the getImageData when using Fabric JS getContext('2d')

Website: Currently, I am working on adding an eye dropper feature to my website. It functions perfectly at a resolution of 1080p, but when tested on higher or lower resolutions, it malfunctions. Here is the basic code snippet: var ctx = canvas.getContex ...

Positioning an element in the center of another using JQuery

Greetings! I am currently working with some elements that look like this: <div id="one">content</div> <div id="two">content</div> These elements are followed by another set of elements (without any parent, placed directly after th ...

Sophisticated jQuery templates

Working with jQuery templates can be straightforward for basic data structures. <script type="jquery/x-jquery-tmpl" id="myTemplate"> <li> ${Element} </li> </script> var elements = [ { Element: "Hydrogen" }, { Element: "Oxy ...

Error: Unspecified process.env property when using dotenv and node.js

I'm encountering an issue with the dotenv package. Here's the structure of my application folder: |_app_folder |_app.js |_password.env |_package.json Even though I have installed dotenv, the process.env variables are always u ...

An unexpected JavaScript error has occurred in the main process: TypeError - not enough arguments provided

During my attempt to package an electron project using the electron-packager npm module, I encountered an error when running the .exe file of the packaged product. The error specifically references app/dist/packaged-app-win32-x64... and you can see the err ...