@angular-wave/angular.ts 0.6.0 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@angular-wave/angular.ts",
3
3
  "description": "A modern, optimized and typesafe version of AngularJS",
4
4
  "license": "MIT",
5
- "version": "0.6.0",
5
+ "version": "0.6.2",
6
6
  "type": "module",
7
7
  "main": "dist/angular-ts.esm.js",
8
8
  "browser": "dist/angular-ts.umd.js",
@@ -1,8 +1,8 @@
1
- export const ngControllerDirective = [
2
- () => ({
1
+ export function ngControllerDirective() {
2
+ return {
3
3
  restrict: "A",
4
4
  scope: true,
5
5
  controller: "@",
6
6
  priority: 500,
7
- }),
8
- ];
7
+ };
8
+ }
@@ -1,4 +1,4 @@
1
- import { isUndefined } from "../../shared/utils.js";
1
+ import { kebabToCamel } from "../../shared/jqlite/jqlite.js";
2
2
 
3
3
  /**
4
4
  * @param {string} source - the name of the attribute to be observed
@@ -10,11 +10,12 @@ export function ngObserveDirective(source, prop) {
10
10
  restrict: "A",
11
11
  compile: () => (scope, element) => {
12
12
  const targetElement = element[0];
13
- if (isUndefined(prop) || prop == "") {
13
+ if (prop === "") {
14
14
  prop = source;
15
15
  }
16
- if (!scope[prop]) {
17
- scope[prop] = targetElement.getAttribute(source);
16
+ const normalized = kebabToCamel(prop);
17
+ if (!scope[normalized]) {
18
+ scope[normalized] = targetElement.getAttribute(source);
18
19
  }
19
20
 
20
21
  const observer = new MutationObserver((mutations) => {
@@ -22,8 +23,8 @@ export function ngObserveDirective(source, prop) {
22
23
  const newValue = /** @type {HTMLElement} */ (
23
24
  mutation.target
24
25
  ).getAttribute(source);
25
- if (scope[prop] !== newValue) {
26
- scope[prop] = newValue;
26
+ if (scope[normalized] !== newValue) {
27
+ scope[normalized] = newValue;
27
28
  scope.$digest();
28
29
  }
29
30
  });
@@ -89,7 +89,7 @@ describe("observe", () => {
89
89
  });
90
90
 
91
91
  it("should observe attribute changes and update the same scope name if data-update attribute is absent", () => {
92
- $scope.myProp = "";
92
+ $scope.testAttribute = "";
93
93
  const template = `<div ng-observe-test-attribute></div>`;
94
94
  element = $compile(template)($scope);
95
95
  $scope.$digest();
@@ -107,5 +107,6 @@ describe("observe", () => {
107
107
 
108
108
  mutationObserverCallback([mutationRecord]);
109
109
  expect($scope.$digest).toHaveBeenCalled();
110
+ expect($scope.testAttribute).toBe("newValue");
110
111
  });
111
112
  });
