< Previous | Next >

Lesson 7: Create a custom Dojo widget

In this lesson, you create a custom Dojo widget so that users can rate movies and add comments.

About this task

Dojo widgets are composed of three files that the New Dojo Widget wizard creates for you:
  • An HTML file
  • A JavaScript file
  • A CSS file
You then edit the HTML template and the JavaScript file.

Procedure

  1. In the Enterprise Explorer view, right-click the dojo folder and select New > Dojo Widget. The New Dojo Widget wizard opens.
  2. In the Module Name field, enter widgets.
  3. In the Widget Name field, enter RateAndComment. The HTML template and style sheet relative for the widget paths are populated automatically. You can add more supertypes to a custom Dojo widget using this wizard, but it is not necessary in this tutorial.
  4. Ensure that the Use AMD format check box is selected.
  5. Click Finish. The JavaScript source file for the widget opens in the editor.
    Code generated for custom widget
    The files for the custom widget are in the Enterprise Explorer in the WebContent/dojo/widgets folder.
    Custom widget files in Enterprise Explorer
  6. In the Enterprise Explorer view, open the RateAndComment.html file in the Rich Page Editor.
  7. Click the Source tab to view the HTML markup.
  8. In the palette, expand the Dojo Application Widgets drawer.
  9. Drag a Dialog widget inside the <div> tags. A Dojo dijit.Dialog is a modal dialog box that blocks access to and dims the screen.
  10. Add a dojo attach point for the Dojo dialog widget:
    <div data-dojo-type="dijit.Dialog" data-dojo-attach-point="dialogbox"></div>
    Attach points enable the JavaScript file to refer to variables defined by the HTML file.
  11. Set a title for the Dojo dialog widget:
    1. Place your cursor after the data-dojo-attach-point attribute value and press Spacebar.
    2. Press Ctrl+Spacebar to open content assist to look for the data-dojo-props attribute, and select it using content assist:
      data-dojo-props in content assist
    3. Place your cursor inside the data-dojo-props attribute value.
    4. Open content assist to look for the title attribute and select it using content assist.
    5. Set the title attribute value to Rate and Comment!
    <div data-dojo-type="dijit.Dialog" data-dojo-attach-point="dialogbox" data-dojo-props="title: 'Rate and Comment!'"></div>
  12. Inside the dialog <div> tag, use content assist to help you add the rating and commenting features:
    1. Type Rate: < and press Ctrl+Spacebar to open content assist.
    2. Select div from the content assist:
      Div in content assist
    3. Place your cursor inside the inserted <div> tag and press the Spacebar.
    4. Open content assist to look for the data-dojo-type attribute and select it using content assist:
      dojotype attribute
    5. Place the cursor between the quotation marks and open content assist. A list of Dojo widgets is displayed.
    6. Look for the dojox.form.Rating widget. As you type the name of the widget, the list shown by the content assist narrows.
      dojox.form.Rating content assist
    7. Edit the <div> tag to look like the following code snippet:
      Rate: <div data-dojo-type="dojox.form.Rating" data-dojo-attach-point="rating" data-dojo-props="numStars:='5'"></div>
      Real-time validation is supported in the editor. If you write an invalid element, it is validated with warnings that are described when you hover over the invalid element. Here is an example of writing an undefined attribute for the dojox.form.Rating dojo widget:
      Undefined attribute validation warning
    8. Copy and paste the following code into the editor, inside the Dialog widget, below the Rating widget:
      <br>
      <br>
      Comments:<div data-dojo-type="dijit.form.Textarea" data-dojo-attach-point = "comments" style="width: 60%;"></div>
      <br>
      <button data-dojo-type="dijit.form.Button" data-dojo-attach-event="onClick: saveFeedback">Submit</button>
      Users can use the text area widget to enter comments. Using the Button widget, users can submit ratings and comments.
  13. In the palette, expand the Dojo Application Widgets drawer.
  14. Drag a Title Pane widget outside the Dialog widget (in the Source pane, after the dijit.Dialog widget closing </div> tag). A title pane has a title on the top and can be expanded or collapsed.
  15. Edit the title pane to look like the following code snippet:
    <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'History of Ratings and Comments'">
    <ol data-dojo-attach-point="feedbacklist"></ol>
    </div>
    A history of comments and ratings are stored inside the ordered list. The HTML template now has the following elements:

    Source for html template

  16. Now that the HTML template for the custom widget is created, you can edit the JavaScript of the custom widget. In the Enterprise Explorer view, open the RateAndComment.js file, which is located under the WebContent/dojo/widgets folder.
  17. Add the module identifiers for the dojo widgets added to the HTML template. Copy and paste the following code to the module dependency array in the define method of the widget:
    dojox/form/Rating
    dojo/data/ItemFileWriteStore
    dijit/form/Textarea
    dijit/Dialog
    dijit/form/Button
    dijit/TitlePane
    Tip: The module dependency array is located in the define method as a parameter.
  18. Copy and paste the following code below the templateString variable:
    movieTitle : "",
    
    	rcStore : new dojo.data.ItemFileWriteStore({
    		data : {
    			items : []
    		}
    	}),
    The movie title holds the name of the movie that is being rated. rcStore is a Dojo data store to store the data that users enter when they rate and comment.
  19. After the constructor function, copy and paste these functions:
    getComment : function(){
    		return this.comments.attr('value');
    	},
    
    	getRating : function(){
    		return this.rating.attr('value');
    	},
    
    	setComment : function(value){
    		this.comments.attr("value", value);
    	}, 
    	
    	setRating : function(value){
    		this.rating.attr("value", value);
    	},
    The functions act as getters and setters for the values of the dojox.form.Rating and dijit.form.Textarea widgets. Each function uses the data-dojo-attach-point value of each widget set in the HTML template to access the value attribute.
  20. After the getters and setters, copy and paste the following code:
    showFeedbackTools : function(movieTitle){
    		this.movieTitle = movieTitle;
    		this.dialogbox.show();
    		},
    The showFeedbackTools function shows the dialog box with the form for rating and commenting. The movieTitle element is the title of the movie that is currently being rated.
  21. After showFeedbackTools, create the function to store the comments and rating into the data store for the widget when the button from the dialog box is clicked. Name the function saveFeedback. This name is the function name that you set on the data-dojo-attach-event attribute for the onClick event of the button in the HTML template for the widget.
    saveFeedback : function(){
    				},
    1. Use the getter functions that you previously wrote to get the values set by the user in the dialog box. Inside the function, type: var userRating = this. and then press Ctrl+Spacebar to use content assist to help you find the rating property in a JavaScript file. Select getRating().
    2. Set a variable for the comments by typing var userComment = this.getComment(); under the userRating variable.
    3. Store the feedback data using the following code:
      	this.rcStore.newItem({
      		title : this.movieTitle,
      		comment : userComment,
      		rating : userRating
      	});
      The data is stored in rcStore as a new item. This new item contains the movie title that is set when the dialog box is opened and the rating and the comments added by the user.
    4. Add the values to the list with the history of comments and ratings made. They are added as child elements of the <ol > HTML element set inside the title pane in the HTML template of the widget.
      dojo.create("li", {
      			innerHTML : "<b>Title:</b> " + this.movieTitle + " <b>Rating:</b>"
      					+ userRating + "<br> <b>Comments:</b> " + userComment
      		}, this.feedbacklist);
    5. To reset values and hide the dialog box, copy and paste the following code after the code from the previous step.
      this.dialogbox.hide();
      this.setComment("");
      this.setRating(0);
      Resetting the values clears the fields. Values from a previous "rate and comment" action do not remain for a new "rate and comment" action. By hiding the dialog, users can continue to rate other movies on the data grid.
  22. Below the saveFeedback function, copy and paste the getCommentsByMovieTitle function. This function receives a movie title as a parameter and returns an array that contains all the comments in the data store for that movie:
    getCommentsByMovieTitle : function(movieTitle) {
    
    		var comments = [];
    		var store = this.rcStore;
    		this.rcStore.fetch({
    			query : {
    				title : movieTitle
    			},
    			onComplete : function(items, request) {
    				dojo.forEach(items, function(item) {
    					comments.push(store.getValue(item, "comment"));
    				});
    			}
    		});
    
    		return comments;
    	},

