Building on Angular service hierarchy with Object.create()

Exploring the concept of inheritance in Angular services is my current objective. I came across this helpful resource: . I am particularly interested in implementing the "Inject the parent" technique.

However, despite following the guidelines, I'm facing difficulties as it doesn't seem to function correctly, and I'm unable to pinpoint the issue.

var myApp = angular.module('myApp',[]);

angular.module('myApp').controller('MyCtrl', MyCtrl);
angular.module('myApp').factory('BaseModel', BaseModel);
angular.module('myApp').factory('ThreadModel', ThreadModel);
angular.module('myApp').factory('PostModel', PostModel);

function MyCtrl($scope, ThreadModel, PostModel) {
  $scope.tableNameForThreads = ThreadModel.getTableName();
  $scope.tableNameForPosts = PostModel.getTableName();
}

function BaseModel() {
  var tableName = "";

  var service = {
    init: init,
    getTableName: getTableName
  };

  return service;

  function getTableName() {
    return tableName;
  }

  function init(theTableName) {
    tableName = theTableName;
  }
}

function ThreadModel(BaseModel) {
  var service = Object.create(BaseModel);
  service.init("threads");
  return service;
}

function PostModel(BaseModel) {
  var service = Object.create(BaseModel);
  service.init("posts");
  return service;
}

After analysis, it appears that the function ThreadModel.getTableName() incorrectly returns "posts" instead of "threads".

I experimented with both Object.create(...) and angular.copy(BaseModel, this), but unfortunately, neither method seems to create a deep copy.

To replicate the issue and for further investigation, you can view the JSFIDDLE demo here: http://jsfiddle.net/dirkpostma/Lvc0u55v/3989/

I would appreciate any insights or guidance on what could be causing the problem in my implementation.

Answer №1

The issue lies in the current configuration using Object.create, which results in services sharing the same tableName variable stored within a common closure (BaseModel function). In simpler terms, the init method alters the identical local tableName variable.

A potential solution is as follows:

function BaseModel() {

  var service = {
    init: init,
    getTableName: getTableName
  };

  return service;

  function getTableName() {
    return this._tableName;
  }

  function init(theTableName) {
    this._tableName = theTableName;
  }
}

It's important to note that the getTableName and init methods now manipulate the instance property this._tableName, ensuring it's not shared between instances of TableModel and PostModel.

See demonstration here: http://jsfiddle.net/Lvc0u55v/3991/

Answer №2

After reading @dfsq's explanation and simple solution, I wanted to share my thoughts on this issue.

When you use Object.create(BaseModel), it creates a new object with a prototype that is the returned value of the BaseModel function. The init method in those child models modifies the tableName within the local scope of the BaseModel function. If you replace tableName with this.tableName, both the init and getTableName methods will modify/call the tableName property of the service variable within the functions of ThreadModel or PostModel. However, this may seem complicated.

In your situation, I would like to suggest a clearer solution for service inheritance. You may find another post on Stack Overflow interesting.

var myApp = angular.module('myApp', []);

angular.module('myApp').controller('MyCtrl', MyCtrl);
angular.module('myApp').service('BaseModel', BaseModel);
angular.module('myApp').service('ThreadModel', ['BaseModel', ThreadModel]);
angular.module('myApp').service('PostModel', ['BaseModel', PostModel]);

function MyCtrl($scope, ThreadModel, PostModel) {
  $scope.tableNameForThreads = ThreadModel.getTableName();
  $scope.tableNameForPosts = PostModel.getTableName();
}

function BaseModel() {
  this.tableName = "";

  this.getTableName = function() {
    return this.tableName;
  }

  this.init = function(theTableName) {
    this.tableName = theTableName;
  }
}

function ThreadModel(BaseModel) {
  angular.extend(ThreadModel.prototype, BaseModel);
  this.tableName = "threads";
}

function PostModel(BaseModel) {
  angular.extend(PostModel.prototype, BaseModel);
  this.tableName = "posts";
}

JSFiddle: http://jsfiddle.net/Lvc0u55v/3993/

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

Running numerous MEAN stack applications on a single VPS is a cost-effective and efficient

Currently, I am delving into the world of MEAN stack development and have successfully deployed an application to an AWS EC2 instance running Ubuntu. While everything is functioning properly, I'm curious about whether it's feasible to host multip ...

How to verify the parent nodes in a jstree

I have implemented a two state jstree. However, I am encountering an issue where it is not possible to select any other node in relation to a node. My goal is that when I click on a specific node, all of its parent nodes should also be checked. Any assist ...

Encountering the issue of "Cannot read properties of undefined" while attempting to pass data to a prop

Currently, I am developing a Vue application that heavily relies on charts. The issue lies in the fact that I am fetching data from the database individually for each chart component, resulting in multiple calls and a slower page load time. I am looking to ...

