jeudi 30 avril 2020

Ember @model is undefined

I have created a route /test in ember, and added a model in the route as well. But i am getting the model as undefined, and the list is not being printed.

router.js

import EmberRouter from '@ember/routing/router';
import config from './config/environment';

const Router = EmberRouter.extend({
  location: config.locationType,
  rootURL: config.rootURL
});

Router.map(function() {
  this.route('test');
});

export default Router;

routes/test.js

import Route from '@ember/routing/route';

export default class TestsRoute extends Route {
  model() {
    return ['abc'];
  }
}

test.hbs


<ul>
  
    <li></li>
  
</ul>



EmberJS: Set environment variables in index.html

We have an Ember (3.5) application. For technical reasons we need environment variables to be set on page load, as opposed to build time. We're trying to set them in index.html the following way:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>App</title>
    <meta name="description" content="">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    
    <script type="application/javascript">
      // Object.assign polyfill
      Object.assign||Object.defineProperty(Object,"assign",{enumerable:!1,configurable:!0,writable:!0,value:function(e,r){"use strict";if(null==e)throw new TypeError("Cannot convert first argument to object");for(var t=Object(e),n=1;n<arguments.length;n++){var o=arguments[n];if(null!=o)for(var a=Object.keys(Object(o)),c=0,b=a.length;c<b;c++){var i=a[c],l=Object.getOwnPropertyDescriptor(o,i);void 0!==l&&l.enumerable&&(t[i]=o[i])}}return t}});
      window.env = {};

      var request = new XMLHttpRequest();
      request.open('GET', '/api/frontend_settings', true);
      request.send(null);

      request.addEventListener('readystatechange', () => {
        if (request.status === 200) {
          if (request.readyState === 4) {
            Object.assign(window.env, JSON.parse(request.response).settings);
          }
        }
      }, false);
    </script>

    <link integrity="" rel="stylesheet" href="assets/vendor.css">
    <link integrity="" rel="stylesheet" href="assets/app-frontend.css">

    
  </head>
  <body>
    <script integrity="" src="assets/vendor.js"></script>
    <script integrity="" src="assets/app-frontend.js"></script>
  </body>
</html>

We added a script which makes a request to some endpoint (/api/frontend_env_vars) in the snippet. This endpoint responds with a JSON with the key-values of environment variables which we then assign to window.env.

The problem we have is that sometimes Ember scripts load before the variables have been assigned (since we do a request that takes some time to complete), which makes the application crash.

We tried the following alteration to the script, but it didn't work (the error was different, though):

<script type="application/javascript">
  // Object.assign polyfill
  Object.assign||Object.defineProperty(Object,"assign",{enumerable:!1,configurable:!0,writable:!0,value:function(e,r){"use strict";if(null==e)throw new TypeError("Cannot convert first argument to object");for(var t=Object(e),n=1;n<arguments.length;n++){var o=arguments[n];if(null!=o)for(var a=Object.keys(Object(o)),c=0,b=a.length;c<b;c++){var i=a[c],l=Object.getOwnPropertyDescriptor(o,i);void 0!==l&&l.enumerable&&(t[i]=o[i])}}return t}});
  window.env = {};
  var request = new XMLHttpRequest();
  request.open('GET', '/api/frontend_env_vars', true);
  request.send(null);
  function loadScript(src) {
    const script = document.createElement('script');
    script.src = src;
    document.body.append(script);
  }
  request.addEventListener('readystatechange', () => {
    if (request.status === 200) {
      if (request.readyState === 4) {
        Object.assign(window.env, JSON.parse(request.response).settings);
        loadScript('assets/vendor.js');
        loadScript('assets/app-frontend.js');
      }
    }
  }, false);
</script>



Nested JS decorator get/set's, how to properly chain them?

The ember framework has adopted decorators aggressively. In order to utilize data binding now i have to decorate my properties with @tracked which gets me all my nice UI updates anytime i change a property.

     @tracked username = 'dave';

This works well, but i'm encountering some serious problems if i need to add a custom decorator on top of the tracked decorator.

    @typed(StateTrackMap)
    @tracked
    mapConfigsArray = [create(StateTrackMap)];

I'm able to get this to work by having my @typed decorator check to see if it is above another decorator or not.

export default function typed(classType) {
    let weak = new WeakMap();
    return function(object, property, descriptor) {
        return {
            get() {
                // Check if there is another decorator attached below us in the chain
                // i.e. "tracked"
                if (typeof descriptor.get == 'function') {
                    return descriptor.get.call(this);
                }
                // If we haven't initialized before but there is one ready, return that
                if (!weak.has(this) && typeof descriptor.initializer == 'function') {
                    weak.set(this, descriptor.initializer.call(this));
                }
                return weak.get(this);
            },
            set(value) {
                // my set code which does the type checking/converting this descriptor is for

                                // Apply the converted value to the lower level object
                // This may be the object itself, or it may be another setter in the chain
                if (typeof descriptor.set == 'function') {
                    descriptor.set.call(this, typedValue);
                } else {
                    return weak.set(this, typedValue);
                }
            }
        }
    }
}

But this feels, weird... and doesn't look like any of the usages of descriptors i've seen. Mostly because if i change the order of the decorators things explode

    @tracked
    @typed(StateTrackMap)
    mapConfigsArray = [create(StateTrackMap)];
index.js:172 Uncaught Error: Assertion Failed: The options object passed to tracked() may only contain a 'value' or 'initializer' property, not both.

So i guess my question is, what is the proper way to chain decorators that have get & set? It seems to me that the order of the decorators determines if i can go up/down the chain or not. Also it seems to me that this chaining logic has to be baked into every decorator or else it doesn't work. Is there some generic way i can pass decorators to other decorators?

I've seen some examples where i return the descriptor reference but that doesn't appear to help the problem here either as i am not quite sure how i can still inject my get/set on it without erasing the property property chain or getting into the same boat as above where my code has to be designed to work with other descriptors specifically.

export default function typed(classType) {
    return function(object, property, descriptor) {
        const set = descriptor.set;
        const get = descriptor.get;
        const weak = new WeakMap();

        descriptor.get = function() {
            if (typeof get == 'function') {
                return get.call(this);
            }
            // If we haven't initialized before but there is one ready, return that
            if (!weak.has(this) && typeof descriptor.initializer == 'function') {
                weak.set(this, descriptor.initializer.call(this));
            }
            return weak.get(this);
        }
        descriptor.set = function(value) {
            // My type checking / conversion code

            // Apply the converted value to the lower level object
            // This may be the object itself, or it may be another setter in the chain
            if (typeof set == 'function') {
                set.call(this, typedValue);
            } else {
                return weak.set(this, typedValue);
            }
        }
        return descriptor;
    }
}

BTW this method gives a different explosion.

Assertion Failed: You attempted to use @tracked on mapConfigsArray, but that element is not a class field.



mercredi 29 avril 2020

emberjs glimmer object set() with variable property name

I have a component in Ember 1.15 where I am trying to do something like

import { action, set } from '@ember/object';

@action
someMethod() {
  const value = ... // something random
  let propertyName = ... // some variable string
  set(this, propertyName, value);
}

It seems to be working fine in the browser but typescript is flagging the set line as an error (specifically the propertyName argument). So if it works, why doesn't typescript like it?

This also seems to be happening with get() where it doesn't like variable propertyNames like get(this, propertyName).




mardi 28 avril 2020

Call ember route from server side rendered app

So I currently have a rails 6.0 and ember (octane) 3.14 app. Ember is being included into the server-side rendered rails template. I configured the ember app to have a rootElement and rootURL as mentioned here: https://guides.emberjs.com/release/configuring-ember/embedding-applications/

This way, ember gets included fine. Problem is now, I would like to use an href or button from the rails template to call a route in the ember app.

My rails application.html:

<div id="ember" class="ember-application">
  <a href="/app/accounts/new">new Account</a>
</div>

The rootURL is configured to /app and the rootElement is #ember

An ember LinkTo element does not seem to have any special event handlers or anything. Those seem to sit on the rootElement so I had the idea of delegating the click event to the rootElement but no luck so far...

Any ideas how I could get this to work?




Ember Octane - access route's model from controller

I am trying to make ember & typescript work together - I created a route with controller. In route's model() method I fetch data from server. I previously was able to access these data in controller via model, but not now. model attribute in controller is undefined (event though the model() method works correctly).

