stephanrauh / AngularFaces

AngularFaces is a JSF library making it easy to integrate AngularJS code.
https://www.angularfaces.net
Apache License 2.0
90 stars 39 forks source link

Error when loading the Hello world (calculator) page #37

Closed DimaSol closed 9 years ago

DimaSol commented 9 years ago

Hi, I'm trying to use the AngularFaces and seems I'm doing somthing wrong...? I'm getting the below javascript error (in the console) when loading the page.

index.xhtml:1 GET http://me:8181/test/RES_NOT_FOUND 404 (Not Found) angular.js.xhtml?ln=AngularJS:11594 Error: [ng:areq] Argument 'calculatorController' is not a function, got undefined http://errors.angularjs.org/1.3.9/ng/areq?p0=calculatorController&p1=not%20a%20function%2C%20got%20undefined at REGEX_STRING_REGEXP (angular.js.xhtml?ln=AngularJS:63) at assertArg (angular.js.xhtml?ln=AngularJS:1577) at assertArgFn (angular.js.xhtml?ln=AngularJS:1587) at angular.js.xhtml?ln=AngularJS:8418 at angular.js.xhtml?ln=AngularJS:7592 at forEach (angular.js.xhtml?ln=AngularJS:331) at nodeLinkFn (angular.js.xhtml?ln=AngularJS:7579) at compositeLinkFn (angular.js.xhtml?ln=AngularJS:7075) at publicLinkFn (angular.js.xhtml?ln=AngularJS:6954) at angular.js.xhtml?ln=AngularJS:1451 angular.js.xhtml?ln=AngularJS:11594(anonymous function) angular.js.xhtml?ln=AngularJS:8544$get angular.js.xhtml?ln=AngularJS:14485$get.Scope.$apply angular.js.xhtml?ln=AngularJS:1449bootstrapApply angular.js.xhtml?ln=AngularJS:4182invoke angular.js.xhtml?ln=AngularJS:1447doBootstrap angular.js.xhtml?ln=AngularJS:1467bootstrap angular.js.xhtml?ln=AngularJS:1361angularInit angular.js.xhtml?ln=AngularJS:26054(anonymous function) jquery-1.11.1.js.xhtml?ln=jQuery:3119jQuery.Callbacks.fire jquery-1.11.1.js.xhtml?ln=jQuery:3231jQuery.Callbacks.self.fireWith jquery-1.11.1.js.xhtml?ln=jQuery:3443jQuery.extend.ready jquery-1.11.1.js.xhtml?ln=jQuery:3474completed revjet_contentscript.js:10 GET http://ads.dfgio.com/loader.js?client=dvn1201 net::ERR_BLOCKED_BY_CLIENT

stephanrauh commented 9 years ago

The error message that AngularJS doesn't know about the CalculatorController. Usually that's because the Javascript file hasn't been loaded. By default, the file "main.js" in the folder of the XHTML file is loaded automatically.

Another problem is that there's a breaking change in AngularJS 1.3. Many examples of mine still are based on AngularJS 1.2. The first few lines of the Controller have to look like so:

