aboutsummaryrefslogtreecommitdiffstats
path: root/src/directive/ngController.js
blob: e5b8bffd634944c4b2b627af692c121b754ed2f7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
'use strict';

/**
 * @ngdoc directive
 * @name angular.module.ng.$compileProvider.directive.ng-controller
 *
 * @description
 * The `ng-controller` directive assigns behavior to a scope. This is a key aspect of how angular
 * supports the principles behind the Model-View-Controller design pattern.
 *
 * MVC components in angular:
 *
 * * Model — The Model is data in scope properties; scopes are attached to the DOM.
 * * View — The template (HTML with data bindings) is rendered into the View.
 * * Controller — The `ng-controller` directive specifies a Controller class; the class has
 *   methods that typically express the business logic behind the application.
 *
 * Note that an alternative way to define controllers is via the `{@link angular.module.ng.$route}`
 * service.
 *
 * @element ANY
 * @scope
 * @param {expression} ng-controller Name of a globally accessible constructor function or an
 *     {@link guide/dev_guide.expressions expression} that on the current scope evaluates to a
 *     constructor function.
 *
 * @example
 * Here is a simple form for editing user contact information. Adding, removing, clearing, and
 * greeting are methods declared on the controller (see source tab). These methods can
 * easily be called from the angular markup. Notice that the scope becomes the `this` for the
 * controller's instance. This allows for easy access to the view data from the controller. Also
 * notice that any changes to the data are automatically reflected in the View without the need
 * for a manual update.
   <doc:example>
     <doc:source>
      <script type="text/javascript">
        function SettingsController($scope) {
          $scope.name = "John Smith";
          $scope.contacts = [
            {type:'phone', value:'408 555 1212'},
            {type:'email', value:'john.smith@example.org'} ];

          $scope.greet = function() {
           alert(this.name);
          };

          $scope.addContact = function() {
           this.contacts.push({type:'email', value:'yourname@example.org'});
          };

          $scope.removeContact = function(contactToRemove) {
           var index = this.contacts.indexOf(contactToRemove);
           this.contacts.splice(index, 1);
          };

          $scope.clearContact = function(contact) {
           contact.type = 'phone';
           contact.value = '';
          };
        }
      </script>
      <div ng-controller="SettingsController">
        Name: <input type="text" ng-model="name"/>
        [ <a href="" ng-click="greet()">greet</a> ]<br/>
        Contact:
        <ul>
          <li ng-repeat="contact in contacts">
            <select ng-model="contact.type">
               <option>phone</option>
               <option>email</option>
            </select>
            <input type="text" ng-model="contact.value"/>
            [ <a href="" ng-click="clearContact(contact)">clear</a>
            | <a href="" ng-click="removeContact(contact)">X</a> ]
          </li>
          <li>[ <a href="" ng-click="addContact()">add</a> ]</li>
       </ul>
      </div>
     </doc:source>
     <doc:scenario>
       it('should check controller', function() {
         expect(element('.doc-example-live div>:input').val()).toBe('John Smith');
         expect(element('.doc-example-live li:nth-child(1) input').val())
           .toBe('408 555 1212');
         expect(element('.doc-example-live li:nth-child(2) input').val())
           .toBe('john.smith@example.org');

         element('.doc-example-live li:first a:contains("clear")').click();
         expect(element('.doc-example-live li:first input').val()).toBe('');

         element('.doc-example-live li:last a:contains("add")').click();
         expect(element('.doc-example-live li:nth-child(3) input').val())
           .toBe('yourname@example.org');
       });
     </doc:scenario>
   </doc:example>
 */