model() {
    return new EmberPromise((resolve, reject) => {
        //fetch data from server
        resolve(dataFromServer);
    });
}

Can you advise me, how to access model attribute in ember octane & typescript world?

thanks




When to choose a framework like Ember over React?

What are the points, use cases or scenarios when one would choose a framework like EmberJS over React




lundi 27 avril 2020

Integrate Patternlab patterns into EmberJS structure

did anyone have any luck integrating patternlab and emberjs together? Ideally, I'd see ember feeding off a PL based UIkit to generate the available elements for use in an ember site, but I can't seem to find any real world examples.




dimanche 26 avril 2020

Ember Octane when calling a save on the model what is the default accept and content-type headers?

In Ember Octane, when calling a save on the model, which calls a save to the back-end (.NET CORE 3.1), what is the default accept and content-type headers? I have tried looking online, but I cannot seem to find anything in the documentation.

Is it application/json or application/vnd.api+json?




Ember & Typescript - how to inject router service

I am trying to inject RouterService into my controller:

import Controller from '@ember/controller';
import { action } from '@ember/object';
import { inject as service } from '@ember/service';
import RouterService from '@ember/routing/router-service';


export default class Search extends Controller.extend({
  // anything which *must* be merged to prototype here
}) {
    @service router!: RouterService;


    @action
    actionClick(){
        this.router.transitionTo('protected.apk.detail')
    }
}

// DO NOT DELETE: this is how TypeScript knows how to look up your controllers.
declare module '@ember/controller' {
  interface Registry {
    'search': Search;
  }
}

but I get an error: Error: Assertion Failed: Attempting to inject an unknown injection: 'service:router' I guess it's because there is not service:router, but router:main instead. Can you advise me, how to properly inject RouterService? This controller is in my engine. ember version: 3.18.0

thank you




How to figure out why error happen during running app in ember fastboot

I'm trying to integrate third-party npm library (editorjs like npm package npm i @editorjs/editorjs --save-dev) to ember application with auto loading "ember-auto-import": "^1.5.3",

Everything works fine without fastboot, but under fastboot application crashing with following error:

nodejs_1      |  TypeError: Cannot read property 'syscall' of null
nodejs_1      |     at AppendOpcodes.evaluate (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/@glimmer/runtime.js:2003:1)
nodejs_1      |     at LowLevelVM.evaluateSyscall (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/@glimmer/runtime.js:4923:1)
nodejs_1      |     at LowLevelVM.evaluateInner (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/@glimmer/runtime.js:4879:1)
nodejs_1      |     at LowLevelVM.evaluateOuter (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/@glimmer/runtime.js:4871:1)
nodejs_1      |     at JitVM.next (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/@glimmer/runtime.js:5815:1)
nodejs_1      |     at TemplateIteratorImpl.next (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/@glimmer/runtime.js:5944:1)
nodejs_1      |     at RootState.render (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/@ember/-internals/glimmer/index.js:8771:1)
nodejs_1      |     at runInAutotrackingTransaction (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/@glimmer/validator.js:106:1)
nodejs_1      |     at /tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/@ember/-internals/glimmer/index.js:9069:1
nodejs_1      |     at inTransaction (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/@glimmer/runtime.js:1959:1)
nodejs_1      |     at InertRenderer._renderRoots (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/@ember/-internals/glimmer/index.js:9043:1)
nodejs_1      |     at InertRenderer._renderRootsTransaction (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/@ember/-internals/glimmer/index.js:9104:1)
nodejs_1      |     at InertRenderer._revalidate (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/@ember/-internals/glimmer/index.js:9146:1)
nodejs_1      |     at invokeWithOnError (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/backburner.js:347:1)
nodejs_1      |     at Queue.flush (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/backburner.js:229:1)
nodejs_1      |     at DeferredActionQueues.flush (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/backburner.js:426:1)
nodejs_1      |     at Backburner._end (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/backburner.js:960:1)
nodejs_1      |     at Backburner.end (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/backburner.js:710:1)
nodejs_1      |     at Backburner._run (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/backburner.js:1009:1)
nodejs_1      |     at Backburner._join (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/backburner.js:989:1)
nodejs_1      |     at Backburner.join (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/backburner.js:760:1)
nodejs_1      |     at Array.loopEnd (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/@ember/-internals/glimmer/index.js:8888:1)
nodejs_1      |     at Backburner._trigger (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/backburner.js:1084:1)
nodejs_1      |     at Backburner._end (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/backburner.js:979:1)
nodejs_1      |     at Backburner.end (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/backburner.js:710:1)
nodejs_1      |     at Backburner._run (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/backburner.js:1009:1)
nodejs_1      |     at Backburner._join (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/backburner.js:989:1)
nodejs_1      |     at Backburner.join (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/backburner.js:760:1)
nodejs_1      |     at Array.loopEnd (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/@ember/-internals/glimmer/index.js:8888:1)
nodejs_1      |     at Backburner._trigger (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/backburner.js:1084:1)
nodejs_1      |     at Backburner._end (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/backburner.js:979:1)
nodejs_1      |     at Backburner.end (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/backburner.js:710:1)
nodejs_1      |     at Backburner._run (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/backburner.js:1009:1)
nodejs_1      |     at Backburner._join (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/backburner.js:989:1)
nodejs_1      |     at Backburner.join (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/backburner.js:760:1)
nodejs_1      |     at Function.join (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/@ember/runloop/index.js:168:1)
nodejs_1      |     at Object.hash.success (/tmp/broccoli-1Awdijh61s6pE/out-475-append_ember_auto_import_analyzer/assets/addon-tree-output/@ember-data/adapter/rest.js:919:1)
nodejs_1      |     at fire (/app/node_modules/jquery-deferred/lib/jquery-callbacks.js:78:30)
nodejs_1      |     at Object.fireWith (/app/node_modules/jquery-deferred/lib/jquery-callbacks.js:188:7)
nodejs_1      |     at Object.fire [as resolve] (/app/node_modules/jquery-deferred/lib/jquery-callbacks.js:195:10)
nodejs_1      |     at dataHandler (/app/node_modules/najax/lib/najax.js:167:13)
nodejs_1      |     at IncomingMessage.<anonymous> (/app/node_modules/najax/lib/najax.js:198:9)
nodejs_1      |     at IncomingMessage.emit (events.js:322:22)
nodejs_1      |     at IncomingMessage.EventEmitter.emit (domain.js:482:12)
nodejs_1      |     at endReadableNT (_stream_readable.js:1187:12)
nodejs_1      |     at processTicksAndRejections (internal/process/task_queues.js:84:21)

fastboot mode check like this.fastboot.isFastBoot not solve the problem and looks like error throwing even if I just import @editorjs/editorjs. Maybe someone came across, so ready for any information to get direction for digg in.

Example of created component:

import Component from '@ember/component';
import EditorJS from '@editorjs/editorjs';
import { inject as service } from '@ember/service';
export default Component.extend({
   fastboot: service(),
   didInsertElement() {
   if (!this.fastboot.isFastBoot) {
     const editor = new EditorJS({
    holder: this.elementId
    });
   }
   }
});

Environment:

DEBUG: -------------------------------
index.js:194 DEBUG: Ember             : 3.18.0
index.js:194 DEBUG: Ember Data        : 3.17.0
index.js:194 DEBUG: jQuery            : 3.5.0
index.js:194 DEBUG: Ember Bootstrap   : 2.8.1
index.js:194 DEBUG: -------------------------------



jeudi 23 avril 2020

Ember Octane and D3

I am trying to create a simple d-3 bar-chart in Ember and am getting the following errors:

12:3) Only string, number, symbol, boolean, null and undefined... are allowed as default properties

15:22) Do no use jQuery - this after I installed the optional features, including jQuery

Any help would be appreciated

import Component from '@ember/component';
import { select } from 'd3-selection';