angular.module("AngularTetris", [ "angularfaces" ])
.controller('AngularTetrisController', ['$scope', function($scope) {

The old way seens to be broken:

function AngularTetrisController($scope) { old syntax - doesn't work with AngularJS 1.3
DimaSol commented 9 years ago

Thank you for the fast answer. It did seems to solve the previous issue but i now getting a new one: (It seems that the way JSF concatenate IDs is problematic for angular?) Any ideas?

angular.js.xhtml?ln=AngularJS:11594 Error: [$parse:syntax] Syntax Error: Token ':' is an unexpected token at column 14 of the expression [myform.j_idt5:firstNumberID.$error] starting at [:firstNumberID.$error]. http://errors.angularjs.org/1.3.9/$parse/syntax?p0=%3A&p1=is%20an%20unexpected%20token&p2=14&p3=myform.j_idt5%3AfirstNumberID.%24error&p4=%3AfirstNumberID.%24error at REGEX_STRING_REGEXP (http://dimas_w7_64:8181/Bennu/javax.faces.resource/angular.js.xhtml?ln=AngularJS:63:12) at Parser.throwError (http://dimas_w7_64:8181/Bennu/javax.faces.resource/angular.js.xhtml?ln=AngularJS:11998:11) at Parser.parse (http://dimas_w7_64:8181/Bennu/javax.faces.resource/angular.js.xhtml?ln=AngularJS:11951:12) at $parse (http://dimas_w7_64:8181/Bennu/javax.faces.resource/angular.js.xhtml?ln=AngularJS:12660:39) at Scope.$get.Scope.$watchCollection (http://dimas_w7_64:8181/Bennu/javax.faces.resource/angular.js.xhtml?ln=AngularJS:14002:30) at link (http://dimas_w7_64:8181/Bennu/javax.faces.resource/angularfaces-directives.js.xhtml?ln=AngularFaces:39:21) at http://dimas_w7_64:8181/Bennu/javax.faces.resource/angular.js.xhtml?ln=AngularJS:8207:44 at invokeLinkFn (http://dimas_w7_64:8181/Bennu/javax.faces.resource/angular.js.xhtml?ln=AngularJS:8213:9) at nodeLinkFn (http://dimas_w7_64:8181/Bennu/javax.faces.resource/angular.js.xhtml?ln=AngularJS:7722:11) at compositeLinkFn (http://dimas_w7_64:8181/Bennu/javax.faces.resource/angular.js.xhtml?ln=AngularJS:7075:13) angular.js.xhtml?ln=AngularJS:11594 (anonymous function)angular.js.xhtml?ln=AngularJS:8544 $getangular.js.xhtml?ln=AngularJS:8215 invokeLinkFnangular.js.xhtml?ln=AngularJS:7722 nodeLinkFnangular.js.xhtml?ln=AngularJS:7075 compositeLinkFnangular.js.xhtml?ln=AngularJS:7078 compositeLinkFnangular.js.xhtml?ln=AngularJS:7078 compositeLinkFnangular.js.xhtml?ln=AngularJS:7078 compositeLinkFnangular.js.xhtml?ln=AngularJS:7078 compositeLinkFnangular.js.xhtml?ln=AngularJS:7717 nodeLinkFnangular.js.xhtml?ln=AngularJS:7075 compositeLinkFnangular.js.xhtml?ln=AngularJS:7717 nodeLinkFnangular.js.xhtml?ln=AngularJS:7075 compositeLinkFnangular.js.xhtml?ln=AngularJS:6954 publicLinkFnangular.js.xhtml?ln=AngularJS:1451 (anonymous function)angular.js.xhtml?ln=AngularJS:14384 $get.Scope.$evalangular.js.xhtml?ln=AngularJS:14483 $get.Scope.$applyangular.js.xhtml?ln=AngularJS:1449 bootstrapApplyangular.js.xhtml?ln=AngularJS:4182 invokeangular.js.xhtml?ln=AngularJS:1447 doBootstrapangular.js.xhtml?ln=AngularJS:1467 bootstrapangular.js.xhtml?ln=AngularJS:1361 angularInitangular.js.xhtml?ln=AngularJS:26054 (anonymous function)jquery-1.11.1.js.xhtml?ln=jQuery:3119 jQuery.Callbacks.firejquery-1.11.1.js.xhtml?ln=jQuery:3231 jQuery.Callbacks.self.fireWithjquery-1.11.1.js.xhtml?ln=jQuery:3443 jQuery.extend.readyjquery-1.11.1.js.xhtml?ln=jQuery:3474 completed

DimaSol commented 9 years ago

I've found a solution of putting prependId="false" on the form tag. Is there any other better solutions?

stephanrauh commented 9 years ago

Yes, unfortunately there's a nasty incompatibilty between AngularJS, jQuery and JSF. You can work around it by adding prependId="false" to the form.

stephanrauh commented 9 years ago

I've spent a couple of days finding a better solution - to no avail. You could configure a different separator character in the web.xml. But every character is either illegal in JSF, forbidden by AngularJS or doesn't work in jQuery. So the only solution is to add prependId="false" or to reort a bug to the AngularJS project.

DimaSol commented 9 years ago

another maybe better workaround maybe to put in the web.xml:

<context-param>
    <param-name>javax.faces.SEPARATOR_CHAR</param-name>
    <param-value>-</param-value>
</context-param>

This will replace the ":" to "-" or any other pre-defined char.

DimaSol commented 9 years ago

using prependId="false" will cause issues with jsf dataTables I think, Am I mistaken?

stephanrauh commented 9 years ago

It's funny you find the solution at the same time I jot them down :).

I tried the separator char, but ran into difficulties, too. Until now, I didn't find problems with prependId="false" - but maybe that's because I prefer PrimeFaces datatables or it's AngularJS counterparts (such as ngTable). BalusC reports somewhere on StackOverflow that prependId does cause problems (but I don't remember which ones).

DimaSol commented 9 years ago

How does PrimeFaces dataTable different from any other JSF dataTable? If you'll put a component (such as inputText) inside a column the ID is seperated with ":" - look at the attached screenshot for "form:cars1:2:j_idt105" as a name. (I've checked in: with http://www.primefaces.org/showcase/ui/data/datatable/edit.xhtml) image

stephanrauh commented 9 years ago

Ah, so that's the problem. AFAIK the Angular team believe that the colon is invalid in ids, so they don't support it (yet).

Maybe using a client side table is the better alternative. I've prepared a ngTable implementation: https://github.com/stephanrauh/AngularFaces/blob/master/AngularFaces_2.0/AngularFaces-2.0-examples/src/main/webapp/carshop/ngtable.xhtml

Live demo of the both the PrimeFaces datatable and ngTable: http://angularfaces.net/showcase2.1/carshop/index.jsf

stephanrauh commented 9 years ago

Hi @DimaSol,

I've got an idea how to solve the problem. Would you like to test my solution when I've finished programming?

Thanks in advance Stephan

DimaSol commented 9 years ago

Sure. What is your approach / solution to this?

stephanrauh commented 9 years ago

I thought of a kind of cheating. Or post-processing. It's possible to grab the HTML code and modify it before sending it to the browser (as I did in my BabbageFaces project). The idea is to replace the colons by - say - a string like "COLON". AFAIK the client side code of JSF doesn't really care about the colons, so this should be OK. However, the names of the input fields in subsequent requests don't match the expectations of the JSF framework. But I suppose it suffices to modify the ids, which aren't sent back to the server. The names can keep their colons, because they aren't processed by jQuery or AngularJS.

If this theory is wrong, AngularFaces also has to intercept the request before it's request. In fact, it already does, but I'm not sure if it's possible to modify the request parameters, so my idea might fail.

stephanrauh commented 9 years ago

Oh, I forgot to say "Thanks!" for your offer to test!

DimaSol commented 9 years ago

I'm afraid this kind of solution could be problematic because you can't know if the client (javascript) components implementation using the ":" seperator to identify cells in the grid. I already developed few features to a grid that were based on the ":" separator on client side. Any way what you are proposing is to change the seperator so why not to use javax.faces.SEPARATOR_CHAR? Am I missing somthing?

stephanrauh commented 9 years ago

According to http://stackoverflow.com/questions/12615434/is-there-a-common-replacement-for-javax-faces-separator-char, "ID and NAME tokens must begin with a letter ([A-Za-z]) and may be followed by any number of letters, digits ([0-9]), hyphens ("-"), underscores ("_"), colons (":"), and periods (".").

"Next to the colon, the only sensible choices are the hyphen, the underscore and the period. As the period is at its own also a special character in CSS selectors, it would have the same problem as the colon. So logically you don't have much other choice than the hyphen - and the underscore _."

As far as I remember, AngularJS doesn't accept the hyphen, and JSF itself has problems with the underscore. But I've forgotten the details.

DimaSol commented 9 years ago

I think using "-" should do the work, I was trying to find an issue with it in angularJS and did find anything yet... Any way I don't sure we should eliminate others (like ".") becasue it is a selector in jQuery as it can be easily escaped.

stephanrauh commented 9 years ago

Yes, the dot can easily easily be escaped. Unfortunately, it's the AngularJS code that runs into the error because AngularJS doesn't escape special characters. To solve things properly, we need to fix AngularJS. I didn't notice similar problems with AngularJS 1.2, so maybe it's a bug they've inadvertently introduced.

stephanrauh commented 9 years ago

I wonder if it's enough to add the hyphen to the regular expression AngularJS complains about in your second stacktrace. I suspect line 175 of Angular.js explicitly forbids special characters - but I'm not sure, I can't wrap my head around this expression. I always thought that (.+) amounts to "any string consisting of at least one character":

var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
stephanrauh commented 9 years ago

AngularJS doesn't accept the colon because it considers it a token - and it doesn't accept tokens in ids of $watchCollection objects:

Lexer.prototype = {
  constructor: Lexer,

  lex: function(text) {
    this.text = text;
    this.index = 0;
    this.tokens = [];

    while (this.index < this.text.length) {
      var ch = this.text.charAt(this.index);
      if (ch === '"' || ch === "'") {
        this.readString(ch);
      } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
        this.readNumber();
      } else if (this.isIdent(ch)) {
        this.readIdent();
      } else if (this.is(ch, '(){}[].,;:?')) {
        this.tokens.push({index: this.index, text: ch});
        this.index++;
      } else if (this.isWhitespace(ch)) {
        this.index++;
      } else {
        var ch2 = ch + this.peek();
        var ch3 = ch2 + this.peek(2);
        var op1 = OPERATORS[ch];
        var op2 = OPERATORS[ch2];
        var op3 = OPERATORS[ch3];
        if (op1 || op2 || op3) {
          var token = op3 ? ch3 : (op2 ? ch2 : ch);
          this.tokens.push({index: this.index, text: token, operator: true});
          this.index += token.length;
        } else {
          this.throwError('Unexpected next character ', this.index, this.index + 1);
        }
      }
    }
    return this.tokens;
  },
stephanrauh commented 9 years ago

Yesterday I remembered what precisely is the issue with using special characters in AngularJS. I've created a demo on Plunkr to illustrate the point (http://plnkr.co/edit/RsaJNTK1VxJJkPAv22X9 - but be patient, Plunkr needs 3-5 minutes for some reason unknown). The name of the input field must not contain any special character other than the underscore. Otherwise, validation errors aren't reported by AngularJS. The same restriction seems to apply to any field you want to set a watch on.

image

<!doctype html>
<html ng-app="MyApp">
<head>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular-animate.min.js"></script>
<script type="text/javascript" src="angularjs-1.2.0-animate-app.js"></script>
</head>
<body>
  <h1>Using special characters in field names of AngularJS</h1>
  This demo shows that AngularJS only accepts the underscore as special character in input field names. In
  particular, the colon which is used in JSF is rejected.<br />
  <br />
<form name="userForm">
  This version uses an underscore within the field's name:<br />
  <div class="field">
    <label for="email_Address">Enter your email address:</label>
    <input type="email" name="email_Address" ng-model="data.email1" required />

    <!-- this stuff is WAY too complex -->
    <div ng-if="userForm.email_Address.$error.required" class="error">
      You forgot to enter your email address...
    </div>

  </div>

  <hr>
  This version uses a hyphen within the field's name:<br />

  <div class="field">
    <label for="email-Address">Enter your email address:</label>
    <input type="email" name="email-Address" ng-model="data.email2" required />

    <!-- this stuff is WAY too complex -->
    <div ng-if="userForm.email-Address.$error.required" class="error">
      You forgot to enter your email address...
    </div>

  </div>

  <hr>
  This version uses a colon within the field's name:<br />

  <div class="field">
    <label for="email:Address">Enter your email address:</label>
    <input type="email" name="email:Address" ng-model="data.email3" required />

    <!-- this stuff is WAY too complex -->
    <div ng-if="userForm.email:Address.$error.required" class="error">
      You forgot to enter your email address...
    </div>

  </div>

  <hr>
  This version doesn't use any special character in the field's name:<br />

  <div class="field">
    <label for="email:Address">Enter your email address:</label>
    <input type="email" name="emailAddress" ng-model="data.email4" required />

    <!-- this stuff is WAY too complex -->
    <div ng-if="userForm.emailAddress.$error.required" class="error">
      You forgot to enter your email address...
    </div>

  </div>

  <input type="submit" />
</form>

</body>
</html>
DimaSol commented 9 years ago

I'm getting errors for all the inputs you have created in your example. I've also noticed that the errors seems to appear only when i'm defining the module like angular.module("calculatorApp", [ "angularfaces" ]) but if it will be without the angularfaces dependancy like angular.module("calculatorApp", []) erros won't appear. notice that the ids still contains the ":"... I was hoping to debug it in my free time in the next days. Have any Idea why it can be?

stephanrauh commented 9 years ago

Which example do you refer do? The Plunkr or the JSF Calculator app?

I've uploaded a version of my Tetris demo using the underscore (see http://www.bootsfaces.net/AngularTetrisOnBootsFaces/). The ids look a bit odd (j_idt16_j_idt29 instead of j_idt16:j_idt29, but so far everything works. But I don't use a serverside JSF dataTable in the demo, so maybe that doesn't mean anything :(.

stephanrauh commented 9 years ago

without the angularfaces dependancy like angular.module("calculatorApp", []) erros won't appear

AngularFaces 2.1 renders error messages on the client side (using the AngularJS components pui-message and pui-label). Probably that's why you need the dependency. You can suppress client-side messages by defining a standard JSF h:message tag. In this case, error messages are rendered on the server, and colons aren't a problem at all.

DimaSol commented 9 years ago

In the Plunkr demo you have created, the validation works fine for all 4 fields so I still missing the problem with using other separator instead of ":". The "_" is really looks a bit odd but i still think "-" can do the job pretty good (unless I'm still missing a point).

DimaSol commented 9 years ago

I understand that without using "angularfaces" dependency the errors are rendered in the server but the thing is that the IDs of the components still contains the ":" separator so I would expect to get the same errors in javascripts so I can only assume it somehow related to the dependency of "angularfaces" and this is what I want to figure out...

stephanrauh commented 9 years ago

I start to suspect the problem isn't the id, but the name of the field. But then, I couldn't set a watch on an id containing a ":", so ids seem to be problematic, too.

Hm... do we have the same Plunkr demo? The error message I'm referring to is the line "You forgot to enter your email address". The second and third example don't show it. The red border and the HTML view in the F12 tool show that AngularJS does detect the errors correcty, but it doesn't put the error messages into the form. In other words, the ng-if="userForm.email-Address.$error.required" condition is always false.

stephanrauh commented 9 years ago

Here's the screenshot belonging to the previous comment: image

stephanrauh commented 9 years ago

Hi Dima,

is there any progress on your side?

Cheers, Stephan

stephanrauh commented 9 years ago

AngularFaces 2.2-SNAPSHOT now copes with colons. I re-implemented the label and the message component. However, several AngularJS components or functions don't allow colons, so application developers may run into problems.

stephanrauh commented 9 years ago

AngularFaces 2.1.3 has been published.

DimaSol commented 9 years ago

Hi Stephan, I had a pretty busy time so didn't yet had a chance to check it out.