Results

RateAndComment.js

RateAndComment.js now looks similar to the following code snippet:
define("widgets/RateAndComment", [ "dojo", "dijit", "dijit/_Widget",
		"dijit/_TemplatedMixin", "dijit/_WidgetsInTemplateMixin",
		"dojo/text!widgets/templates/RateAndComment.html", "dojox/form/Rating",
		"dojo/data/ItemFileWriteStore", "dijit/form/Textarea", "dijit/Dialog",
		"dijit/form/Button", "dijit/TitlePane"

], function(dojo, dijit, _Widget, _TemplatedMixin, _WidgetsInTemplateMixin) {
	dojo.declare("widgets.RateAndComment", [ dijit._Widget,
			dijit._TemplatedMixin, dijit._WidgetsInTemplateMixin ],
			{
				// Path to the template
				templateString : dojo.cache("widgets",
						"templates/RateAndComment.html"),

				movieTitle : "",

				rcStore : new dojo.data.ItemFileWriteStore({
					data : {
						items : []
					}
				}),

				// Override this method to perform custom behavior during dijit
				// construction.
				// Common operations for constructor:
				// 1) Initialize non-primitive types (i.e. objects and arrays)
				// 2) Add additional properties needed by succeeding lifecycle
				// methods
				constructor : function() {

				},

				getComment : function() {
					return this.comments.get('value');
				},

				getRating : function() {
					return this.rating.get('value');
				},

				setComment : function(value) {
					this.comments.set("value", value);
				},

				setRating : function(value) {
					this.rating.set("value", value);
				},

				showFeedbackTools : function(movieTitle) {
					this.movieTitle = movieTitle;
					this.dialogbox.show();
				},

				saveFeedback : function() {
					
					var userRating = this.getRating();
					var userComment = this.getComment();
					
					this.rcStore.newItem({
						title : this.movieTitle,
						comment : userComment,
						rating : userRating
					});
					
					dojo.create("li", {
						innerHTML : "<b>Title:</b> " + this.movieTitle + " <b>Rating:</b>"
								+ userRating + "<br> <b>Comments:</b> " + userComment
					}, this.feedbacklist);
					
					this.dialogbox.hide();
					this.setComment("");
					this.setRating(0);
				},
				
				getCommentsByMovieTitle : function(movieTitle) {

					var comments = [];
					var store = this.rcStore;
					this.rcStore.fetch({
						query : {
							title : movieTitle
						},
						onComplete : function(items, request) {
							dojo.forEach(items, function(item) {
								comments.push(store.getValue(item, "comment"));
							});
						}
					});

					return comments;
				},

				// When this method is called, all variables inherited from
				// superclasses are 'mixed in'.
				// Common operations for postMixInProperties
				// 1) Modify or assign values for widget property variables
				// defined in the template HTML file
				postMixInProperties : function() {
				},

				// postCreate() is called after buildRendering(). This is useful
				// to override when
				// you need to access and/or manipulate DOM nodes included with
				// your widget.
				// DOM nodes and widgets with the dojoAttachPoint attribute
				// specified can now be directly
				// accessed as fields on "this".
				// Common operations for postCreate
				// 1) Access and manipulate DOM nodes created in
				// buildRendering()
				// 2) Add new DOM nodes or widgets
				postCreate : function() {
				}
			});
});

RateAndComment.html

In the Source view, RateAndComment.html now looks similar to the following code snippet:
<div class="RateAndComment">
	<div data-dojo-type="dijit.Dialog" data-dojo-attach-point="dialogbox"
		data-dojo-props="title: 'Rate and Comment!'">
		Rate: <div data-dojo-type="dojox.form.Rating" data-dojo-attach-point = "rating" data-dojo-props = "numStars: '5'"></div>
		<br>
		<br>
		Comments: <div data-dojo-type="dijit.form.Textarea" data-dojo-attach-point = "comments" style="width: 60%;"></div>
		<br>
		<button data-dojo-type="dijit.form.Button" data-dojo-attach-event="onClick: saveFeedback">Submit</button>
	</div>
	<div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'History of Ratings and Comments'">
		<ol data-dojo-attach-point="feedbacklist"></ol>
	</div>
</div>
In the Design view, the custom widget now looks similar to the following screen capture:
Custom widget in the Design view

Lesson checkpoint

You created a custom Dojo widget.
You learned:
  • How to modify the HTML template for a custom Dojo widget
  • How to modify the JavaScript file for a custom Dojo widget
< Previous | Next >

Feedback