Is it possible to implement MV* in Polymer using models and services as polymer elements?

Imagine I want two views (polymer-elements) to share a model, how can this be achieved?

In Angular, the model would reside in a singleton service that is injected into the views, allowing both views to access the same data source.

I attempted to replicate this approach with Polymer using the following code:

<polymer-element name="view1">
  <template>
    <my-model></my-model>
    ...
  </template>
  ...
</polymer-element>

<polymer-element name="view2">
  <template>
    <my-model></my-model>
    ...
  </template>
  ...
</polymer-element>

This method appeals to me as it offers a declarative way of defining dependencies, resembling the functionality seen in <core-ajax> and other built-in Polymer elements.

However, one challenge I face is needing to wait for the domReady lifecycle callback before interacting with any element declared in the template. Currently, my initialization logic is held within this callback. The issue arises when this callback gets executed for each <my-model> element declared, potentially initializing the model multiple times due to its presence in both <view1> and <view2>. To enforce the singleton pattern for my model, I must relocate the state outside of the element instance. Here's an example of how I achieve this:

 <polymer-element name="my-model">
   <script>
    (function(){
      // private shared state
      var instances = [], registered; 
      var foo; 

      // element init logic
      Polymer('my-model', {
        domReady: function(){
          if (registered === (registered=true)) return;
          foo = 'something';
          this.addEventListener('foo', function(){ foo += 'baz'; });
        },
        attached: function() { instances.push(this); },
        detached: function(){
          instances = instances.filter(function(instance){
            return instance !== this;
          }.bind(this));
        },
        update: doSomething,
        get state() { return foo; }
      });

      function doSomething(){ foo += 'bar' }
    })();
  </script>
</polymer-element>

While this solution functions, it feels unconventional. Should I reconsider using <polymer-element> for implementing the singleton pattern? Are models and services better suited for another framework instead of Polymer? How do Polymer core-elements manage to handle these scenarios?

[EDIT] Event listeners have been added to the initialization code above. They are only registered in one instance to prevent triggering multiple times across various instances. What happens if the instance where event handlers are defined is removed? Will this disrupt the asynchronous logic?

Answer №1

This is how I would approach it:
Start by defining your model on the main page and then call it from your views.

If the model gets removed, you could:
1 - Listen for the "detached" lifecycle callback and register it imperatively inside it or
2 - Store data on a prototype built in a higher-level object and access it in your preferred way.
3 - As a backup plan, if all else fails (although I have not personally implemented this yet, as I usually communicate with PHP and pass around objects that need to be persistent), you could use a "prepareForRemoval" function. This involves leaving the instance, storing your data locally, executing number 1, and then using "recoverFromRemoval".

In my opinion, I am not a big fan of singletons. While Polymer offers powerful front-end capabilities, I'm not convinced it's the optimal approach.

The API docs do not mention the risk of getting disconnected (as shown here), but I do agree that there is a possibility of losing your data.
That's just my perspective, maybe individuals like @ebidel, @DocDude, or @dodson can shed more light on this topic. Unfortunately, they cannot be tagged here on SO, so I will reach out to them on G+ for clarification. Your question has piqued my interest.
By the way, why are you considering moving away from the main page? In Polymer, the focus should be on changing content dynamically rather than navigating away. What is the intended use case?


ps.: Apologies for not capitalizing proper nouns. Deal with it

EDIT (exceeded comments limit):

I may have misunderstood your query earlier. If I understand correctly now, yes, it will trigger multiple times (this is expected), but it should not exclude others when a specific view is removed.

Regarding your initialization logic, my suggestion would be to add a listener to the window or document (I lean towards window) itself and await the 'polymer-ready' event.

"To ensure my model adheres to the singleton pattern, I must move state outside of the element instance"

That's correct, but instead of waiting for domready in its prototype, utilize a constructor or similar method and invoke it as the callback for the mentioned event listener. I will revise my response for clarity once I am back home (feel free to let me know if it's unclear). I hope this explanation helps.

If you still have questions, I'll return shortly.

Answer №2

When it comes to browsers, the window object is considered a singleton by definition. A simple way to approach this is:

var window.instances = []; 
var window.registered;
var window.foo;

Alternatively, you can utilize the Polymer core-meta element like this:

<core-meta id="x-foo" label="foo"></core-meta>
<core-meta id="x-bar" label="bar"></core-meta>
<core-meta id="x-zot" label="zot"></core-meta>

<core-meta id="apple" label="apple" type="fruit"></core-meta>
<core-meta id="orange" label="orange" type="fruit"></core-meta>
<core-meta id="grape" label="grape" type="fruit"></core-meta>

<h2>meta-data</h2>

<template id="default" repeat="{{metadata}}">
<div>{{label}}</div>
</template>

<h2>meta-data (type: fruit)</h2>

<template id="fruit" repeat="{{metadata}}">
<div>{{label}}</div>
</template>

<script>

document.addEventListener('polymer-ready', function() {
  var meta = document.createElement('core-meta');
  document.querySelector('template#default').model = {
    metadata: meta.list
  };

  var fruitMeta = document.createElement('core-meta');
  fruitMeta.type = 'fruit';
  document.querySelector('template#fruit').model = {
    metadata: fruitMeta.list
  };
});