export default Component.extend({
  // Initial values
  num_sent: [{quarter: '2001-03-31', count: 39}, {quarter: '2001-06-30', count: 66}, {quarter: '2001-09-30', count: 63}, {quarter: '2001-12-31', count: 68}],

  didInsertElement() {
    let svg = select(this.$('svg')[0]);

    svg.selectAll('rect').data(this.get('num_sent'))
      .enter()
      .append('rect')
      .attr('width', 20)
      .attr('height', num_sent => num_sent.count)
      .attr('x', (num_sent, index) => 25 * index);
  }
});```



mercredi 22 avril 2020

Ember build and sourcemaps

My Ember app is actually a child engine. Recently, I observed that the source maps are not working properly in Chrome. So, I have to open the generated engine.js to debug…Instead I want to be able to open the individual source modules/files written in ES6 using Ctrl + P in Chrome Sources and add breakpoints to debug.

I tried both the ways in ember-cli-build.js;

babel: { sourceMaps: ‘inline’ }

sourcemaps: { enabled: true, extensions: [‘js’] }

My question is am I doing something wrong with the above? Does the parent/host app have any impact on the generated source map OR is the child engine configuration for sourcemap totally independent of the parent config ?




Phoenix: Error when refer to exq_ui dependency via git

I use exq and exq_ui for background job processing and monitoring in my Phoenix application.

At first, I refer to exq_ui in mix.exs's deps like this:

{:exq_ui, "~> 0.11.0", only: [:dev, :qa, :stg, :prod]}

And it works fine.

But then I discover 2 bugs in the UI:

  • When I click on the tab Busy, it blows up on the server side, and show nothing in the tab Busy in UI.
  • In the tab Scheduled, when a job is passed args as a list of map, it is showed as [Object object] in the column Args, instead of the real content of the args.

I fix those bugs here in this PR to thee main official repo: https://github.com/akira/exq_ui/pull/89/files

But I cannot expect it will be merged anytime soon, so I change my dependency in mix.exs like this:

{:exq_ui, "~> 0.11.0",
  git: "https://github.com/chauhonglinh/exq_ui.git",
  branch: "feature/fix_busy_tab_in_exq_ui",
  only: [:dev, :qa, :stg, :prod]}

Now the trouble happens.

The mix deps.get, mix deps.compile and iex -S mix phx.server all run successfully.

But then when I browse to http://localhost:4040, the UI doesn't show up, and in the javascript console, there are errors:

Resource interpreted as Stylesheet but transferred with MIME type text/html: "http://localhost:4040/assets/vendor.css".
exq_ui:20 
Resource interpreted as Stylesheet but transferred with MIME type text/html: "http://localhost:4040/assets/exqui.css".
vendor.js:1 Uncaught SyntaxError: Unexpected token <
exqui.js:1 Uncaught SyntaxError: Unexpected token <

Question: Why did this error not happen with ordinary deps config, but it happened with deps config referred to a git repo?

How can I fix it?

I think React and Angular also have similar errors in certain situations, but it seems that nobody has a good fix for them, just some anecdotes here and there.




mardi 21 avril 2020

Does ember-power-select has a feature that mark as selected when a user input text that is not on the record?

I have a scenario where a user can input text and when he press Enter key it will tagged as selected. Question, how will I show the input text that should show as selected when I press Enter? Example: I typed Miguel and press enter it should show as one of the selected records

Addon: https://ember-power-select.com/docs/multiple-selection

enter image description here

/template.hbs

<PowerSelectMultiple
  @searchEnabled=true
  @options=
  @selected=
  @onInput=
  @onKeydown=
  @placeholder="Select some names..."
  @onChange= as |name|>
  
</PowerSelectMultiple>

/component.js

import Controller from '@ember/controller';
import { action } from "@ember/object";
import { tracked } from "@glimmer/tracking";

export default class extends Controller {
  @tracked
  fooBarBazList = [];

  @tracked
  fooBarBazSelectedFromInput = "";

  names = ['Stefan', 'Mike', 'Tomster', 'Pluto'];

  @action
  foo(value) {
    this.fooBarBazSelectedFromInput = value;
  }

  @action
  baz(value, event) {
    if(event.key === 'Enter') {
      let list = this.fooBarBazList;
      let selected = this.fooBarBazSelectedFromInput;
      list.pushObject(selected);
    }
  }
}



lundi 20 avril 2020

How can I search the name or the email using ember-power-select

I am using an addon called ember-power-select.

Github: https://github.com/cibernox/ember-power-select

Docs for search: https://ember-power-select.com/docs/the-search

Docs for Multiple select: https://ember-power-select.com/docs/multiple-selection/

Question: How can I search using name or email

/template.hbs

<PowerSelectMultiple
  @searchEnabled=true
  @options=
  @selected=
  @searchField="email"
  @placeholder="Select some names..."
  @onChange= as |author|>
  
</PowerSelectMultiple>

/controller.js

 authors = [
    { name: 'Foo', email: 'foo@gmail.com' },
    { name: 'Bar', email: 'bar@gmail.com'},
    { name: 'Baz' email:  'baz@gmai.com'}
  ]



dimanche 19 avril 2020

Ember Octane How to convert custom mixins

This question is related to: Does Ember Octane Route class support using mixins? Mixins are officially deprecated in Ember Octane.

Question:

What is the best option to replace Ember mixins with and how do I implement it?

Context:

I have custom mixins that expand functionality offered by ember-simple-auth (~v1.8.2), which was not available at the time the methods were created (see below). I am currently using ember-simple-auth 3.0.0 https://github.com/simplabs/ember-simple-auth. In the documentation on github, they appear to be using their own mixins on Ember Octane as you can see:

// my-engine/addon/routes/index.js
import Route from '@ember/routing/route';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';

export default class IndexRoute extends Route.extend(AuthenticatedRouteMixin) {
  triggerAuthentication() {
    this.transitionToExternal('login');
  }
}

It appears that I am not the only one having this issue as Simple Auth doesn't know what route they want to go down either: https://github.com/simplabs/ember-simple-auth/issues/2185

Options:

  • Pure native classes, sharing functionality via class inheritance.
  • Utility functions which can be imported and used in multiple classes.
  • Services which can be injected into multiple classes, sharing functionality and state between them.

I have the following authentication mixins:

  1. Application Route: This handles whether the user is logged in and whether the user has two-factor authentication (2FA) enabled, and if it is enabled, it verifies that the user has been authenticated through 2FA. This is because simple auth did not offer
  2. Authenticated Route: Makes sure the user is logged in. If they are not logged in then the user is routed to the login page. Once they login, they are routed back to the page that they initially tried to go to.
  3. UnAuthenticated Route: Prevents logged in users from going to certain routes that logged in users should not go to.



vendredi 17 avril 2020

Move highcharts yAxis.tickPositioner callback function outside to some util function

I'm trying to implement highcharts with multiple y-axis and need to set yAxis.tickPositioner callback function for each of the y-axis. So instead of writing the duplicate code in each callback I need to create a util function to which I can pass values like dataMin and dataMax which is only accessible in the tickPositioner callback function.

chartOptions {
    xAxis: {
        tickPositions: [0, 1, 2, 4, 8]
    },

    yAxis: {
        tickPositioner: function () {
            var positions = [],
                tick = Math.floor(this.dataMin),
                increment = Math.ceil((this.dataMax - this.dataMin) / 6);

            if (this.dataMax !== null && this.dataMin !== null) {
                for (tick; tick - increment <= this.dataMax; tick += increment) {
                    positions.push(tick);
                }
            }
            return positions;
        }
    },
}

instead I want something like this which has all the values like this.dataMin and this.dataMax available in the util function

    chartOptions {
        xAxis: {
            tickPositions: [0, 1, 2, 4, 8]
        },

        yAxis: {
            tickPositioner: this.utilFunction();
        },
    }

utilFunction() {
            var positions = [],
                tick = Math.floor(this.dataMin),
                increment = Math.ceil((this.dataMax - this.dataMin) / 6);

            if (this.dataMax !== null && this.dataMin !== null) {
                for (tick; tick - increment <= this.dataMax; tick += increment) {
                    positions.push(tick);
                }
            }
            return positions;
        }



How to set the release version as a part of Ember build?

I want to set the version on the frontend (a meta tag in index.html or a property on window once the code is loaded in the browser, or in some other way) as a part of the build/deployment process in Ember. What would be the ideal way to accomplish this? I need this for mapping sourcemaps to versions in Sentry.




jeudi 16 avril 2020

Refresh application route model after login with ember octane

I have an application template with a sidebar component. This sidebar component is passed the application route model to display in a list. The application route checks if the user is authenticated, and if not, skips the model load. The index route is not a protected route, so it will still display when not logged in, just with no user specific data. When the user logs in or attempts to use a protected route, they are redirected to a login route and back to the attempted route transition or index.

There doesn't appear to be any way to force the application route's model hook to refresh after login. I've tried moving the data load in the application route out to a service and calling a refresh method on the service from the login route, but that resulted in the same issue.

So, my main question is what is the recommended approach to loading data after login that is needed in the application template? Is my only option to move this sidebar component to another template that is only accessible after login? This feels harder than it should be, so I am assuming I'm missing some basic concepts here with data flow between routes/components! Thanks!

My Application Route (app/routes/application.js)

import Route from "@ember/routing/route";
import { inject as service } from "@ember/service";

export default class ApplicationRoute extends Route {
  @service router;
  @service session;

  model() {
    if (this.get('session').get('isAuthenticated'))
    {
      return this.store.findAll("project");
    }
  } 
}

Application Template (app/templates/application.hbs)

<HeadLayout />
<div class="wrapper">
    <SideBar @projects= />

    <div id="content">
        <NavBar />
        <div>
            
        </div>
    </div>
</div>

Sidebar component (app/components/side-bar.hbs)

<nav id="sidebar">
    <div class="container">        
        <div class="sidebar-content">
            ...
                            
                <div class="sidebar-projects">
                        ...
                        <div class="list-group">
                            
                                <button type="button">
                                    
                                </button>
                            
                        </div>
                    </div>   
            
                <p>Login to access projects.</p>
            
        </div>
    </div>
</nav>

My router (app/router.js)

import EmberRouter from '@ember/routing/router';
import config from './config/environment';

export default class Router extends EmberRouter {
  location = config.locationType;
  rootURL = config.rootURL;
}

Router.map(function() {
  this.route('login');

  this.authenticatedRoute('projects', { path: '/projects'}, function() {
    this.authenticatedRoute('new');
    this.authenticatedRoute('edit');
    this.authenticatedRoute('project', { path: ':project_id' });    
  });

  this.authenticatedRoute('photos', { path: '/photos'}, function() {
    this.authenticatedRoute('photo', {path: ':photo_id'});
    });
});



Difference in ember output based on environment

When running the ember build command

ember build --environment={testing or development or production or server}

Is there any difference in the actual dist output generated ? Can the same output be used across different envs?

I am aware about development having better error messages for debugging and similar differences...But wanted to know the exact differences if any e.g. base path in generated index.html or anything which can prevent them from being inter-operable across envs.




mercredi 15 avril 2020

Ember Octane: ember-g-recaptcha reCaptchaResponse comes back as undefined

I am upgrading to Ember Octane and I am using ember-g-recaptcha https://www.npmjs.com/package/ember-g-recaptcha. The project README.MD has not been updated to reflect Ember Octane. The reCaptchaResponse in the component js keeps coming back undefined. How do I fix this?

I have posted the Ember-Twiddle here https://ember-twiddle.com/509eb1c04c9c7d908d16ba2a2bb39ba5. Note: you will need to provide a sitekey to use it.




How to render nested route in new page Ember js?

I currently trying use LinkTo to render another nested route by using tag to another new page.

I have a forum route and nested forum details route

In the forum template:

<LinkTo @route="main.forum.forum-details" @model=></LinkTo>


enter image description here

As the image above. The nested route will be render at the bottom instead of to the new page. How am i going to render it to new page? I was thinking LinkTo will actually link it to the new page right? And the forum details should be render at the tag, so where should I place the tag in order to let it render to new page?




Issues with model when navigating while still being retrieved

Working with an older ember application (2.18.1). The following problem is repeated too many times to all fix in the time frame I got available right now.

The component is loading it's own data (setting this.get('model')) and all works fine.

However as the database is now a little slower the user sometimes click on one link, where the template render the component and it start loading it's data .

If the user click another link (to a route that does exactly the same) data from both the previous and the "new" component get loaded.

I can't reset the model when data get loaded, since the fetchRecord method that loads the data get called over and over with paging (as the user scroll down).

I'm sure I'm just not thinking of an obvious solution (did not work on Ember for a few years), any advise?

(ps: some of these components does not use paging, in mean time I'm going to clear out the model before setting it to what the api returns)




lundi 13 avril 2020

How do you create an ajax service in ember-cli 3.17?

I am recently trying to upgrade my ember from ember-cli: 2.6.2 to ember-cli: 3.17 and I am having a hard time trying to parallel my implementation of ajax service to the newer ember-cli version. I wasn't the one who created the app from the scratch that is why I do not know why the import { raw } from 'ic-ajax'; is needed. Any help are very much appreciated.

Error that I am getting: Uncaught (in promise) TypeError: Cannot read property 'status' of undefined

This is my old ajax service app/services/ajax.js

import Ember from 'ember';
import { raw } from 'ic-ajax';
import dasherizeObjectKeys from '../../utils/dasherize-object-keys';

const { inject, get, isEmpty } = Ember;

export default Ember.Service.extend({
  currentSession: inject.service(),

  request(url, method = 'GET', data = undefined, dataType = 'json') {
    data = JSON.stringify(dasherizeObjectKeys(data));
    return new Ember.RSVP.Promise((resolve, reject) => {
      const contentType = 'application/json';
      const token = this.get('currentSession.token');
      const headers= {
        'Authorization': `Token ${token}`,
        'X-Client-Platform': 'Web'
      };

      raw({ url, headers, data, type: method, dataType, contentType })
        .then((response) => this.handleSuccess(response, resolve))
        .catch((response) => this.handleError(response, reject));
    });
  },

  handleSuccess(response, resolve) {
    return resolve({
      status: response.jqXHR.status,
      response: response.response
    });
  },

  handleError(response, reject) {
    if (response.jqXHR.status === 401 && this.get('currentSession.isAuthenticated')) {
      this.get('currentSession').invalidate();
    }

    let error = get(response, 'jqXHR.responseJSON');
    if (isEmpty(error)) {
      const responseText = get(response, 'jqXHR.responseText');
      // FIXME: server should always return json, empty string is not json,
      // after backend is fixed, `JSON.parse` and try/catch for errors are not needed
      try {
        error = JSON.parse(responseText);
      } catch (e) {
        error = {
          errors: { detail: responseText }
        };
      }
    }

    error.status = response.jqXHR.status;
    reject(error);
  }
});

And this is my new ajax service app/services/request.js

import Service from '@ember/service';
import { Promise } from 'rsvp';
import fetch from 'fetch';
import dasherizeObjectKeys from '../../utils/dasherize-object-keys';

export default class RequestService extends Service {
  request(url, method = 'GET', data = undefined, dataType = 'json') {
    data = JSON.stringify(dasherizeObjectKeys(data));
    return new Promise((resolve, reject) => {
      const contentType = 'application/json';
      const token = this.get('currentSession.token');
      const headers= {
        'Authorization': `Token ${token}`,
        'X-Client-Platform': 'Web'
      };

      fetch({ url, headers, data, type: method, dataType, contentType })
        .then((raw) => this.handleSuccess(raw, resolve))
        .catch((raw) => this.handleError(raw, reject));
    })
  }

  handleSuccess(response, resolve) {
    return resolve({
      status: response.jqXHR.status,
      response: response.response
    });
  }

  handleError(response, reject) {
    if (response.jqXHR.status === 401 && this.get('currentSession.isAuthenticated')) {
      this.get('currentSession').invalidate();
    }

    let error = get(response, 'jqXHR.responseJSON');
    if (isEmpty(error)) {
      const responseText = get(response, 'jqXHR.responseText');
      // FIXME: server should always return json, empty string is not json,
      // after backend is fixed, `JSON.parse` and try/catch for errors are not needed
      try {
        error = JSON.parse(responseText);
      } catch (e) {
        error = {
          errors: { detail: responseText }
        };
      }
    }

    error.status = response.jqXHR.status;
    reject(error);
  }
}



dimanche 12 avril 2020

Ember model attribute is disappear

I am using ancient version of ember: "ember-cli": "^0.2.1"

When I load page first time, everything is good. But when I hit back on browser and go back and again click to same link and reopen page, my model attribute is empty Here is my property looks

duration: Ember.computed.alias('model.duration'),

duration is coming 0 when I open it second time. However, I can see it on rest API reply, and when I console.log I can access it like:

this.get('model')._data.duration;

Please help, this issue braking entire application




Dash-case on controllers routes with JsonApiDotNetCore

I'm trying to configure the JsonApiDotNetCore backend to communicate with my EmberJs frontend, but I have a problem with the naming convention for the controller. The backend is listening on http://localhost:3000/api/v1/articleCategories, but the frontend is requesting on http://localhost:3000/api/v1/article-categories. How can I change the routing on the backend side?




samedi 11 avril 2020

Returning 401 Unauthorized even the adapter is created

The request returned a 401 Unauthorized for GET /foos. From the guide that I read earlier, https://guides.emberjs.com/release/models/customizing-adapters/ it stated that the host name must defined

Question: WHy is it returning 401?

My app/adapters/application.js

  import JSONAPIAdapter from '@ember-data/adapter/json-api';

  export default class ApplicationAdapter extends JSONAPIAdapter {
    namespace = 'api/v1';
    host = 'https://someUrl.com';
  }

and my app/models/foo.js is

import Model, { attr } from '@ember-data/model';

export default class FoowModel extends Model {
  @attr('string') landmark;
};

and my app/routes/welcome.js is

import Route from '@ember/routing/route';

export default class WelcomeRoute extends Route {
  model() {
    return this.store.findAll('foo');
  }
}



For ember Froala Editor toolbar direction is not getting changed for RTL languages

import FroalaEditorComponent from 'ember-froala-editor/components/froala-editor'; export default FroalaEditorComponent.extend({ direction: 'rtl' });

For RTL language the editor is working fine. But the toolbar remains as LTR languages




vendredi 10 avril 2020

tinymce with babel: UnhandledPromiseRejectionWarning (when calling EmberJS npm start)

I have installed tinymce in my EmberJS application. When I run npm start or even npm run build, I get an error like so:

[Package /assets/vendor.js]/home/ikirkpat/Projects/proj_name/frontend/node_modules/typescript/lib/typescript.js:98681
                throw e;

Error: Debug Failure.
    at Object.assertDefined (/home/ikirkpat/Projects/proj_name/frontend/node_modules/typescript/lib/typescript.js:2227:24)
    at /home/ikirkpat/Projects/proj_name/frontend/node_modules/typescript/lib/typescript.js:39474:34
    at Object.filter (/home/ikirkpat/Projects/proj_name/frontend/node_modules/typescript/lib/typescript.js:513:31)
    at serializeAsClass (/home/ikirkpat/Projects/proj_name/frontend/node_modules/typescript/lib/typescript.js:39472:48)
    at serializeSymbolWorker (/home/ikirkpat/Projects/proj_name/frontend/node_modules/typescript/lib/typescript.js:39203:29)
    at serializeSymbol (/home/ikirkpat/Projects/proj_name/frontend/node_modules/typescript/lib/typescript.js:39144:38)
    at /home/ikirkpat/Projects/proj_name/frontend/node_modules/typescript/lib/typescript.js:39119:25
    at Map.forEach (<anonymous>)
    at visitSymbolTable (/home/ikirkpat/Projects/proj_name/frontend/node_modules/typescript/lib/typescript.js:39118:33)
    at symbolTableToDeclarationStatements (/home/ikirkpat/Projects/proj_name/frontend/node_modules/typescript/lib/typescript.js:38989:17)
⠧ building... [SassCompiler](node:14526) UnhandledPromiseRejectionWarning: Error: Debug Failure.
    at CommandCoordinator.dispatchResponse (/home/ikirkpat/Projects/proj_name/frontend/node_modules/stagehand/lib/command-coordinator.js:54:69)
    at CommandCoordinator.<anonymous> (/home/ikirkpat/Projects/proj_name/frontend/node_modules/stagehand/lib/command-coordinator.js:43:29)
    at Generator.next (<anonymous>)
    at /home/ikirkpat/Projects/proj_name/frontend/node_modules/stagehand/lib/command-coordinator.js:7:71
    at new Promise (<anonymous>)
    at __awaiter (/home/ikirkpat/Projects/proj_name/frontend/node_modules/stagehand/lib/command-coordinator.js:3:12)
    at CommandCoordinator.messageReceived (/home/ikirkpat/Projects/proj_name/frontend/node_modules/stagehand/lib/command-coordinator.js:40:16)
    at ChildProcess.emit (events.js:311:20)
    at emit (internal/child_process.js:876:12)
    at processTicksAndRejections (internal/process/task_queues.js:85:21)

(node:14526) UnhandledPromiseRejectionWarning: Error: Debug Failure.
    at CommandCoordinator.dispatchResponse (/home/ikirkpat/Projects/proj_name/frontend/node_modules/stagehand/lib/command-coordinator.js:54:69)
    at CommandCoordinator.<anonymous> (/home/ikirkpat/Projects/proj_name/frontend/node_modules/stagehand/lib/command-coordinator.js:43:29)
    at Generator.next (<anonymous>)
    at /home/ikirkpat/Projects/proj_name/frontend/node_modules/stagehand/lib/command-coordinator.js:7:71
    at new Promise (<anonymous>)
    at __awaiter (/home/ikirkpat/Projects/proj_name/frontend/node_modules/stagehand/lib/command-coordinator.js:3:12)
    at CommandCoordinator.messageReceived (/home/ikirkpat/Projects/proj_name/frontend/node_modules/stagehand/lib/command-coordinator.js:40:16)
    at ChildProcess.emit (events.js:311:20)
    at emit (internal/child_process.js:876:12)
    at processTicksAndRejections (internal/process/task_queues.js:85:21)
(node:14526) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 15)

For background, this was building perfectly before. Then I merged my teams master branch into my feature branch to fix merge conflicts and now it won't build.

So here is my package.json:

{
  "name": "...",
  "version": "0.0.0",
  "private": true,
  "description": "...",
  "repository": "",
  "license": "MIT",
  "author": "",
  "directories": {
    "doc": "doc",
    "test": "tests"
  },
  "scripts": {
    "build": "ember build",
    "codegen": "graphql-codegen",
    "lint:hbs": "ember-template-lint .",
    "lint:js": "eslint .",
    "start": "ember serve --ssl --secure-proxy false --proxy https://localhost:5001 --environment local",
    "test": "ember test"
  },
  "devDependencies": {
    "@ember/edition-utils": "^1.1.1",
    "@ember/jquery": "^1.1.0",
    "@ember/optional-features": "^1.1.0",
    "@glimmer/component": "^1.0.0",
    "@types/ember": "^3.1.1",
    "@types/ember-qunit": "^3.4.7",
    "@types/ember__test-helpers": "^0.7.9",
    "@types/qunit": "^2.9.0",
    "@types/rsvp": "^4.0.3",
    "apollo-link-error": "^1.1.12",
    "babel-eslint": "^10.0.2",
    "broccoli-asset-rev": "^3.0.0",
    "ember-animated": "^0.9.0",
    "ember-apollo-client": "2.0.0",
    "ember-auto-import": "^1.5.3",
    "ember-cli": "~3.15.1",
    "ember-cli-app-version": "^3.2.0",
    "ember-cli-babel": "^7.13.0",
    "ember-cli-dependency-checker": "^3.2.0",
    "ember-cli-deploy": "^1.0.2",
    "ember-cli-deploy-build": "^2.0.0",
    "ember-cli-deploy-s3": "^1.4.0",
    "ember-cli-htmlbars": "^4.2.0",
    "ember-cli-inject-live-reload": "^2.0.1",
    "ember-cli-sass": "^10.0.1",
    "ember-cli-sri": "^2.1.1",
    "ember-cli-typescript": "^3.1.1",
    "ember-cli-typescript-blueprints": "^3.0.0",
    "ember-cli-uglify": "^3.0.0",
    "ember-cli-update": "^0.49.6",
    "ember-css-modules": "^1.3.0-beta.1",
    "ember-css-modules-sass": "^1.0.1",
    "ember-drag-drop": "atomicobject/ember-drag-drop#feature/horizontal-sorting-improvements",
    "ember-export-application-global": "^2.0.1",
    "ember-fetch": "^7.0.0",
    "ember-intl": "^4.2.2",
    "ember-load-initializers": "^2.1.1",
    "ember-maybe-import-regenerator": "^0.1.6",
    "ember-paper": "^1.0.0-beta.26",
    "ember-qunit": "^4.6.0",
    "ember-resolver": "^7.0.0",
    "ember-source": "https://s3.amazonaws.com/builds.emberjs.com/beta/shas/49ae818907447d9c469d68b297060f00728ffb5a.tgz",
    "ember-template-lint": "^1.5.0",
    "ember-test-selectors": "^2.1.0",
    "ember-tooltips": "^3.4.2",
    "ember-welcome-page": "^4.0.0",
    "ember-wormhole": "^0.5.5",
    "eslint": "^6.1.0",
    "eslint-plugin-ember": "^7.7.1",
    "eslint-plugin-node": "^10.0.0",
    "graphql": "^14.5.8",
    "liquid-fire": "^0.31.0",
    "loader.js": "^4.7.0",
    "qunit-dom": "^0.9.2",
    "sass": "^1.23.3",
    "typescript": "^3.7.2"
  },
  "engines": {
    "node": "8.* || >= 10.*"
  },
  "ember": {
    "edition": "octane"
  },
  "dependencies": {
    "@ember/render-modifiers": "^1.0.2",
    "@glimmer/tracking": "^0.14.0-alpha.1",
    "@graphql-codegen/cli": "^1.9.1",
    "@graphql-codegen/near-operation-file-preset": "^1.9.1",
    "@graphql-codegen/typescript": "^1.9.1",
    "@graphql-codegen/typescript-compatibility": "^1.9.1",
    "@graphql-codegen/typescript-operations": "^1.9.1",
    "@simple-dom/interface": "^1.4.0",
    "@types/faker": "^4.1.8",
    "@types/lodash-es": "^4.17.3",
    "@types/tinymce": "^4.5.24",
    "apollo-cache-inmemory": "^1.6.3",
    "apollo-link": "^1.2.13",
    "apollo-link-batch-http": "^1.2.13",
    "bufferutil": "^4.0.1",
    "cldr-core": "^36.0.0",
    "ember-click-outside": "^1.3.0",
    "ember-concurrency-decorators": "^1.0.0",
    "ember-file-upload": "^2.7.1",
    "ember-hacky-set-value": "0.0.1",
    "es6-promise": "^4.2.8",
    "faker": "^4.1.0",
    "isomorphic-fetch": "^2.2.1",
    "lodash-es": "^4.17.15",
    "moment": "^2.24.0",
    "tinymce": "^5.2.1"
  }
}

It was builing until the ember-hacky-set-value package was introduced so I wonder if that's the problem. But that seems like a completely unrelated change that shouldn't have effected tinymce. So maybe it's something else?




jeudi 9 avril 2020

How to get Ember Twiddle working: Route links are not displaying on the page

This question is related to: Ember Octane How to Clear Form Errors?

I have been asking a lot of questions on how to upgrade my Ember Classic project to Ember Octane. Some users in the community have suggested that I post an Ember-Twiddle for them to see what is going on. I have tried doing that, but I cannot get it to work. This is another tool for me to learn and I am struggling a bit to make heads or tails of it while trying to also NOT post my entire project as that seems unnecessary.

Why do I not see the page links for change-password and myroute2?

https://ember-twiddle.com/364eaf05a2e1072994b61f255032eb62?openFiles=templates.application%5C.hbs%2C




mardi 7 avril 2020

Ember Associations Are Not Respecting Model Custom Adapter in Test Environment

In my Ember.js app I have a model Company with a custom adapter that points to an API. I also have a User model with a custom adapter that points to a different API and a belongsTo association with Company (via a user#companyId attribute).

When manually exercising the app, all references to a Company in the code seem to send requests to the correct endpoint defined in the Company's custom adapter. However when running tests (both unit and acceptance tests), direct queries for a company this.store.find... hit the correct endpoint (company-host.com/api/companies/1), but references to a Company through its association with a User user.company are pointing to the User adapter's host and namespace (user-host.com/api/companies/1).

I'm new to Ember and any help or insight would be appreciated.




How do I get Chrome Local Overrides to work?

In an Ember project I am trying to debug the error index.js:903 Uncaught TypeError: Cannot set property onCancel of [object Object] which has only a getter. That has led me to assets/@ember/~internals/utils/index.js:903 which I started to debug with breakpoints. I gave up after realizing how many times this line is called. So I found out about Chromes Local Overrides and tried putting that line in a try-catch block with the catch having a debugger call.

I have reloaded the page with the overrides on and it acts like I never made the change. I have also tried simply adding a console.log but that also does nothing. I have also confirmed that this code gets called multiple times before encountering the error

Is there something that I might be doing wrong?

The file is saved and there is a file symbol with a purple circle on it.

enter image description here




Iterate an array with respect to another array's index in handlebars


   
     
   

I want to do something like this. Using 2 helpers at once "each" and "get". How can i achieve something like this?




Calculating image natural width on initial page load with ember-render-modifiers in Ember Glimmer components

Prior to using Ember's new Glimmer components, I used the didInsertElement lifecycle hook to calculate the natural width of an image element. I am passing in a target width so I can resize the image in the browser.

didInsertElement() {
    this._super(...arguments);
    if (this.targetWidth && this.targetWidth < this.element.naturalWidth) {
        this.targetHeight = Math.round(this.targetWidth / this.element.naturalWidth * this.element.naturalHeight);
        set(this, 'width', this.targetWidth);
        set(this, 'height', this.targetHeight);
    }
    else {
        set(this, 'width', this.naturalWidth);
        set(this, 'height', this.naturalHeight);
    }

With Glimmer components, and using @ember/render-modifiers, the src being passed in by the parent:

<img  src= width= height=>

In the component JS file

@action
calculateDimensions(element) {
    if (this.args.targetWidth && this.args.targetWidth < element.naturalWidth) {
        this.height = Math.round(this.args.targetWidth / element.naturalWidth * element.naturalHeight);
        this.width = this.args.targetWidth;
    }
    else {
        this.width = element.naturalWidth;
        this.height = element.naturalHeight;
    }
    element.setAttribute("width", this.width);
    element.setAttribute("height", this.height);
}

This all works fine if you reload the index/home page. However, when you first arrive at the site, the image is not rendered, because the value for element.naturalWidth is zero. The element is present, and if it's logged to the console I can see the values for natural width and height. Is the ember-render-modifier not a full replacement for didInsertElement?

Any help much appreciated.




lundi 6 avril 2020

"Invalid token specified" error when adding query params to url

Using Ember.JS v3.8: When trying to load a URL in a fresh browser with no session data: If I try to access the page like:

https://www.example.com/mystuff/resourceid/usage-summary

This loads just fine. But if I try something like:

https://www.example.com/mystuff/resourceid/usage-summary?param=myparam

I get: {message: "Invalid token specified"} error thrown by jquery. I have other places in my app where query params in the URL works just fine. It is one specific route that would not accept them.

The session data is initialized with an empty token {}. But I get the same results if I init it with null or no token data at all. And again - it works just fine unless I have query params.

Note: I have my own custom authenticator that extends ember-simple-auth. I've put a breakpoint in the init() - it never gets hit.

Anything would help! Thanks!




Difference in property binding for custom component in Ember V/s Angular

In Angular, if we want to bind a property from outside for a custom component, we have to use @Input to kind of allow that property to be set from outside (i.e. from consuming component template)

Does EmberJS also have some sort of similar mechanism OR does it allow binding directly from the template (hbs) without adding/marking anything in the component JS? Is there any difference when it comes to Ember Octane V/s the earlier versions of Ember?




emberjs child component not re-rendering after property update

I have a page component (five-whys) with a number of inputs that the user can choose to finalize the input. When the user clicks finalize, all questions are made to be disabled.

Page component

five-whys.hbs:


  <Generic::RichTextInput
    @value=
    @onChange=
    @disabled= />

<button >Finalize</button>

five-whys.ts

interface AnalyzeFiveWhysArgs {
  dataStory: DataStory;
}

export default class AnalyzeFiveWhys extends Component<AnalyzeFiveWhysArgs> {
  @alias("args.dataStory.fiveWhysAnalysis") fiveWhysAnalysis

  @tracked
  isFinalized: boolean = this.fiveWhysAnalysis.isFinalized ?? false;

  @tracked
  whys: LocalWhy[] = this.fiveWhysAnalysis.whys;

  @tracked
  isFinalized: boolean = this.fiveWhysAnalysis.isFinalized ?? false; 

  @action
  async finalizeWhy() {
    this.isFinalized = true;
  }

This works fine when my rich text component is just a regular text area. However, I am trying to implement tinymce which requires me to do stuff outside of Embers little safe space of magic.

My rich text component:

Template:

<textarea id= disabled=></textarea>

Typescript:

interface GenericRichTextInputArgs {
  value?: string;
  onChange: (value: string) => void;
  name: string;
  disabled?: boolean;
}

export default class GenericRichTextInput extends Component<GenericRichTextInputArgs> {
  constructor(owner: unknown, args: GenericRichTextInputArgs) {
    super(owner, args);

    this.initializeTinymce();
  }

  id = this.args.name;

  get editor() {
    return tinymce.get(this.id);
  }

  get settings() {
    console.log(this.args.disabled);

    const settings: TinyMCESettings = {
      selector: `#${this.id}`,
      setup: (editor: Editor) => this.setupEditor(this, editor),
      readonly: this.args.disabled ? this.args.disabled : false
    };
    return settings;
  }

  initializeTinymce() {
    Ember.run.schedule('afterRender', () => {
      console.log("re-initializing"); // I expect to see this log every time the isFinalized property in the five-whys component changes. But I only see it on page load.

      tinymce.init(this.settings);
    });
  }

  setupEditor(self: GenericRichTextInput, editor: Editor) {
    ... // details of tinymce API
  }
}

