ArcGIS, move map to point using ArcGIS API for JavaScript - arcgis-js-api

I'm new on ArcGis (and also Angular, I start today developing in both), can't find an example on how to move the map to certain point, I was trying:
this.mapa.map.centerAt(new Point(-118.15, 33.80));
But I have a javascript error TocComponent.html:10 ERROR ReferenceError: Point is not defined
When I do console.log(this.mapa.map); I got this (I put this in case someone is wondering if this.mapa.map was incorrect):
EDIT: My solution, same as the answer. It not all, it's a demo of my app with Angular:
import { MapaComponent } from '../mapa/mapa.component';
// some code
export class MyComponent implements OnInit {
constructor(private arcgisService: ArcgisApiService, private mapa: MapaComponent) { }
// another code
onChangeSomething(evt: any): void {
// more code
loadModules([
'esri/geometry/Point'
]).then(([Point]) => {
const my_center = new Point([-99.94867549215655, 20.55088183550196]);
this.mapa.map.centerAndZoom(my_center, 5);
});

You may not be including the Point module in your AMD includes at the top of your file. Your list should include esri/geometry/Point like this:
require([
"esri/map",
"esri/layers/FeatureLayer",
"esri/geometry/Point",
], function(Map, FeatureLayer, Point) {
[... the rest of your code ...]
});

Related

Problem with ejs-upload with $event on a couple of methods v. 18.2.44 and Angular13

I'm using my own example of Syncfusion upload. Here's the Stackblitz which WORKS and I designed 3 years ago.
So, what's the problem? Well, in my latest project, using Angular13 and the same version of Syncfusion, 18.2.44, the ejs-uploader is not rendering.
Here's the graphics:
And the CODE in VSCode
ERROR in the debug console.
public dropElement!: HTMLElement;
ngAfterViewInit(): void {
let self = this;
this.dropElement = document.getElementsByClassName(
'control_wrapper'
)[0] as HTMLElement;
(document.getElementsByClassName('e-btn')[0] as any).style.display = 'none';
setTimeout(function () {
(document.getElementById('full') as any).onclick = (args: any) => {
console.log('Args afterViewInit: ', args);
self.uploadObj.upload(self.uploadObj.getFilesData());
};
}, 5000);
}
WHAT I DID:
This line above: self.uploadObj.upload(self.uploadObj.getFilesData()); FINDS the function nicely in VSCode but when I run the app at compile time, it's UNDEFINED.
Syncfusion support suggested I use the wrapper of (document.getElementById('full') as any) and not just: document.getElementById('full') wherever I call or reference an element. It works in Stackblitz but SHOULD in my code.
BUT it doesn't in my project.
All I see in the debug console is . If you check INSPECT in the debug console in Stackblitz, however, you'll see it fully rendered.

fire event from external controller

i'm moving to Rails 7 and i feel like there are so many changes but i'm confident on understanding them and be able to upgrade a personal applications i made myself for keeping my personal records and appointments
More specific i need to communicate between controllers (#hotwire/stimulus) between a flatpickr controller and fullcalendar contorller. The idea is to jump to a date when selecting from flatpicr
I've tried so many diferent options but i'm really stuck.. any help is welcome :)
Rails 7.0.3.1
index.html.erb
<div data-controller="flatpickr" name="" data-action=""></div>
<div data-controller="calendar">
<div data-calendar-target="window"></div>
<turbo-frame id="popup" data-calendar-target="popup"></turbo-frame>
</div>
flatpickr_controller.js
import Flatpickr from 'stimulus-flatpickr'
export default class extends Flatpickr {
connect() {
this.config = {
inline: true,
enableTime: false,
time_24hr: false,
onChange: function(selectedDates, dateStr, instance) {
const calendarController = this.application.getControllerForElementAndIdentifier(this.calendarTarget, "calendar")
calendarController.gotoDate('18-01-2025') //random date
},
};
super.connect();
}
}
calendar_controller.js
import { Controller } from "#hotwired/stimulus";
import { Calendar } from '#fullcalendar/core';
import resourceTimeGridPlugin from '#fullcalendar/resource-timegrid';
import interactionPlugin from '#fullcalendar/interaction';
export default class extends Controller {
static targets = [ "popup", "window" ];
connect() {
let overlay = this.popupTarget;
this.calendar = new Calendar(this.windowTarget, {
plugins: [ resourceTimeGridPlugin, interactionPlugin ],
themeSystem: 'bootstrap5',
initialView: 'resourceTimeGridDay',
aspectRatio: 1.8,
nowIndicator: true,
selectable: true,
editable: true,
allDaySlot: false,
});
window.addEventListener('load', () => {
this.calendar.render();
});
}
refresh(e) {
if (e.detail.success) {
this.calendar.refetchEvents();
}
}
}
output
application-7082a89999639e6d01ae0ef0aaaf6707b39fab96541f1dcd1c79da24753cb0ed.js:28271 Uncaught TypeError: Cannot read properties of undefined (reading 'getControllerForElementAndIdentifier')
at Object.onChange (ap ...
I think I'm gonna get mad with this... thank you!
Well done on trying to understand all of this, it can be hard to learn something new and especially when you have 'working' code and you are kind of forced to change.
One thing that can help is to revisit the Stimulus documentation, it does have pretty much all the answers you need for these issues but maybe needs a bit of a re-read.
The other thing which can be super frustrating is JavaScript's usage of this and how it works.
Hopefully the below breakdown helps.
Problems
1. Understanding this (JavaScript)
The first problem with the code above is that you are referencing this with the assumption that it refers to your controller instance, but rather it is referring to the event's context.
onChange: function(selectedDates, dateStr, instance) {
const calendarController = this.application.getControllerForElementAndIdentifier(this.calendarTarget, "calendar")
calendarController.gotoDate('18-01-2025') //random date
},
In the above code, this.application and this.calendarTarget will never work as the this here is the context created by the onChange handler calling context.
The quick way around this this issue is to just use an arrow function. In the below revised code snippet (which will still not work, due to issues 2 & 3 below), the arrow function approach is used instead of a function declaration, which pulls in the this from the parent context, which will be the Controller's instance.
onChange: (selectedDates, dateStr, instance) => {
const calendarController = this.application.getControllerForElementAndIdentifier(this.calendarTarget, "calendar")
calendarController.gotoDate('18-01-2025') //random date
},
The best way, however, is to read the documentation on Mozilla here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this in full, read it again and then maybe a third time. After that, find some YouTube videos and watch those. You will find JavaScript development much easier if you truly 'grok' this concept, but it is hard to understand.
2. Understanding Stimulus Targets
The next issue is your use of this.calendarTarget in your flatpackr controller, this controller will not have any target available due to it not being set up correctly.
In the Stimulus docs - https://stimulus.hotwired.dev/reference/targets you can read that the target must be in the controller's scope. But in the HTML below the data-controller="flatpickr" div has no children and also has no targets in the HTML anywhere that can be accessed by this controller.
<div data-controller="flatpickr" name="" data-action="">No Children here?</div>
<div data-controller="calendar">
<div data-calendar-target="window"></div>
<turbo-frame id="popup" data-calendar-target="popup"></turbo-frame>
</div>
There are a few ways to access something outside the controller's scope, but the easiest way would be to bypass this problem all together and use the Stimulus' preferred way to communicate with other controllers.
But, if you want to use a target you need to do two things.
A. Ensure the target static attribute is declared on your controller.
export default class extends Flatpickr {
static targets = [ "calendar" ]; // this is required
B. Ensure the target element has the right attribute and is a child of the desired controller.
<div data-controller="flatpickr" name="" data-action="">
<div data-controller="calendar" data-flatpickr-target="calendar">
<div data-calendar-target="window"></div>
<turbo-frame id="popup" data-calendar-target="popup"></turbo-frame>
</div>
</div>
3. Stimulus Cross-Controller Coordination With Events
Finally, your use of getControllerForElementAndIdentifier is documented as a work around if there is no other way to communicate with another controller.
The preferred way is using events and it is incredibly powerful, flexible and will probably solve 99.9% of your use cases. Have a read of https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent & https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent if you are not sure what browser events are first.
Then, you can dispatch an event from your flatpackr controller for your calendar controller to pick up on.
The simplest way to go about this is an event that jut gets dispatched on your first controller and 'bubbles' up the DOM and then your calendar controller listens to this globally.
Solution - Example Code
First, start with your HTML, the only real change below is the data-action attribute on your calendar.
This will listen for a global event flatpackr:changed and when it sees that, it will call your calendar controller's method goToDate.
<div data-controller="flatpickr"></div>
<div data-controller="calendar" data-action="flatpackr:changed#window->calendar#goToDate">
<div data-calendar-target="window"></div>
<turbo-frame id="popup" data-calendar-target="popup"></turbo-frame>
</div>
In your flatpackr controller, using the arrow function approach described above, you can call this.dispatch() which will trigger the dispatching of a CustomEvent with the supplied options.
Stimulus will prefix the name supplied with the controller's name.
Note: You could be more specific with where this event gets dispatched to, but if there is only ever going to be one calendar instance, no need to worry for now.
import Flatpickr from 'stimulus-flatpickr'
export default class extends Flatpickr {
connect() {
this.config = {
inline: true,
enableTime: false,
time_24hr: false,
onChange: (selectedDates, dateStr, instance) => {
// note: Stimulus sets `bubbles` to true by default but good to be explicit
const someDate = '18-01-2025'; // random date
// passing data to the other controller can be via the `detail` object in the CustomEvent & Stimulus will automatically add 'flatpackr:' to the start of the event name for you (Thanks Stimulus!)
this.dispatch('changed', { detail: { date: someDate } , bubbles: true } );
},
};
// super.connect(); - not sure that you need this in most cases so commented out
}
}
In the calendar controller, all that is needed is the method to be declared goToDate.
You can read the supplied detail in the event.detail from the param.
import { Controller } from "#hotwired/stimulus";
import { Calendar } from '#fullcalendar/core';
import resourceTimeGridPlugin from '#fullcalendar/resource-timegrid';
import interactionPlugin from '#fullcalendar/interaction';
export default class extends Controller {
static targets = [ "popup", "window" ];
connect() {
// ...
// note: you may not need the window on load listener as `connect` will only be called when there is a DOM ready to attach to.
}
refresh(e) {
// ...
}
goToDate(event) {
// note: you can use destructuring above and change the signature to ` goToDate({ detail: { date } }) {` instead
const date = event.detail.date;
console.log('do something with the date now', date);
}
}
Note: I have not tested locally but should be close enough

ckeditor5 - custom container element - recursion error on paste

I'm trying to create a CKEditor5 custom element plugin - mainly for custom format/styles -- nested divs etc. Managed to be able to inject/format the elements, and I can type in them. But if I try to copy and paste text into a custom element I get a too much recursion error.
MyWidget plugin:
export default class MyWidgetPlugin extends Plugin {
init() {
const editor = this.editor;
editor.model.schema.register('my-widget', {
inheritAllFrom: '$root',
isLimit: true,
});
editor.conversion.elementToElement({ model: 'my-widget', view: 'my-widget' });
editor.commands.add('myWidget', new MyWidgetCommand(editor));
}
}
MyWidget command:
class MyWidgetCommand extends Command {
execute() {
const editor = this.editor;
const block = first(this.editor.model.document.selection.getSelectedBlocks());
this.editor.model.change(writer => {
const myWidget = writer.createElement('my-widget')
writer.insert ( myWidget, block, 'after');
writer.appendElement( 'paragraph', myWidget );
});
}
}
Inserting a widget injects this into the editor:
<my-widget>
<p></p>
</my-widget>
And I can type fine, but I can't paste. I'm guessing I got the schema wrong... have played around with quite a few different options.. but to no avail.
I didn't check it but I think that the issue is here:
editor.model.schema.register('my-widget', {
inheritAllFrom: '$root',
isLimit: true,
});
This schema rule says that <my-widget> will allow e.g. a <paragraph> inside it. But it doesn't say anything about where <my-widget> may be used. That's because $root is not allowed in any other element (cause it's a root :)).
I think that the following should work fine:
editor.model.schema.register('my-widget', {
inheritAllFrom: '$root',
allowIn: '$root',
isLimit: true,
});
Alternatively, a more generic solution should work too:
editor.model.schema.register('my-widget', {
inheritAllFrom: '$root',
allowWhere: '$block',
isLimit: true,
});
Still, the editor should not crash with an infinite loop, so I reported https://github.com/ckeditor/ckeditor5-engine/issues/1441.

Dojo provide - update legacy to AMD

This is a followup to this question.
So I have this pre AMD dojo code :
dojo.require(...);
dojo.provide("abc.def.foo");
som.var.iable = {A:1,B:2};
som.var.iable2 = {C: 3, D:som.var.iable.B}
dojo.declare("blah",[],{
//code based on the above variables
});
For AMD, after reading this and the previous link, I am trying something like this
som.var.iable = {A:1,B:2};
som.var.iable2 = {C: 3, D:som.var.iable.B}
define([
"dojo/_base/declare",
], function(declare){
return declare("abc.def.foo", null {
});
});
define([
"dojo/_base/declare",
], function(declare){
som.var.iable = {A:1,B:2};
som.var.iable2 = {C: 3, D:som.var.iable.B}
return declare("blah", null {
//code based on the above variables
});
});
Obviously this fails, as there is no object structure like som.var.iable. I can it, but my question is how did it work in the legacy code? and what would be the correct AMD equivalent?
Any help is greatly appreciated!
OK, here are my assumptions about what you're trying to do:
You don't really need a global variable called some with a property var, that's just a way to organize stuff
You want three modules, some/var/iable, some/var/iable2, and blah. This means three files and three define() calls
Neither som.var.iable nor som.var.iable2 are real inheritable classes, they're just plain old objects... so only blah needs to use declare()
Thus you should create a file som/var/iable.js, which is a provides a plain object:
define([
],
function(){
return {A:1,B,2}
});
And another called som/var/iable2.js, which is a module that provides a plain object:
define([
"som/var/iable",
],
function(iable){
return {C: 3, D:iable.B}
});
And then your third module blah.js that provides a Class-object:
define([
"dojo/_base/declare",
"som/var/iable2"
],
function(declare,iable2){
var parentClasses = [];
var clazz = declare(parentClasses, {
constructor : function(){
// No need for this.inherited(arguments) in this particular case
alert("Welcome to the constructor. Did you know that iable2.D is "+iable2.D+"?");
},
});
return clazz;
});
I haven't tested all this, but to kick it off in a page you'd finally want to put:
require(["blah",dojo/domReady!"], function(blah){
var b = new blah();
});
Dojo should take care of loading everything in-order so that you get an alert that says
Welcome to the constructor. Did you know that iable2.D is 2?

fairytale about mvc, require.js and angular. is there happily ever after?

So. Once upon a time there were four magical creatures: asp.net mvc, require.js and angular. And one wise wizard decided to put them in the same house, and let for every single view of asp.net to have its own "code-behind" javascript file;
first he added to the _Layout.cshtml
<script data-main="/main" src="~/Scripts/require.js"></script>
and then he created main.js in the root:
require.config({
baseUrl: "/Scripts/",
paths: {
'jquery': 'jquery-1.9.1.min',
'jquery-ui': 'jquery-ui-1.10.2.custom.min',
'angular': 'angular.min',
'ng-grid': 'ng-grid-2.0.2.debug'
},
shim: {
'jquery': { exports: "$" },
'underscore': { exports: "_" },
'jquery-ui': ['jquery'],
},
});
// Standard Libs
require(['jquery','jquery-ui','underscore','angular']);
nothing fancy and magical yet. But then he created an html helper as such:
public static MvcHtmlString RequireJs(this HtmlHelper helper)
{
var controllerName = helper.ViewContext.RouteData.Values["Controller"].ToString(); // get the controllername
var viewName = Regex.Match((helper.ViewContext.View as RazorView).ViewPath, #"(?<=" + controllerName + #"\/)(.*)(?=\.cshtml)").Value; //get the ViewName - extract it from ViewPath by running regex - everything between controllerName +slash+.cshtml should be it;
// chek if file exists
var filename = helper.ViewContext.RequestContext.HttpContext.Request.MapPath("/Scripts/views/" + controllerName.ToLower() + "-" +
viewName.ToLower()+".js");
if (File.Exists(filename))
{
return helper.RequireJs(#"views/" + controllerName.ToLower() + "-" + viewName.ToLower());
}
return new MvcHtmlString("");
}
public static MvcHtmlString RequireJs(this HtmlHelper helper, string module)
{
var require = new StringBuilder();
require.AppendLine(" <script type=\"text/javascript\">");
require.AppendLine(" require(['Scripts/ngcommon'], function() {");
require.AppendLine(" require( [ \"" + module + "\"] );");
require.AppendLine(" });");
require.AppendLine(" </script>");
return new MvcHtmlString(require.ToString());
}
and then he could use it in _Layout.cshtml just like that:
#Html.RequireJs()
and if you were listening carefully to the story, you probably noticed that there was also Scripts/ngcommon.js file to manually bootstrap angular.js and have commonly used angular directives and services
require(['angular', 'jquery'], function() {
angular.module("common",[]).directive('blabla', function() {
return {
restrict: 'A',
scope: { value: "#blabla" },
link: function(scope, element, attrs) { }
}
});
//manually bootstrap it to html body
$(function(){
angular.bootstrap(document.getElementsByTagName('body'), ["common"]);
});
});
And here comes the magic: from now on if it was a javascript file in \Scripts\views named as controllerName-viewName.js as home-index.js for Home\Index.cshtml it would be automagically picked up by require.js and loaded. Beautiful isn't it?
But then the magician thought: What If I need to load something else (like ng-grid) and that something should not be injected into common angular module because not all the pages will be using it. Of course he could always manually bootstrap another module into a page element in each code-behind javascript where he needed, but he's not wise enough to find answer to the question:
Is it possible to inject some angular.js component (like ng-grid) directly into a controller, without having it as a part of the app module?
If I understand magician's idea right, then it is possible to go on by splitting your application into sub-modules being defined as a collection of components.
It will work if he sets up dependencies for main myApp module like:
var myApp = angular.module('myApp', ['Constants', 'Filters', 'Services', 'Directives', 'Controllers']);
myApp.Constants = angular.module('Constants', []);
myApp.Controllers = angular.module('Controllers', []);
myApp.Filters = angular.module('Filters', []);
myApp.Services = angular.module('Services', []);
myApp.Directives = angular.module('Directives', []);
Then each of sub-modules: Services etc. - can be extended with single component, like:
myApp.Controllers.controller('MyController', function () {});
myApp.Services.factory('myService', function () {});
myApp.Directives.directive('myDirective', function () {});
myApp.Filters.filter('myFilter', []);
myApp.Constants.constant('myConstant', []);
That way main application module is loaded with several sub-modules, but each structure is not important. It makes possible to include individual controllers, services, directives and filters on each page served from back-end - magician just needs to be sure that all needed dependencies are loaded.
DI is the magic key for having separate angular codebehind in MVC views.
You don't even need the requirejs at all, because angular is a dependency injector and module loader by nature, angular.bootstrap is the magic place to start.
So let wizard became more powerfull with the spell - $inject.
var TmplController = function($scope, $compile, $http... // any module itself
{
this.parts = ['legs','arms','head'];
$scope.dynamicPageTemplate = function($compile)
{
$compile('<div><p ng-repeat="each in parts">{{each}}</p></div>' )( $scope );
}
}
TmplController.$inject = ['$scope','$comple', '$http']; //try legs or head
refer complete annotated source of angular-scenario.js from https://github.com/angular/bower-angular-scenario, and you will find how to inject code with define manner helpers.