</script>

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

What is the process for consumers to provide constructor parameters in Angular 2?

Is it possible to modify the field of a component instance? Let's consider an example in test.component.ts: @Component({ selector: 'test', }) export class TestComponent { @Input() temp; temp2; constructor(arg) { ...

What is the benefit of using $q.all() with a single promise in AngularJS?

As I delve into this codebase, one snippet keeps popping up: $q.all([promise]).then(responseFunc); However, I find this confusing. After reading the documentation, I wonder why not simply use the following, as it's just a single promise... promise. ...

Showing live JSON data in AngularJS

Receiving a JSON string from a web service, the content may differ depending on the request. Here are 2 JSON results: Result 1 [ { "Key": 1, "ID": 1, "applicationId": "1", "applicationName": "APP1" }, { "Key": 2, "ID": 1, ...

Limit the number input to only allow values between 0 and 100

I am utilizing the Number input component from MUI, which includes Increment and Decrement buttons for adjusting numbers. Is there a method to limit the input to only accept values ranging from 0 to 100 ? Additionally, how can I decrease the width of the ...

Parsing JSON into a List of Objects

Here is a filter string in the following format: {"groupOp":"AND","rules":[{"field":"FName","op":"bw","data":"te"}]} I am looking to deserialize this into a Generic list of items. Any tips on how I can accomplish this? ...

Preventing closure of an angular-bootstrap dropdown (Removing a bound event from a directive to keep it open)

Currently, I am working with the Angular-Bootstrap Dropdown feature and I'm looking to make sure it doesn't close when clicked unless the user specifically chooses to do so. By default, the dropdown closes when clicking anywhere on the document. ...

What is the best way to ensure TypeScript recognizes a variable as a specific type throughout the code?

Due to compatibility issues with Internet Explorer, I find myself needing to create a custom Error that must be validated using the constructor. customError instanceof CustomError; // false customError.constructor === CustomError; // true But how can I m ...

Angular - The connections between deeply nested object models are established

I have an Angular application that is used to display company and contact person information in a text box format. Here is the code for displaying the Company Email address: <label> Company Email address</label> <input type="text& ...

Extending a type by adding properties from separate files using TypeScript

I am faced with a situation where I have a file containing either a type or interface variable (all of which are exported), and I need to add new properties to this variable from different files without using extends. Essentially, making changes to the sam ...

How to retrieve a refined selection of items

In my scenario, there are two important objects - dropdownOptions and totalItem https://i.sstatic.net/VreXd.png The requirement is as follows: If 12 < totalItems < 24, then display "Show 12, Show 24" If 24 < totalItems < 36, only show "Show ...

show data pulled from localStorage

I'm struggling to figure out how to use localStorage for the first time, specifically in storing an array of objects and displaying that information even after page refresh. Currently, I can see that it is being stored but not displayed. const book ...

Angular UI and Node backend causing CORS issues

Just diving into the world of node and could use a little assistance. Encountering this error message: Access to XMLHttpRequest at 'http://localhost:3000/api/auth/signin' from origin 'http://localhost:4200' has been blocked by CORS pol ...

Generate a random number to select a song file in Javascript: (Math.floor(Math.random() * songs) + 1) + '.mp3'

My current JavaScript code selects a random song from the assets/music folder and plays it: audio.src = path + 'assets/music/'+(Math.floor(Math.random() * songs) + 1)+'.mp3' However, I've noticed that sometimes the same trac ...

Dismiss the pop-up box by clicking outside its borders

I have created a unique homepage featuring pop-up boxes with login fields. The background dims when a pop-up appears, and the user should be able to close the pop-up by clicking outside the box boundaries. Although the function closeOverlay works when the ...

What is the reason for not encountering an error when reading an array beyond its index limit?

Recently, while working on a pre-existing website with JavaScript code, I made an interesting discovery. I noticed that the code was accessing array items in a unique way: var value = myArray[0, 1]; This seems to be retrieving the second item of the arr ...

Move the div containing an <audio></audio> tag

Is it possible to drag a div with a width of 200px and an element into a droppable area, and once the div is dropped, have its size change according to the sound duration (e.g. 1px per second)? Check out this example on jsFiddle. Below is the code snipp ...

Auto language-switch on page load

Wondering if using jQuery is the best approach to create a single French page on my predominantly English website. I want it to work across multiple browsers on pageload, currently using HTML replace. jQuery(function($) { $("body").children().each(funct ...

What is the best way to combine several packed props simultaneously?

After attempting the following, I encountered the following error: Unhandled Runtime Error TypeError: navbar.makeButtonClick is not a function Source .next/static/chunks/pages/index.js (19:29) @ onClick 17 | return( 18 | <button href='#&apo ...

React: Assigning a unique className to an element in a list

In the code snippet below, I am attempting to add a className based on the state of the checkbox. While the className is being added correctly, the issue arises when it gets applied to all elements in the list upon checking/unchecking any checkbox. I aim ...

Setting up the data for the AjaxAppender Request Log4Javascript

I am completely new to Log4Javascript and the concept of logging, so please bear with me. My current task involves sending logs to a CouchDB server using a POST request. However, I keep encountering an error from the server: {"error":"bad_request","reaso ...