Restrict the amount of displayed information in Vue

I'm having trouble creating a Vue button that limits the display of items fetched from an API. The goal is to only show 3 out of 10 questions initially, and when the "showmore" button is clicked, another set of 3 should be displayed, and so on. Howeve ...

A guide on dynamically showcasing/summoning icons in react by utilizing array data

QUESTION: 1 (SOLVED!) https://i.stack.imgur.com/1M1K7.png What is the best way to display icons based on an array of data codes? const data = [{ img: '01d' }, { img: '02d' }] if(data) { data.map((item) => ( <img src={`./ ...

Is there a way to streamline and optimize this React/Material UI code for faster performance?

There seems to be a lot of repetition in the code that needs to be cleaned up. I'm wondering if the switch statement is necessary. It looks like it requires the muiTheme palette to be passed this way. Also, can these theme constants be placed in a sep ...

Sorting nested table rows in vueJS is an essential feature to enhance

I am working with a json object list (carriers) that looks like this: https://i.stack.imgur.com/0FAKw.png Within my *.vue file, I am rendering this using the following code: <tr v-for="carrier in this.carriers"> <td>{{ carrier.id ...

Attempting to clear the value of a state property using the delete method is proving to be ineffective

Within my React-component, there exists an optional property. Depending on whether this property is set or not, a modal dialog is displayed. Therefore, when the modal should be closed/hidden, the property must not be set. My state (in simplified form): i ...

Javascript splice method mistakenly eliminating the incorrect elements

I have an array of objects like the one below: [{"name":"Rain"},{"name":"Storm"},{"name":"Forest"}] These objects are indexed as follows: [0, 1, 2]. I'm attempting to delete an item at a specific position using this code: $scope.selectedSound ...

Is it possible to use the jQuery .ajax() method to parse a javascript object that contains XML data?

Currently, I am facing a challenge parsing a javascript object that contains xml data within the .ajax() call using jquery. Can anyone provide guidance on how to accomplish this task? Any help would be greatly appreciated. Thank you! ...

Angular - transform expression result using specific values

I am attempting to modify the output of an expression based on a set of options stored in an object. Currently, I have an expression {{ resultItem.OdometerUnit }} that can result in one of three variations: hours kilometers miles My goal is to first ch ...

Tips for passing a variable containing an image source from Node.js to a Jade file

Below is the code snippet from my index.js file, where I'm using express for routing: var express = require('express'); var router = express.Router(); /* GET home page. */ router.get('/', function(req, res){ var db = req.db; ...

What is the best way to assign a value to a button in Ionic/Angular?

Is there a way to bind the name and value attributes to a button? I am facing an issue with this and need a solution. I am attempting to achieve the following: <ion-button type="submit" color="primary" style="text-align:center& ...

Divide Angular ngFor into separate divs

Here is an example of my current array: [a, b, c, d, e, f, g, h, i] I am aiming to iterate through it using ngFor and split it into groups of 3 elements. The desired output should look like this: <div class="wrapper"> <div class="main"> ...

Styling with CSS: A guide to reducing paragraph margins within a div element

My current project involves using javascript to dynamically insert paragraphs inside a div element. Essentially, the script grabs the value entered into an input form when a button is clicked and adds it as a new paragraph within a specific div. However, I ...

AngularJS - Utilizing Google's Place Autocomplete API Key

Recently, I started digging into Google's APIs and attempting to integrate the Places Autocomplete API with Angular. Although I'm fairly new to autocomplete features in general, I haven't included the jQuery library in my project yet. I&apos ...

What is causing the role="status" attribute to malfunction?

I'm having an issue with the role="status" attribute in my code. When using a screen reader, the paragraph text doesn't get read once it's appended to the body. index.html: <!DOCTYPE html> <html> <head> <title> ...

When a function is called, retrieve a specific number of elements from an array using the slice method in

If I have an array of 15 objects, but I am only displaying 5 on the browser using this code: const displayedArray = array.slice(0,4) I also have a function that will load another 5 objects each time a user scrolls down. displayedArray.concat(array.slice ...

Adding a tooltip with a date format to a Highchart graph

Hey everyone, I'm currently working with a Highchart and I want to customize the tooltip value in a specific format. My categories and series are structured as follows: {"Categories":["2015-11-09","2015-11-08""2015-11-15"],"Series":[2,0,2]} Current ...

Optimizing Backend Access with Laravel and Vue JS: How to Choose the Most Effective Approach

Currently, I am utilizing Laravel API passport to handle authentication in my Single Page Application (SPA) built with Vue. At the moment, whenever I need to access the backend server, I have to include a header in order to be allowed through the protected ...