var ngControllerDirective = ['$controller', '$window', function($controller, $window) {
  return {
    scope: true,
    controller: '@'
  }
}];
(element)).toEqual('<div>hello <span ng:bind="name"></span>!</div>'); scope.name = 'Misko'; scope.$digest(); expect(sortedHtml(element)).toEqual('<div>hello <span ng:bind="name">Misko</span>!</div>'); }); it('should translate {{}} in terminal nodes', function() { compile('<select ng:model="x"><option value="">Greet {{name}}!</option></select>'); scope.$digest(); expect(sortedHtml(element).replace(' selected="true"', '')). toEqual('<select ng:model="x">' + '<option ng:bind-template="Greet {{name}}!">Greet !</option>' + '</select>'); scope.name = 'Misko'; scope.$digest(); expect(sortedHtml(element).replace(' selected="true"', '')). toEqual('<select ng:model="x">' + '<option ng:bind-template="Greet {{name}}!">Greet Misko!</option>' + '</select>'); }); it('should translate {{}} in attributes', function() { compile('<div src="http://server/{{path}}.png"/>'); expect(element.attr('ng:bind-attr')).toEqual('{"src":"http://server/{{path}}.png"}'); scope.path = 'a/b'; scope.$digest(); expect(element.attr('src')).toEqual("http://server/a/b.png"); }); describe('OPTION value', function() { beforeEach(function() { this.addMatchers({ toHaveValue: function(expected){ this.message = function() { return 'Expected "' + this.actual.html() + '" to have value="' + expected + '".'; }; var value; htmlParser(this.actual.html(), { start:function(tag, attrs){ value = attrs.value; }, end:noop, chars:noop }); return trim(value) == trim(expected); } }); }); afterEach(function() { if (element) element.remove(); }); it('should populate value attribute on OPTION', function() { compile('<select ng:model="x"><option>abc</option></select>'); expect(element).toHaveValue('abc'); }); it('should ignore value if already exists', function() { compile('<select ng:model="x"><option value="abc">xyz</option></select>'); expect(element).toHaveValue('abc'); }); it('should set value even if newlines present', function() { compile('<select ng:model="x"><option attr="\ntext\n" \n>\nabc\n</option></select>'); expect(element).toHaveValue('\nabc\n'); }); it('should set value even if self closing HTML', function() { // IE removes the \n from option, which makes this test pointless if (msie) return; compile('<select ng:model="x"><option>\n</option></select>'); expect(element).toHaveValue('\n'); }); }); it('should bind href', function() { compile('<a ng:href="{{url}}"></a>'); expect(sortedHtml(element)).toEqual('<a ng:bind-attr="{"href":"{{url}}"}"></a>'); }); it('should bind disabled', function() { compile('<button ng:disabled="{{isDisabled}}">Button</button>'); scope.isDisabled = false; scope.$digest(); expect(element.attr('disabled')).toBeFalsy(); scope.isDisabled = true; scope.$digest(); expect(element.attr('disabled')).toBeTruthy(); }); it('should bind checked', function() { compile('<input type="checkbox" ng:checked="{{isChecked}}" />'); scope.isChecked = false; scope.$digest(); expect(element.attr('checked')).toBeFalsy(); scope.isChecked=true; scope.$digest(); expect(element.attr('checked')).toBeTruthy(); }); it('should bind selected', function() { compile('<select><option value=""></option><option ng:selected="{{isSelected}}">Greetings!</option></select>'); jqLite(document.body).append(element) scope.isSelected=false; scope.$digest(); expect(element.children()[1].selected).toBeFalsy(); scope.isSelected=true; scope.$digest(); expect(element.children()[1].selected).toBeTruthy(); }); it('should bind readonly', function() { compile('<input type="text" ng:readonly="{{isReadonly}}" />'); scope.isReadonly=false; scope.$digest(); expect(element.attr('readOnly')).toBeFalsy(); scope.isReadonly=true; scope.$digest(); expect(element.attr('readOnly')).toBeTruthy(); }); it('should bind multiple', function() { compile('<select ng:multiple="{{isMultiple}}"></select>'); scope.isMultiple=false; scope.$digest(); expect(element.attr('multiple')).toBeFalsy(); scope.isMultiple='multiple'; scope.$digest(); expect(element.attr('multiple')).toBeTruthy(); }); it('should bind src', function() { compile('<div ng:src="{{url}}" />'); scope.url = 'http://localhost/'; scope.$digest(); expect(element.attr('src')).toEqual('http://localhost/'); }); it('should bind href and merge with other attrs', function() { compile('<a ng:href="{{url}}" rel="{{rel}}"></a>'); expect(sortedHtml(element)).toEqual('<a ng:bind-attr="{"href":"{{url}}","rel":"{{rel}}"}"></a>'); }); it('should bind Text with no Bindings', function() { forEach(['checked', 'disabled', 'multiple', 'readonly', 'selected'], function(name) { compile('<div ng:' + name +'="some"></div>'); expect(element.attr('ng:bind-attr')).toBe('{"' + name +'":"some"}'); expect(element.attr(name)).toBe(name); dealoc(element); }); compile('<div ng:src="some"></div>'); expect(sortedHtml(element)).toEqual('<div ng:bind-attr="{"src":"some"}" src="some"></div>'); dealoc(element); compile('<div ng:href="some"></div>'); expect(sortedHtml(element)).toEqual('<div href="some" ng:bind-attr="{"href":"some"}"></div>'); dealoc(element); }); it('should Parse Text With No Bindings', function() { var parts = parseBindings("a"); assertEquals(parts.length, 1); assertEquals(parts[0], "a"); assertTrue(!binding(parts[0])); }); it('should Parse Empty Text', function() { var parts = parseBindings(""); assertEquals(parts.length, 1); assertEquals(parts[0], ""); assertTrue(!binding(parts[0])); }); it('should Parse Inner Binding', function() { var parts = parseBindings("a{{b}}C"); assertEquals(parts.length, 3); assertEquals(parts[0], "a"); assertTrue(!binding(parts[0])); assertEquals(parts[1], "{{b}}"); assertEquals(binding(parts[1]), "b"); assertEquals(parts[2], "C"); assertTrue(!binding(parts[2])); }); it('should Parse Ending Binding', function() { var parts = parseBindings("a{{b}}"); assertEquals(parts.length, 2); assertEquals(parts[0], "a"); assertTrue(!binding(parts[0])); assertEquals(parts[1], "{{b}}"); assertEquals(binding(parts[1]), "b"); }); it('should Parse Begging Binding', function() { var parts = parseBindings("{{b}}c"); assertEquals(parts.length, 2); assertEquals(parts[0], "{{b}}"); assertEquals(binding(parts[0]), "b"); assertEquals(parts[1], "c"); assertTrue(!binding(parts[1])); }); it('should Parse Loan Binding', function() { var parts = parseBindings("{{b}}"); assertEquals(parts.length, 1); assertEquals(parts[0], "{{b}}"); assertEquals(binding(parts[0]), "b"); }); it('should Parse Two Bindings', function() { var parts = parseBindings("{{b}}{{c}}"); assertEquals(parts.length, 2); assertEquals(parts[0], "{{b}}"); assertEquals(binding(parts[0]), "b"); assertEquals(parts[1], "{{c}}"); assertEquals(binding(parts[1]), "c"); }); it('should Parse Two Bindings With Text In Middle', function() { var parts = parseBindings("{{b}}x{{c}}"); assertEquals(parts.length, 3); assertEquals(parts[0], "{{b}}"); assertEquals(binding(parts[0]), "b"); assertEquals(parts[1], "x"); assertTrue(!binding(parts[1])); assertEquals(parts[2], "{{c}}"); assertEquals(binding(parts[2]), "c"); }); it('should Parse Multiline', function() { var parts = parseBindings('"X\nY{{A\nB}}C\nD"'); assertTrue(!!binding('{{A\nB}}')); assertEquals(parts.length, 3); assertEquals(parts[0], '"X\nY'); assertEquals(parts[1], '{{A\nB}}'); assertEquals(parts[2], 'C\nD"'); }); it('should Has Binding', function() { assertTrue(hasBindings(parseBindings("{{a}}"))); assertTrue(!hasBindings(parseBindings("a"))); assertTrue(hasBindings(parseBindings("{{b}}x{{c}}"))); }); });