@@ -280,12 +280,6 @@ export const ngRepeatDirective = [
280
280
  // However, we need to keep the reference to the jqlite wrapper as it might be changed later
281
281
  // by a directive with templateUrl when its template arrives.
282
282
  block.clone = clone;
283
- $animate.enter(clone, null, previousNode);
284
- previousNode = clone;
285
- // Note: We only need the first/last node of the cloned nodes.
286
- // However, we need to keep the reference to the jqlite wrapper as it might be changed later
287
- // by a directive with templateUrl when its template arrives.
288
- block.clone = clone;
289
283
  nextBlockMap[block.id] = block;
290
284
  updateScope(
291
285
  block.scope,
@@ -0,0 +1,18 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title>AngularTS Test Runner</title>
6
+
7
+ <link rel="shortcut icon" type="image/png" href="/images/favicon.ico" />
8
+ <link rel="stylesheet" href="/jasmine/jasmine-5.1.2/jasmine.css" />
9
+ <script src="/jasmine/jasmine-5.1.2/jasmine.js"></script>
10
+ <script src="/jasmine/jasmine-5.1.2/jasmine-html.js"></script>
11
+ <script src="/jasmine/jasmine-5.1.2/boot0.js"></script>
12
+ <script src="/jasmine/jasmine-5.1.2/boot1.js"></script>
13
+ <script type="module" src="/src/directive/setter/setter.spec.js"></script>
14
+ </head>
15
+ <body>
16
+ <div id="dummy"></div>
17
+ </body>
18
+ </html>
@@ -0,0 +1,60 @@
1
+ ngSetterDirective.$inject = ["$parse"];
2
+ /**
3
+ * @returns {import('../../types.js').Directive}
4
+ */
5
+ export function ngSetterDirective($parse) {
6
+ return {
7
+ restrict: "A",
8
+ link(scope, element, attrs) {
9
+ const modelExpression = attrs.ngSetter;
10
+
11
+ if (!modelExpression) {
12
+ console.warn("ngSetter: Model expression is not provided.");
13
+ return;
14
+ }
15
+
16
+ const assignModel = $parse(modelExpression).assign;
17
+
18
+ if (!assignModel) {
19
+ console.warn("ngSetter: Invalid model expression.");
20
+ return;
21
+ }
22
+
23
+ const updateModel = (value) => {
24
+ assignModel(scope, value);
25
+ scope.$digest();
26
+ };
27
+
28
+ const observer = new MutationObserver((mutationsList) => {
29
+ let contentChanged = false;
30
+ for (const mutation of mutationsList) {
31
+ if (
32
+ mutation.type === "childList" ||
33
+ mutation.type === "characterData"
34
+ ) {
35
+ contentChanged = true;
36
+ break;
37
+ }
38
+ }
39
+
40
+ if (contentChanged) {
41
+ updateModel(element[0].innerHTML);
42
+ }
43
+ });
44
+
45
+ if (element && element[0]) {
46
+ observer.observe(element[0], {
47
+ childList: true,
48
+ subtree: true,
49
+ characterData: true,
50
+ });
51
+ } else {
52
+ console.warn("ngSetter: Element is not a valid DOM node.");
53
+ return;
54
+ }
55
+
56
+ scope.$on("$destroy", () => observer.disconnect());
57
+ updateModel(element.html());
58
+ },
59
+ };
60
+ }
@@ -0,0 +1,76 @@
1
+ import { Angular } from "../../loader.js";
2
+ import { wait } from "../../shared/test-utils.js";
3
+
4
+ describe("setter", () => {
5
+ let $compile, $rootScope, $parse, observerSpy;
6
+
7
+ beforeEach(() => {
8
+ window.angular = new Angular();
9
+ angular.module("myModule", ["ng"]);
10
+ angular
11
+ .bootstrap(document.getElementById("dummy"), ["myModule"])
12
+ .invoke((_$compile_, _$rootScope_, _$parse_) => {
13
+ $compile = _$compile_;
14
+ $rootScope = _$rootScope_;
15
+ $parse = _$parse_;
16
+ });
17
+ observerSpy = jasmine.createSpyObj("MutationObserver", [
18
+ "observe",
19
+ "disconnect",
20
+ ]);
21
+ });
22
+
23
+ it("should update the scope model when the element content changes", async () => {
24
+ $rootScope.testModel = "";
25
+ const element = $compile('<div ng-setter="testModel"></div>')($rootScope);
26
+ $rootScope.$digest();
27
+
28
+ element.html("New content");
29
+ await wait();
30
+ $rootScope.$digest();
31
+
32
+ expect($rootScope.testModel).toBe("New content");
33
+ });
34
+
35
+ it("should handle initial content in the element", () => {
36
+ $rootScope.testModel = "";
37
+ const element = $compile(
38
+ '<div ng-setter="testModel">Initial content</div>',
39
+ )($rootScope);
40
+ $rootScope.$digest();
41
+
42
+ expect($rootScope.testModel).toBe("Initial content");
43
+ });
44
+
45
+ it("should warn if no model expression is provided", () => {
46
+ spyOn(console, "warn");
47
+
48
+ $compile("<div ng-setter></div>")($rootScope);
49
+ $rootScope.$digest();
50
+
51
+ expect(console.warn).toHaveBeenCalledWith(
52
+ "ngSetter: Model expression is not provided.",
53
+ );
54
+ });
55
+
56
+ it("should clean up the MutationObserver on scope destruction", async () => {
57
+ spyOn(window, "MutationObserver").and.returnValue(observerSpy);
58
+ const element = $compile('<div ng-setter="testModel"></div>')($rootScope);
59
+ const isolateScope = element.isolateScope();
60
+
61
+ $rootScope.$destroy();
62
+ await wait();
63
+ expect(observerSpy.disconnect).toHaveBeenCalled();
64
+ });
65
+
66
+ it("should gracefully handle invalid DOM elements", () => {
67
+ spyOn(console, "warn");
68
+
69
+ const element = $compile("<div></div>")($rootScope);
70
+ $rootScope.$digest();
71
+
72
+ expect(console.warn).not.toHaveBeenCalledWith(
73
+ "ngSetter: Element is not a valid DOM node.",
74
+ );
75
+ });
76
+ });
@@ -0,0 +1,11 @@
1
+ import { test, expect } from "@playwright/test";
2
+
3
+ const TEST_URL = "src/directive/setter/setter.html";
4
+
5
+ test("unit tests contain no errors", async ({ page }) => {
6
+ await page.goto(TEST_URL);
7
+ await page.content();
8
+ await expect(page.locator(".jasmine-overall-result")).toHaveText(
9
+ /0 failures/,
10
+ );
11
+ });
package/src/public.js CHANGED
@@ -131,6 +131,7 @@ import {
131
131
  ngView,
132
132
  } from "./router/directives/view-directive.js";
133
133
  import { ngChannelDirective } from "./directive/channel/channel.js";
134
+ import { ngSetterDirective } from "./directive/setter/setter.js";
134
135
 
135
136
  /**
136
137
  * @type {string} `version` from `package.json`, injected by Rollup plugin
@@ -185,6 +186,7 @@ export function publishExternalAPI(angular) {
185
186
  ngNonBindable: ngNonBindableDirective,
186
187
  ngRef: ngRefDirective,
187
188
  ngRepeat: ngRepeatDirective,
189
+ ngSetter: ngSetterDirective,
188
190
  ngShow: ngShowDirective,
189
191
  ngStyle: ngStyleDirective,
190
192
  ngSwitch: ngSwitchDirective,
@@ -539,7 +539,13 @@ export function HttpProvider() {
539
539
  );
540
540
 
541
541
  Object.keys(defHeaders).forEach((defHeaderName) => {
542
- if (!reqHeaders[lowercase(defHeaderName)]) {
542
+ const lowercaseDefHeaderName = lowercase(defHeaderName);
543
+ const hasMatchingHeader = Object.keys(reqHeaders).some(
544
+ (reqHeaderName) => {
545
+ return lowercase(reqHeaderName) === lowercaseDefHeaderName;
546
+ },
547
+ );
548
+ if (!hasMatchingHeader) {
543
549
  reqHeaders[defHeaderName] = defHeaders[defHeaderName];
544
550
  }
545
551
  });
@@ -84,7 +84,7 @@ describe("$http", function () {
84
84
  expect(response.config.method).toBe("GET");
85
85
  });
86
86
 
87
- it("sets deffault headers on request", async function () {
87
+ it("sets default headers on request", async function () {
88
88
  await $http({
89
89
  url: "/mock/hello",
90
90
  headers: {
@@ -96,9 +96,7 @@ describe("$http", function () {
96
96
  });
97
97
  expect(response).toBeDefined();
98
98
 
99
- expect(response.config.headers.Accept).toBe(
100
- "application/json, text/plain, */*",
101
- );
99
+ expect(response.config.headers.Accept).toBe("text/plain");
102
100
  expect(response.config.headers["Cache-Control"]).toBe("no-cache");
103
101
  });
104
102
 
@@ -1,6 +1,6 @@
1
- export const ngControllerDirective: (() => {
1
+ export function ngControllerDirective(): {
2
2
  restrict: string;
3
3
  scope: boolean;
4
4
  controller: string;
5
5
  priority: number;
6
- })[];
6
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @returns {import('../../types.js').Directive}
3
+ */
4
+ export function ngSetterDirective($parse: any): import("../../types.js").Directive;
5
+ export namespace ngSetterDirective {
6
+ let $inject: string[];
7
+ }