When I click the finalize button, The effect of the disabled flag in the rich text component does not change.

Note:

The tinymce library I'm using sets the text area display to none and the aria-hidden to true. This is because it wraps the textarea in a widget. So I have to use the library's api to set disabled.




You attempted to generate a link for the route, but did not pass the models required for generating its dynamic segments

I am in the process of upgrading an Ember app to 3.16 LTS.

I get this console error:

You attempted to generate a link for the "forgot-password" route, but did not pass the models required for generating its dynamic segments.

The link in the template is:

<LinkTo @route="forgot-password">Forgot Password</LinkTo>

In the router it is defined as:

this.route('forgot-password');

As you can see, it does not have any dynamic segments to pass in a model for. How do I fix this?




How to pass value in inline handlebars partial within ember

I have a .html file where following code is included:

<script type="text/x-handlebars" id="social"> 
    I'm partial content: <br>
</script>

Further down I use:


The above is rendering textual "I'm partial content:" ... but not the passed value ('x') from the key 'label'

I do not know why this is not workin? Using Handlebars 2.0 and Ember 1.9 (I know old versions but I'm editing existing tools from someone else).




dimanche 5 avril 2020

Ember Octane How to access Model data in Component

I am upgrading from Ember Classic to Ember Octane 3.17.0.

Question:

How do I access model data in a component?

Context:

I am attempting to allow for reset password functionality. This sends a URL string to the user's e-mail. When they click on the link, I am able to query the string and load the userId and token into the model. However, I cannot access that model data in the component. In Classic, I achieved this with the didReceiveAttrs method, which is now deprecated. The documentation suggests using getters, but I am not clear on how that is done.

See the code below.

Note: I have not placed this in Ember Twiddle because I do not know how; that is another learning curve; and I tried looking for a walk-through but could not find one. If anyone wants to load this into Ember Twiddle then they have the code necessary to do so.

Template Component HBS:

<div class="middle-box text-center loginscreen animated fadeInDown">
    <div>
        <h3>Reset Password</h3>
        <form class="m-t" role="form" >
            
                <div class="error-alert"></div>
            
            <Input @type="hidden" @value= />
            <Input @type="hidden" @value= />
            <div class="form-group">
                <Input @type="password" class="form-control" placeholder="New Password" @value= required="true" />
            </div>
            <div class="form-group">
                <Input @type="password" class="form-control" placeholder="Confirm Password" @value= required="true" />
            </div>
            <div>
                <button type="submit" class="btn btn-primary block full-width m-b">Reset Password</button>
            </div>
        </form>
    </div>
</div>

Template HBS:

<Clients::ResetPasswordForm @resetPasswordModel= @resetPassword= @errors= />

Route:

import Route from '@ember/routing/route';
import AbcUnAuthenticatedRouteMixin from '../../mixins/abc-unauthenticated-route-mixin';

export default class ResetPasswordRoute extends Route.extend(AbcUnAuthenticatedRouteMixin) {

    model(params) {

        return {
            userId: params.strUserId,   // The strUserId found in the query parameters of the reset password URL.
            newPassword: '',
            confirmPassowrd: '',
            token: params.strToken,     // The strToken found in the query parameters of the reset password URL.
        };
    }
}

Component JS:

import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';

export default class ResetPasswordForm extends Component {

    @tracked userId;
    @tracked newPassword;
    @tracked confirmPassword;
    @tracked token;

    @action
    resetPassword(ev) {

        ev.preventDefault();

        this.args.resetPassword({
            userId: this.userId,
            newPassword: this.newPassword,
            confirmPassword: this.confirmPassword,
            token: this.token
        });
    }
}

Controller JS:

import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';

export default class ResetPassword extends Controller {

    @service ajax;

    queryParams = ['strUserId', 'strToken'];
    strUserId = null;
    strToken = null;

    @action
    resetPassword(attrs) {

    if(attrs.newPassword == undefined || attrs.newPassword == null || attrs.newPassword == '' ||
        attrs.confirmPassword == undefined || attrs.confirmPassword == null || attrs.confirmPassword == '' ||
        attrs.newPassword != attrs.confirmPassword) 
        {

            this.set('errors', [{
                detail: "The new password and confirm password must be the same and their values and cannot be blank.  Reset password was not successful.",
                status: 1005,
                title: 'Reset Password Failed'
            }]);

            this.set('model', attrs);
        }
        else {

            this.ajax.request(this.store.adapterFor('application').get('host') + "/clients/reset-password", {
                method: 'POST',
                data: JSON.stringify({ 
                    data: {
                        attributes: {
                            "userid" : attrs.userId,
                            "new-password" : attrs.newPassword,
                            "confirm-password" : attrs.confirmPassword,
                            "token" : attrs.token
                        },
                        type: 'reset-passwords'
                    }
                }),
                headers: {
                    'Content-Type': 'application/vnd.api+json',
                    'Accept': 'application/vnd.api+json'
                }
            })
            .then(() => {

                // Transistion to the reset-password-success route.
                this.transitionToRoute('clients.reset-password-success');
            })
            .catch((ex) => {

                this.set('errors', ex.payload.errors); 
            });
        }
    }
}



ember octane testing: env.locationType not working

I'm noticing this.transitionTo() methods in my code are changing the actual address URL in my Ember 3.16 test server window.

I made sure that my ENV.locationType=none. Can someone spot something really obvious that I'm missing?

config/environment.js:

module.exports = function (environment) {
  let ENV = {
    modulePrefix: "portal-client3",
    environment,
    rootURL: "/",
    locationType: "auto",
    EmberENV: {
      FEATURES: {
        // Here you can enable experimental features on an ember canary build
        // e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true
      },
      EXTEND_PROTOTYPES: {
        // Prevent Ember Data from overriding Date.parse.
        Date: false,
      },
    },

    APP: {},
  };

  if (environment === "test") {
    ENV.locationType = "none";
    ENV.APP.LOG_ACTIVE_GENERATION = false;
    ENV.APP.LOG_VIEW_LOOKUPS = false;
    ENV.APP.rootElement = "#ember-testing";
    ENV.APP.autoboot = false;
  }
  return ENV;
};

app/router:

import EmberRouter from "@ember/routing/router";
import config from "./config/environment";

export default class Router extends EmberRouter {
  location = config.locationType;
  rootURL = config.rootURL;
}

tests/test-helper:

import Application from "../app";
import config from "../config/environment";
import { setApplication } from "@ember/test-helpers";
import { start } from "ember-qunit";

setApplication(Application.create(config.APP));
start();

tests/acceptance/login-test:

import { module, test } from "qunit";
import { visit, currentURL } from "@ember/test-helpers";
import { setupApplicationTest } from "ember-qunit";
import { setupMirage } from "ember-cli-mirage/test-support";

module("Acceptance | login", function (hooks) {
  setupApplicationTest(hooks);
  setupMirage(hooks);

  test("visiting /login", async function (assert) {
    await visit("/clientname");
    assert.equal(currentURL(), "/clientname/login");
  });
});




samedi 4 avril 2020

Ember controller redirection after rendering

I want to test can we pass value in controller after rendering.

https://ember-twiddle.com/fef25dfa8fbe58ca181d9f7d969de39e




vendredi 3 avril 2020

New syntax for click event in Handlebars/EmberJS

How do I switch from:

 <Checkbox
   //do I need it to be @onclick?
   onclick=
 />

to:

 <Checkbox
  
 />

given that I'm not using a class for my controller:

import Controller from '@ember/controller';

export default Controller.extend({
  actions: {
    toggleCheckBox(checkBox) {
      //code
    }
  }
})



jeudi 2 avril 2020

Ember Octane Logout button disappears

I am upgrading to Ember Octane and I modified the template HBS to call the component JS. When I use Ember Classic, the Logout button exists and works. But, when I convert to Octane then the Logout button disappears. o_O

What is the correct way to display a Logout button on the template HBS? Note: I do not have a component HBS file. Is this required in Ember Octane?

Classic Template HBS snippet:

<li><a href="#" onclick=>Logout</a></li>

Octane Template HBS snippet:

<li><a href="#" onclick=>Logout</a></li>

Octane Component JS (works with Classic Template, but not Octane Template):

import Component from '@ember/component';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';

export default class Navigation extends Component {    

    @service session
    @service currentClient

    @action
    logout(ev) {

        ev.preventDefault();

            this.session.invalidate();
    }
}



Ember Octane How to Clear Form Errors?

This question is related to Ember Octane How to Get Error Messages to be Displayed?

Question: What is the correct way to clear form errors and how do I do it?

In Ember Classic, I am able to clear form errors within the component JS file using the following code snippet:

Import { A } from '@ember/array';

...

init() {
    this._super(... arguments);
    this.set('errors', A([]));
},

However, in Ember Octane, I get the following ESLint error:

Don't use this._super in ES classes ember/no-ember-super-in-es-classes

I tried changing the code snippet to:

Import { A } from '@ember/array';

...

init() {
    super(... arguments);
    this.set('errors', A([]));
}

Unfortunately, I get the following error:

super() is only valid inside a class constructor of a subclass. Maybe a typo in the method name ('constructor') or not extending another class?




Error: Assertion Failed: You must provide one or more parameters to the link-to component

I'm following the emberjs tutorial and I am getting an error when adding a <LinkTo> tag. I'm not sure what it means as I can't find any documentation what I need to do to properly construct this tag.

What do I need to do to construct this properly? It doesn't render on the page.

Here is my environment:

ember-cli: 3.6.1

node: 13.12.0

os: darwin x64

Here is the error in the console:

Error: Assertion Failed: You must provide one or more parameters to the link-to component.

Here are the relevant code snippets

app/templates/index.hbs

<div class="jumbo">
  <div class="right tomster"></div>
  <h2>Welcome to Super Rentals!</h2>
  <p>We hope you find exactly what you're looking for in a place to stay.</p>
  <LinkTo @route="about">About</LinkTo>
</div>

app/router.js

import EmberRouter from '@ember/routing/router';
import config from './config/environment';

export default class Router extends EmberRouter {
  location = config.locationType;
  rootURL = config.rootURL;
}

Router.map(function() {
  this.route('about');
});



Can't we use plain Ember Objects in place of Ember Mixins?

If Ember.Mixin.create() is itself extended from Ember.Object then why don't we mix objects created by Ember.Object.create() instead of Ember.Mixin.create()?

Why dont we use Ember objects like this

let TestObject = Ember.Object.create({
  list: []
}); 

let TestComponent = Ember.Component.extend(TestObject, {});

Instead of using a Ember mixin like below

let TestMixin = Ember.Mixin.create({
      list: []
    }); 

let TestComponent = Ember.Component.extend(TestMixin, {});



How to use adapters, serializers, models and other tools in Ember more effectively?

I have an app based on Ember Data. I would like to figure out how to make the code more compact. My application looks like this:: there are several endpoints that from the Express server. In the Ember application, each endpoint corresponds to: adapter, serializer, model, route, template. Everything works fine, but the code is too cumbersome. I'm new to Ember, and maybe there is a way to use adapters and other tools more universally. Here are some parts of my application that illustrate how it works.

localhost:5000/api/cars

localhost:5000/api/vehicles

Adapter "cars":

import RESTAdapter from '@ember-data/adapter/rest';
export default RESTAdapter.extend({
  host: http://localhost:5000/api,
  pathForType() {
    return "cars";
  }
});

Adapter "vehicles":

import RESTAdapter from '@ember-data/adapter/rest';
export default RESTAdapter.extend({
  host: http://localhost:5000/api,
  pathForType() {
    return "vehicles";
  }
});

Serializer "cars":

import RESTSerializer from '@ember-data/serializer/rest';
export default RESTSerializer.extend({
  normalizeResponse(store, primaryModelClass, payload, id, requestType) {
    payload = {
      cars: payload
    };
    return this._super(store, primaryModelClass, payload, id, requestType);
  },
  primaryKey: '_id'
});

Serializer "vehicles":

import RESTSerializer from '@ember-data/serializer/rest';
export default RESTSerializer.extend({
  normalizeResponse(store, primaryModelClass, payload, id, requestType) {
    payload = {
      vehicles: payload
    };
    return this._super(store, primaryModelClass, payload, id, requestType);
  },
  primaryKey: '_id'
});

Car model:

import DS from 'ember-data';
const { attr } = DS;
export default DS.Model.extend({
  name: attr("string"),
  body: attr("array"),
  date: attr('date')
});

Vehicle model (the same as car!):

import DS from 'ember-data';
const { attr } = DS;
export default DS.Model.extend({
  name: attr("string"),
  body: attr("array"),
  date: attr('date')
});

Cars index route:

import Route from '@ember/routing/route';
export default class CarsIndexRoute extends Route {
  model() {
    return this.store.findAll("car");
  }
}

Vehicles index route:

import Route from '@ember/routing/route';
export default class VehiclesIndexRoute extends Route {
  model() {
    return this.store.findAll("vehicles");
  }
}

Hbs templates are completely similar. cars/index.hbs (and vehicles/index.hbs):


<h3></h3>
<p></p>

The code clearly shows that the structure is the same, and the differences are only in one parameter, which corresponds to the model name and the "end" of the api endpoint. Can someone tell me how to organize everything more correctly, in the tradition of Ember? Thanks!




How to assign type to string input as object output

I have a forEach that takes in a string and outputs an object. How would I type this appropriately?

descriptionParts.forEach((text: string) => {
    let parts = {
       text,
       isLink: false
    }
    if (text.match('^#') || text.match('^@')) {
       parts = {
          text: `<span class=${this.boldClass}>${text}</span>`,
          isLink: true
       };
    }
    parsedDescriptionParts.pushObject(parts);
});



mercredi 1 avril 2020

Having an optional prefix in ember routes

Problem

The app has to have root prefix that is optional. The prefix also has a dynamic segment.

Example

The content of

/posts/1 and /account/123/posts/1 has to load the same route and template.

where /account/123 is the prefix. 123 is a dynamic segment of the prefix.

Requirement

  1. All the urls has to be accessible with and without the prefix.
  2. When prefix route is loaded , all single page transitions has to be in context of prefix-urls.

    /account/123/post/1 to /account/123/post/2

  3. When non-prefix route is loaded , all single page transitions has to be in context of non-prefix-urls.

    /post/1 to /post/2

Constraints

  1. The existing codebase has routes without the prefix urls.
  2. As controllers,routes,tempaltes already exist in routes without prefix , the solution has to reuse the existing code.

Expected Solution

  1. All functions making spa transitions has to see the window.location context and make transition to prefix or non-prefix path. This will include functions as transitionTo,replaceWith,linkTo helper,etc

    Since the existing link-to helper,etc, will have route name as post.detail (non-prefix routes)

  2. Has to have minimal code changes in the existing controller , routes, templates.

Example of Existing Routes

NewRouter.map(function() {
  this.route('posts', function() {
    this.route('detail',{path: "/:id"});

    this.route('comments', function() {
        this.route('detail',{path: "/:id"});
    });
  });

});

The controllers, routes, templates already exist for post, post.detail, comments, comment.detail.




UI Automation - Elements on my UI have ember ids , which change frequently with addition of new UI elements. How to use the id for automation?

Example of the HTML of a dropdown element:

    <div aria-owns="ember-basic-dropdown-content-ember1234" tabindex="0" data-ebd-id="ember1234-trigger" role="button" id="ember1235" class="ember-power-select-trigger ember-basic-dropdown-trigger ember-view">  <!---->
    <span class="ember-power-select-status-icon"></span>
    </div>

The xpath and CSS selector also contain the same ember id. xpath : //*[@id="ember1235"] css selector : #ember1235

The ember id would change from id="ember1235" to say, id="ember1265" when there is a change in the UI.

I am using id to locate the element. But every time it changes I need to modify the code. Is there any other attribute I could use for Ember JS UI elements?