Testing ngModel in Jasmine - javascript

I have a directive that creates some input fields and links them to the scope with ngModel. When the user of the directive creates this in the HTML file:
<div tk-quick-form="formStructure" form-data="formData" id="main_form" ></div>
and this in the controller:
$scope.formStructure = [
{
fieldName: 'name',
type: 'string'
}
];
Then an input field will be created with ngModel linking in to formData.name.
The directive works as planned, but I don't know how to test for it in Jasmine.
This is what I'm doing so far:
Under the it function:
$rootScope.formStructure = [
{
fieldName: 'name',
type: 'string'
}
];
var element = $compile('<div tk-quick-form="formStructure" form-data="formData" id="main_form" ></div>"')($rootScope);
$(element).find("input#name").val("set")
dump($(element).find("input#name").val()) // outputs "set"
expect($rootScope.formData.name).toBe("set"); //outputs Expected undefined to have value 'set'
//(although in manual testing $scope.formData.name is actually set because the input field contains ng-model="formData.name")
$rootScope.$digest();
How should I write this test?

As for me you were doing everything right, except couple of things. You should have called $rootScope.$digest() before expect. Also, you have to trigger 'input' with .triggerHandler('input'), so model will get updated.
describe('tkQuickForm', function () {
beforeEach(module('tkQuickForm'));
beforeEach(inject(function ($rootScope, $compile) {
this.$rootScope = $rootScope;
this.scope = $rootScope.$new();
this.$compile = $compile;
}));
it('should pass dummy test', function () {
expect(true).toBe(true);
});
it('should link ng-model to formData', function() {
this.scope.formStructure = [
{
fieldName: 'name',
type: 'string'
}
];
var element = this.$compile(`
<div tk-quick-form="formStructure" form-data="formData" id="main_form"></div>"
`)(this.scope);
this.scope.$digest(); // call digest first
var input = element.find("input")[0];
angular.element(input).val('set').triggerHandler('input');
expect(this.scope.formData.name).toBe("set");
});
});
Checked with "angular": "~1.5.0". All specs are OK.
Related pull-request

Related

access resolve in component controller in angularjs 1.5

I am trying to bind to an angularjs 1.5 component a resolve value without success, In the state definition, I have replaced the common value of template properties with a value of the name of my new component. like this:
.state('eventslogs.create', {
url: '/create',
template: '<addevent data="$resolve.addevent"></addevent>.',
resolve: {
addevent: newEventslog
},
data: {
roles: ['admin'],
pageTitle: 'Eventslogs Create'
}
})
NewEventslog is a function that injects one of my services
newEventslog.$inject = ['EventslogsService'];
function newEventslog(EventslogsService) {
return new EventslogsService();
}
In my controller I have tried several ways but nothing works
angular.module('eventslogs')
.component('addevent', {
templateUrl: 'addevent.client.component.view.html',
bindings: {
data: '<'
},
controller: function($scope, $element) {
var vm = this;
vm.eventslog = vm.data;
}
But vm.eventslog always results in an undefined value, what's wrong with my aproach?, If I use "#" instead "<" in bindings, vm.addevent results in a string with value "$resource.addevent" and not like an instance of newEventslog function.
I am using ui-route version 0.2.18
not sure but try this inside the component
this.$onInit = () => {
vm.eventslog = vm.data;
}

Dynamically including directive in an AngularJS partial

I have a list of directives (normally form fields and custom form controls). Now I will get the list of directive names from backend to use to build a form.
It basically creates a dynamic form where I don't know what all form fields are in the form (it depends on the JSON config file I get from the backend).
Sample JSON:
field1 : {
type: 'text',
directive : 'directive1'
},
field2: {
type : 'dropdown',
directive : 'dropdown-directive'
}
Can I do something similar in AngularJS, and if possible, how?
Use the $compile service against the scope. This will allow you to compile angular code which can be appended to a container.
See jsfiddle: http://jsfiddle.net/p8jjZ/1/
HTML:
<div ng-app="myApp" ng-controller="MainController">
<div custom-elements="myData.elements"></div>
<p>{{user}}</p>
</div>
JavaScript:
var mod = angular.module("myApp", []);
mod.controller("MainController", function ($scope) {
$scope.myData = {};
$scope.myData.elements = {
field1 :{ type: 'text', directive : 'directive1' },
field2: { type : 'dropdown', directive : 'dropdown-directive' }
};
});
mod.directive("customElements", function ($compile) {
return {
restrict: "A",
scope: {
customElements: "="
},
link: function (scope, element, attrs) {
var prop,
elems = scope.customElements,
currElem,
compiled;
for (prop in elems) {
currElem = elems[prop];
console.log("Working on " + prop);
//compile input against parent scope. Assuming directives are attributes, but adapt to your scenario:
compiled = $compile('<div ' + currElem.directive + '></div>')(scope.$parent);
//append this to customElements
element.append(compiled);
}
}
}
});
mod.directive("directive1", function () {
return {
restrict: "A",
template: '<div>Whoa! I am directive 1<br><input type="text" ng-model="user.name"></div>'
}
});
mod.directive("dropdownDirective", function () {
return {
restrict: "A",
template: '<div>I am another directive<br><select ng-model="user.color"><option value="blue">blue</option><option value="green">Green</option></div>'
}
});
The customElement directive just creates the directive as if it were an attribute on an element. This is a very simple example, but should get you started on what you are looking to do where you can update the logic that builds the elements/directive accordingly.

AngularJS - model changes not updating the view

AngularJS Classic: Im changing my Model on ng-click an the view is not updating. I thought a simply $scope.$apply() would update but i'm not getting it working. I suppose I'm not setting $apply() in the right place or something like that.
Example: Plunker
The name of the Object is changed on Press button.
Just update your explorerObject one more time after clicking, cause it is still pointing on your previous object:
$scope.click = function () {
ExplorerService.setExplorerObject({
selected : {
name: 'Defined Now !',
id: 'id',
timestamp: 'timestamp'
},
templateURL: 'views/beispiel.html'
});
$scope.explorerObject = ExplorerService.getExplorerObject(); // <--- here
}
Working: http://plnkr.co/edit/UWE7o3mtAzY3xzYRWfkf?p=preview
After question' edit:
You can use $watch in your second controller:
app.controller('SecondCtrl', function($scope, ExplorerService) {
$scope.$watch(function(){
return ExplorerService.getExplorerObject();
},function(n,o){
$scope.explorerObject = ExplorerService.getExplorerObject();
},true)
});
Working: http://plnkr.co/edit/8mZO5kZmTrqwHKnxtBSd?p=preview
Or use a $broadcast approach, like:
app.controller('SecondCtrl', function($scope, ExplorerService) {
$scope.explorerObject = ExplorerService.getExplorerObject();
$scope.$on("EXPLORER_OBJECT_CHANGED" ,function(event,obj){
$scope.explorerObject = obj;
});
});
And in your service add: $rootScope.$broadcast("EXPLORER_OBJECT_CHANGED", explorerObject);
Example: http://plnkr.co/edit/9WKypJTb0wViQZ01m9TN?p=preview

Select an option in dropdown using a directive in angularjs

I am new to angularjs and working to directive for a dropdown but having problem to select initial value. I have to pass the Id of the option to select as an attribute to the directive. I am setting the attribute value in a scope variable which I then use in ng-Init.
Here is my directive code:
export class ChoiceGroup
{
constructor ()
{
var directive: ng.IDirective = {};
directive.restrict = "E";
directive.replace = true;
directive.template = '<div><select name="cmbCG" ng-model="dataSel" ng-options="c.DisplayName for c in data"></select></div>';
directive.link = function ($scope: any, element: JQuery, attributes: any) {
var injector = angular.element(document).injector();
var key = attributes.key;
var cgCacheService: ChoiceGroupsCacheService;
seCGCacheService = injector.get('seChoiceGroupsCacheService');
var values: Array<ChoiceValue>;
values = seCGCacheService.GetChoiceValues(key);
$scope.data = values;
if (attributes.selectedvalue) {
$scope.selectedvalue = values[attributes.selectedvalue];
}
}
return directive;
}
The code is in Typescript. Here is HTML:
<choicegroupcombo key="RecurrenceFrequency" selectedvalue="3"></choicegroupcombo>
If I hardcode the value of dataSel ie ng-init="dataSel=3" then it works fine but when I set to scope variable then it does not work. So how can I fix the issue.
EDIT
Solved. I have updated the code accordingly.
I think this might be a classic case of "over-engineering".
Your code doesn't seem to be trying to do anything other than what a standard select box does.
Try this instead:
app = angular.module('app', [])
app.controller 'MainCtrl', ($scope) ->
# $scope.values = ChoiceGroupsCacheService(key)
$scope.values = [
{ id: 0, label: 'Zero' }
{ id: 1, label: 'One' }
{ id: 2, label: 'Two' }
{ id: 3, label: 'Three' }
]
$scope.number = 2
and the view:
<select ng-model="number" ng-options="item.id as item.label for item in values"></select>
Code is in coffeescript:
Here's a plunkr: http://plnkr.co/edit/C6qmUoJ6HjsAT69oavRg

Angular.js: Is .value() the proper way to set app wide constant and how to retrieve it in a controller

Hi there I was watching a couple of the angular.js videos and saw that the value() method was used to set a kind of module-wide constant. for example, one can set the Angular-UI library's config like so: (coffeescript)
angular.module('app',[])
.value "ui.config",
tinymce:
theme: 'simple'
width: '500'
height: '300'
And my app is currently looking like this:
window.app = angular.module("app", [ 'ui'])
.config(["$routeProvider", ($routeProvider) ->
$routeProvider
.when "/users",
templateUrl: "assets/templates/users/index.html"
controller: IndexUsersCtrl
.otherwise redirectTo: "/users"
])
.value 'csrf', $('meta[name="csrf-token"]').attr('content') #<---- attention here
IndexUsersCtrl = ($scope) ->
$scope.users = gon.rabl
console.log "I want to log the csrf value here" #<---- then attention
IndexUsersCtrl.$inject = ['$scope']
But I can't seem to get that value by tapping into the 'app' variable which is corresponding to the app module.
I read up here on ST and over on angularjs's google group that one way to share common code btwn controllers is through a service, will this concept apply here, too?
Thanks!
Module.value(key, value) is used to inject an editable value,
Module.constant(key, value) is used to inject a constant value
The difference between the two isn't so much that you "can't edit a constant", it's more that you can't intercept a constant with $provide and inject something else.
// define a value
app.value('myThing', 'weee');
// define a constant
app.constant('myConst', 'blah');
// use it in a service
app.factory('myService', ['myThing', 'myConst', function(myThing, myConst){
return {
whatsMyThing: function() {
return myThing; //weee
},
getMyConst: function () {
return myConst; //blah
}
};
}]);
// use it in a controller
app.controller('someController', ['$scope', 'myThing', 'myConst',
function($scope, myThing, myConst) {
$scope.foo = myThing; //weee
$scope.bar = myConst; //blah
});
I recently wanted to use this feature with Karma inside a test. As Dan Doyon points out the key is that you would inject a value just like a controller, service, etc. You can set .value to many different types - strings, arrays of objects, etc. For example:
myvalues.js a file containing value - make sure it is including in your karma conf file
var myConstantsModule = angular.module('test.models', []);
myConstantModule.value('dataitem', 'thedata');
// or something like this if needed
myConstantModule.value('theitems', [
{name: 'Item 1'},
{name: 'Item 2'},
{name: 'Item 3'}
]);
]);
test/spec/mytest.js - maybe this is a Jasmine spec file loaded by Karma
describe('my model', function() {
var theValue;
var theArray;
beforeEach(module('test.models'));
beforeEach(inject(function(dataitem,theitems) {
// note that dataitem is just available
// after calling module('test.models')
theValue = dataitem;
theArray = theitems;
});
it('should do something',function() {
// now you can use the value in your tests as needed
console.log("The value is " + theValue);
console.log("The array is " + theArray);
});
});
You need to reference csrf in your controller IndexUsersCtrl = ( $scope, csrf )
IndexUsersCtrl.$inject = [ '$scope', 'csrf' ]

Categories

Resources