Writing a Sencha Touch Application, Part 4

This is the last of a four-part series on how to write a Sencha Touch application. If you’re new to the series, here are the links to the previous installments:

In part 3 of this tutorial we worked on the Note Editor and added the ability to create notes. It is time now to let our users edit and delete notes. Let’s work on the edit note feature first.

Disclosure events in a Sencha Touch List

When the person using the application touches a note’s disclosure button in the Notes List view, the selected note should be displayed in the Edit Note view:

We can complete this feature by implementing the onItemDisclosure handler of the notes list. Here’s the code:

NotesApp.views.notesList = new Ext.List({
    id: 'notesList',
    store: 'NotesStore',
    onItemDisclosure: function (record) {
        var selectedNote = record;
        NotesApp.views.noteEditor.load(selectedNote);
        NotesApp.views.viewport.setActiveItem('noteEditor', { type: 'slide', direction: 'left' });
    },
    itemTpl: '
<div class="list-item-title">{title}</div>' +
        '<div class="list-item-narrative">{narrative}</div>',
    listeners: {
        'render': function (thisComponent) {
            thisComponent.getStore().load();
        }
    }
});

The handler function accepts the selected note as a parameter. What we need to do in the handler is load the note in the editor utilizing the editor’s load() method, and then, make the Note Editor view active via a call to the viewport’s setActiveItem().

Very cool. At this point we can switch over to the emulator, where we should be able to edit notes.

Removing records from a data store

Deleting notes is also very easy. The Trash button on the Note Editor view’s bottom toolbar will trigger this function. We need to change the button’ handler like so:

NotesApp.views.noteEditorBottomToolbar = new Ext.Toolbar({
    dock: 'bottom',
    items: [
        { xtype: 'spacer' },
        {
            iconCls: 'trash',
            iconMask: true,
            handler: function () {

                var currentNote = NotesApp.views.noteEditor.getRecord();
                var notesList = NotesApp.views.notesList;
                var notesStore = notesList.getStore();

                if (notesStore.findRecord('id', currentNote.data.id)) {
                    notesStore.remove(currentNote);
                }

                notesStore.sync();

                notesList.refresh();
                NotesApp.views.viewport.setActiveItem('notesListContainer', { type: 'slide', direction: 'right' });
            }
        }
    ]
});

After acquiring references to the current note, the notes list and the notes data store, we use the store’s findRecord() function to find and remove the note loaded on the editor.

Next, we call sync() on the store to make the removal permanent, and proceed to re-render the notes list and make the Notes List view active.

This procedure is similar to the one we followed when we implemented the Save Note feature, although in this case we’re deleting the note, not saving it.

A quick check on the emulator should confirm that we can now delete notes.

Grouping items in a Sencha Touch List

One last thing we want to do is, in the Notes List view, render the notes grouped by date. This will make it easier for our users to work with their notes.

First, we need to tell our notesList Ext.List that it needs to render its items grouped. We can do this using the grouped config option like so:

NotesApp.views.notesList = new Ext.List({
    id: 'notesList',
    store: 'NotesStore',
    grouped: true,
    emptyText: '<div style="margin: 5px;">No notes cached.</div>',
    onItemDisclosure: function (record) {
        var selectedNote = record;
        NotesApp.views.noteEditor.load(selectedNote);
        NotesApp.views.viewport.setActiveItem('noteEditor', { type: 'slide', direction: 'left' });
    },
    itemTpl: '<div class="list-item-title">{title}</div>' +
        '<div class="list-item-narrative">{narrative}</div>',
    listeners: {
        'render': function (thisComponent) {
            thisComponent.getStore().load();
        }
    }
});

Then, we need to override the getGroupString() function of the NotesStore:

Ext.regStore('NotesStore', {
    model: 'Note',
    sorters: [{
        property: 'date',
        direction: 'DESC'
    }],
    proxy: {
        type: 'localstorage',
        id: 'notes-app-store'
    },
    getGroupString: function (record) {
        if (record && record.data.date) {
            return record.get('date').toDateString();
        } else {
            return '';
        }
    }
});

You can define the grouping behavior of the store using the groupField property and the getGoupString() function.

The getGroupString() function returns the string to group on, based on the store’s data model’s properties. By default, getGroupString() returns the value of the groupField property. In our case we want to use the value of the note’s date as the group’s header, but we want to format the value as a date.

If we check the emulator, the notes taken the same day should render under the same group header:

We made it!

This is pretty much it. A very simple Sencha Touch application and an easy way to learn some of the features of the Sencha Touch framework.

I hope you take this app to the next level as you learn more about the framework. :-)

And don’t forget to leave a comment letting me know the Sencha Touch topics about which you would like me to write.

Download the Notes App: Notes-App-v1.0.zip

Here’s the rest of the series:

Want To Learn More?

My Sencha Touch books will teach you how to create an application from scratch.

Comments

  1. Dave Fowler says

    Great tutorial

    I found it really useful and informative – would love to see a tutorial on turning it into a true phone app with integrated database :)

    • coolon says

      I think it would be really useful to make some topics about another simple sencha touch application – RSS reader

      Also its interesting to read about sencha touch customization – custom buttons and custom app design

      thanks

  2. Dario J. says

    This is an excellent tutorial! However, I wonder how would you modify it to use remote store (ajax or rest proxy) instead of local store? I tried to modify proxy property of the store:

    proxy : {
    type : ‘ajax’,
    url : ‘/notes.php’,
    reader : { type : ‘json’ }
    }, (…)

    where “notes.php” is simple script on the that returns JSON encoded list of notes from the database.

    Now, list of notes gets populated, as well as edit note form. But when you try to save changes (Save button), or add new note, nothing happens. Notes app sends empty POST request to server (notes.php script)?

    Any suggestions? Thanks!

  3. Stenlo says

    Thanks, this is a very nice tutorial. How would you save the notes on a server and not only locally?

    Best,
    Stenlo

  4. Jim says

    Very well done. Complete with Create, Read, Update and Delete operations on data store. I learned a lot. Thanks.

  5. TJ says

    Well done.

    I agree with Klaus that breaking it up into the Sencha recommended directories would be a nice next step.

    It would also be great if you could add pointers to more general discussions of the various topic areas you introduce: e.g., models/stores/dataviews; navigation; verification and validation, etc.

    But, overall, this is an excellent tutorial as it is.

    Thanks!

  6. Ted says

    Great tutorial. I’ve got a question. In the section called “Disclosure events in a Sencha Touch List”, in the code on about line 12, you define a listener for the ‘render’ event. What is this for? Thanks in advance.

  7. Oleg says

    Awesome tutorial, well done!

    I would love to see some tutorials on TDD and BDD using Jasmine and how to run the tests on a build server.

    Thanks and keep blogging

  8. Homan says

    Hey!
    Great tutorial!

    I have one question though:

    I’m making an application where I want to populate a textfield with the record from a list item and I just can’t get it to work. Could you point me in the right direction?

    Again, great tutorial…it helped me a bunch!

    Best regards,
    Homan

  9. Brad says

    Outstanding article! Thank you. In addition to the remote JSON via AJAX questions already mentioned above, can you please explain what the listeners: ‘render’ is for in the NotesList? This just showed up between parts 3 and 4 in the code without saying why.

  10. Dinesh says

    Thanks for the wonderful tutorial. I have a question. when I use this line of code NotesList.refresh(); in this blockNotesApp.views.noteEditorBottomToolbar = new Ext.Toolbar. I am not able to go back to Note list when I delete a note. When I did not use refresh it work fine. Can you tell me what could be the problem?

  11. Scott B says

    It would be great to see a tutorial on adding a logo to the top of the app and changing the color schemes.

  12. Zach M says

    How is the localstorage suppose to work? When I refresh in my iPhone browser the data gets wiped. Anyone else having this issue? Thanks in advance.

  13. xmo says

    Hey! Really helpful tutorial for begginers like me! I have one question, how can I change the code to pass the informations in the note’s list to another panel and show this information again in a html format instead to load the informations in a form? I want it to show more detailed informations about each item in my list. Thank you!

  14. AOXO says

    I am also experiencing problems with the local store when I refresh my browser or restart my Ipad the notes are wiped.
    Another minor issue is when my note is delete via the delete button int he bottom toolbar the notes list view does not become active. Would appreciate your suggested solution and how to save notes to a server

  15. CM says

    Hey, thx for this great tutorial, thx to you i was able to make the list fit, i was trying that for while now… .

  16. Muhammad Faisal says

    Thnx jorge, this article was very helpfull.
    i am facing a problem. when i generat grouped list according to ur method then a vertcal bar at right side will be displayed having (A – Z) alphabets. i want to remove this bar plz help me.
    something like that;
    A
    B
    C
    D
    E
    F….

  17. Uche Ngadi says

    This tutorial really helped me to get started with Sencha Touch. However, i will request that you provide us with a tutorial on retrieving and manipulating data from a database say MySQL.

    Thank you very much all the same.

    Good work.

  18. SUKANTA GHORU says

    Hi Jorge,

    Thanks you very much to write a wonderful article with Sencha Touch for beginners.

    This tutorial really helped me to get started with Sencha Touch. It would be really helpful for a beginner like me if you please share a tutorial on retrieving and manipulating data from a database like SQL Server. User can work with records in offline and when Internet connection will get available, then all the records stored locally will be sent to the DataBase.

    Great work.

    Thanks & Regards,
    Sukanta Ghorui

  19. says

    Hİ Jorge ,

    Thanks tutorial is very useful especially toolbar spacer object. I couldnt find anywhere use spacer object via this tutorial Iearned how can I use it.

    Regards.

  20. kprk says

    Hi Jorge,
    It is really awesome. This is what I was looking for . I was goggling since morning to know how to start ST application, but you saved my day. I am already reader of your cookbook. Can you update some articles on ST2 as it is released couple of days before.

  21. Andy Jones says

    Jorge
    Many thanks as a beginner here your Notes example provides a simple way of understanding the components of the app and how they work together.
    I do have an ask however, do you have an example with how i can access information from a website and display within my app. for example hooking into my tweets or being able to render news items from a website.
    I am also looking to build my own website with a database from which i provide the data for my mobile pages. i take it i need to do this using something like php. do you have any simple references to how to achieve this.

    Many thanks again.
    Andy j

  22. AAT says

    I echo the comments of gratitude. Marvelous job especially in contrast with the “Getting started” guides on Sencha website (their 2.0 guides make no sense whatsoever i.e. following the code examples result in error after erro) Really bad for someone just getting into the framework.

    Your work is truly laudable. Thank you for taking the time!

    I look forward to reading your tutorials on working with JSON and served data.

  23. says

    The tutorial was really helpful.

    There’s just one problem when i run the application. When i remove a note and refresh my browser the notes app crashes. The error i get is ” Cannot read property ‘id’ of null”. This happens inside the Sencha Touch debug file, not the notes script itself. My guess is that something goes wrong with the local storage.

    Has anyone else had this problem and if so, how did you solve it? I’ve encountered this problem within my own version of the app as have i in the version that Jorge has put at the end of the tutorial.

    Thank you!

  24. says

    Great tutorial man!!

    This gave me a great insight on how to start doing my own sencha touch app, thank you very much! Now let’s expand it even more!! BTW… have u tried to make it a native app with phonegap? Any advice on this? Saludos bro!

    Adrian

  25. says

    Jorge,

    Excellent tutorial! I had a great time following along building the app step by step and I have to say I was pleasantly surprised and very happy that it worked every step. It was my first attempt with building a mobile app. i used Eclipse along with the Android ADT plugin, the PhoneGap plugin by Mobile Developer Solutions and was able to build it as a native app quite effortlessly!

    Thanks again for making it possible.

    Dave

    P.S. If you were to continue with more tutorials on this app, I would love to see how you would integrate it with a server side storage solution such as MongoDB or MySQL and having the application be able to sync while connected.

    • says

      Thank you, Dave. It’s very rewarding to see how these tutorials have helped so many people.

      Stay tuned. I will be talking about server-side storage and synchronization in the coming weeks.

  26. max says

    App works fine but im having problems with making app offilne using cache manifest? It works offline perfectly fine for a few times i use it, then all my notes disappear from my phone when I reconnect to the internet? I think some should touch up on this topic also, that would make a complete package of this great app.

  27. Juan says

    Jorge great tutorial.

    I have one question, how to control that the same note is not saved more than once on the local storage? In my localstorage, the record is store one time, but on the id key keeps adding the id even that is already present, if the user press the save button again and again. The consequence of that is when I delete the item from my list, it will still be shown on the list if the id is more than one time.

  28. Andres says

    I have read that there is a component that ExtJs ComboBox is a variation of this component is called TimeField and serves to display values ​​over time. Most properties are the same as the ComboBox. But I failed to get results like to know if you can help with that.

  29. tony says

    I’ve typed in the code, I’ve copied and pasted from the web and the book and no luck compiling. Mac and Aptana. Can you up load the source files? I have found differences between the web and book versions. I’m a Flex guy trying to get into to this and would appreciate it.
    Thanks,
    Tony

    • says

      Tony, I’m not sure what you mean that you’ve had no luck compiling. Please post the specific error you’re seeing so we can help you.

  30. Hacho says

    I ran through your tutorial making my notes as I went along. My intent was to get familiar with the API and the calls you were making. The only thing that caught me off guard / confused me was in your last edit of:

    views.noteEditorBottomToolbar

    In it, you reference

    currentNote.data.id

    I didn’t find anywhere in the API that said that indicated Model “data” could be accessed that way. The closest i got was a get() method.

    I also noticed in the very last edit, defining the getGroupString function, you use both methods to reference the “data” in a Model object

    if (record && record.data.date) {
    return record.get(‘date’).toDateString();

    Can you shed a little more light for me?

    By the way, fantastic tutorial. I actually bought the PDF because it was so good.

    • says

      Thank you, Hacho. Both ways are acceptable. I personally prefer using accessor methods (get(‘something’) or getSomething()) if they exist. But I wanted to show both ways in the tutorial.

  31. Jeffyt says

    Great tutorial!

    I have worked through it have a working demo application!

    I do have one question that I am struggling with, I would like to add a check box to the note screen. In my code below I can see the checkbox on the note edit screen and check it but it saves it as false always.

    Here is my model:
    Ext.regModel(‘Note’, {
    idProperty: ‘id’,
    fields: [
    { name: ‘id’, type: ‘int’},
    { name: ‘date’, type: ‘date’, dateFormat: ‘c’ },
    { name: ‘title’, type: ‘string’},
    { name: ‘complete’, type: ‘boolean’ },
    { name: ‘narrative’, type: ‘string’}
    ],
    validations: [
    { type: ‘presence’, field: ‘id’ },
    { type: ‘presence’, field: ‘title’, message: ‘Please enter a title for this note.’ }
    ]
    });

    Here is my noteEditor

    //Create the Sencha Touch Form Panel
    NotesApp.views.noteEditor = new Ext.form.FormPanel({
    id: ‘noteEditor’,
    items: [
    {
    xtype: ‘textfield’,
    name: ‘title’,
    label: ‘Title’,
    required: true
    },
    {
    xtype: ‘textareafield’,
    name: ‘narrative’,
    label: ‘Narrative’
    },
    {
    xtype: ‘checkboxfield’,
    name: ‘complete’,
    label: ‘Complete’
    }

    ],
    dockedItems: [
    NotesApp.views.noteEditorTopToolbar,
    NotesApp.views.noteEditorBottomToolbar
    ]
    });

    Here is my noteListToolbar

    //Create the Sencha toolbar
    NotesApp.views.notesListToolbar = new Ext.Toolbar({
    id: ‘notesListToolbar’,
    title: ‘My Notes’,
    layout: ‘hbox’,
    items: [
    { xtype: ‘spacer’},
    {
    id: ‘newNoteButton’,
    text: ‘New’,
    ui: ‘action’,
    handler: function () {

    var now = new Date();
    var noteID = now.getTime();
    var note = Ext.ModelMgr.create(
    { id: noteID, date: now, title: ”, narrative: ”, complete: ”},
    ‘Note’
    );

    NotesApp.views.noteEditor.load(note);
    NotesApp.views.viewport.setActiveItem(‘noteEditor’, {type: ‘slide’, direction: ‘left’});
    }
    }
    ]
    });

      • Jeffyt says

        Sorry…left that out…(thanks for the quick response)

        NotesApp.views.noteEditorTopToolbar = new Ext.Toolbar({
        title: ‘Edit Note’,
        items: [
        {
        text: ‘Home’,
        ui: ‘back’,
        handler: function () {
        NotesApp.views.viewport.setActiveItem(‘notesListContainer’, { type: ‘slide’, direction: ‘right’ });
        }
        },
        { xtype: ‘spacer’ },
        {
        text: ‘Save’,
        ui: ‘action’,
        handler: function () {

        var noteEditor = NotesApp.views.noteEditor;

        var currentNote = noteEditor.getRecord();
        // Update the note with the values in the form fields.
        noteEditor.updateRecord(currentNote);

        var errors = currentNote.validate();
        if (!errors.isValid()) {
        currentNote.reject();
        Ext.Msg.alert(‘Wait!’, errors.getByField(‘title’)[0].message, Ext.emptyFn);
        return;
        }

        var notesList = NotesApp.views.notesList;
        var notesStore = notesList.getStore();

        if (notesStore.findRecord(‘id’, currentNote.data.id) === null) {
        notesStore.add(currentNote);
        } else {
        currentNote.setDirty();
        }

        notesStore.sync();
        notesStore.sort([{ property: ‘date’, direction: ‘DESC’}]);

        notesList.refresh();

        NotesApp.views.viewport.setActiveItem(‘notesListContainer’, { type: ‘slide’, direction: ‘right’ });

        }
        }
        ]
        });

  32. Ian says

    i just started checking out sencha touch and this tutorial has been a very great jumpstart for me. very smooth & well-explained. kudos!

    “Stay tuned. I will be talking about server-side storage and synchronization in the coming weeks.”

    Please let me know if you’ve made a php-mysql-sencha app tutorial or anything similar. thanks!!

  33. says

    Hi Jorge,
    Thanks for this useful guides. I’ve implemented you sample codes. Unfortunately I have 2 problems:
    1. I cannot save a note that I edit.
    2. I cannot delete a note.

    On Save button’s handler, I put some console.log, like this:

    var notesList = NotesApp.views.notesList;
    var notesStore = notesList.getStore();

    if (notesStore.findRecord(‘id’, currentNote.data.id) === null) {
    notesStore.add(currentNote);
    console.log(‘Notes store add current note’);
    } else {
    currentNote.setDirty();
    console.log(‘Make current note dirty’);
    }

    notesStore.sync();
    console.log(‘Notes store synched’);
    notesStore.sort([{property: ‘date’, direction: ‘DESC’}]);

    notesList.refresh();
    console.log(‘List refreshed’);

    NotesApp.views.viewport.setActiveItem(‘notesListContainer’, {
    type: ‘slide’, direction: ‘right’
    });

    I got every log is passed but the list item is not update after I edit a row from the list.

    The same case happened when I tried to delete a content. Here is my code in trash button:

    var currentNote = NotesApp.views.noteEditor.getRecord();
    var notesList = NotesApp.views.notesList;
    var notesStore = notesList.getStore();

    if (notesStore.findRecord(‘id’, currentNote.data.id)) {
    console.log(‘Deleting current note.’);
    notesStore.remove(currentNote);
    }

    console.log(‘Syncing.’);
    notesStore.sync();

    console.log(‘Refresh list with .’);
    notesList.refresh();

    console.log(‘Move to list.’);
    NotesApp.views.viewport.setActiveItem(‘notesListContainer’, {
    type: ‘slide’, direction: ‘right’
    });

    Every log seem so normal. I don’t think notesStore.sync() works like I expected.

    Please share your experince of what migh be wrong in my code. Thanks in advance.

    • says

      Amri, I cannot reproduce the issue you described. what is the Sencha Touch version are you using? Also, can you send me your source code via email?

  34. Sam says

    For a simulator, Can I use Safari? The code for the first part of the app loads, but none of the subsequent ones do, not even after downloading the final app and running… Should this not working in Safari?

  35. alexis says

    i have a problem. i can navigate to the new page ( noteEditor ) but if i try to go back it won’t work. it has the same code.

    //This one works
    App.views.mainTopbar = new Ext.Toolbar({
    id: ‘mainTopbar’,
    dock:’top’,
    title: ‘Mobile Todo’,
    layout: ‘hbox’,
    items: [{
    xtype:’spacer’
    },{
    xtype:’button’,
    text:’New’,
    handler:function(){
    App.views.viewport.setActiveItem(‘addTodoContainer’,{type:’slide’,direction:’right’});
    }
    }]
    });//end of mainTopbar

    //This one doesn’t
    App.views.addTopbar = new Ext.Toolbar({
    id: ‘addTopbar’,
    dock:’top’,
    title: ‘Add Todo’,
    layout: ‘hbox’,
    items: [{
    ui:’back’,
    text:’Back’,
    handler:function(){
    Ext.Msg.alert(“INFO”,”No error here”);
    App.views.viewport.setActiveItem(‘mainTodoContainer’,{type:’slide’,direction:’left’});

    }
    }]
    });//end of mainTopbar

  36. Roby says

    When I save one note I noticed that in notesStore I can’t read the last notes id (but something like “ext-record-1″), when I refresh the page I can read the id . . . I don’t know why . . .

    I use this: console.log(notesStore.data.keys);
    the result is (:
    [idNote1, idNote2, …, “ext-record-n”]
    when I refresh the page with the same command I obtain [idNote1, idNote2, …, idNoten]

  37. Justin says

    Huge thanks for putting this tutorial together and for keeping up with the comments.

    I’m working on implementing some geolocation information to each note, which I’ve done successfully upon creating the note. However, I cannot seem to figure out how to update the latitude and longitude fields I’ve setup in my model when the Note is updated.

    I have a similar question about updating the date field upon edits as well.

    I assume these pieces would need to be in the save function, but in trying to use the var.set() method I can’t seem to reach the datastore.

    Any ideas?

    Sorry if that’s awkwardly written, still trying to get a hold of the language, so to speak.

  38. Marian says

    Hi Jorge,

    since Sencha Touch 2 Beta 3 is ready, and API is frozen, can we expect a ST2 version of your tutorial soon?

    Btw, wonderful job on this tutorial, it helped me a lot.

  39. says

    Hi ! Can you help me ?

    How can I do List paging in sencha touch mvc ? .for example there are 36 records and I want to list ten items each page on screen. Help .Is it possible to do with Ext.plugins.ListPagingPlugin?

  40. luiski says

    The tutorial was really helpful.

    There’s just one problem when i run the application. When i remove a note and refresh my browser the notes app crashes. The error i get is ” Cannot read property ‘id’ of null”. This happens inside the Sencha Touch debug file, not the notes script itself. My guess is that something goes wrong with the local storage.

    Has anyone else had this problem and if so, how did you solve it? I’ve encountered this problem within my own version of the app as have i in the version that Jorge has put at the end of the tutorial.

    Thank you!

  41. M.Suresh Kumar says

    hi jorge
    Thanks fa dis tutorial..i’m a beginner for sencha touch,so i learned d basic things from dis tutorial..i need more tutorials from u…

  42. Hans-Jacob says

    This tutorials looks great even though I have just skimmed them briefly. I’ve been looking in to using Sencha Touch 2 myself and have hit a brick wall regarding the enclosed Tools which they use extensively in their ‘Getting Started’-video. Even though the work fine in a Mac OS X environment, I need to make it work in a Windows environment and failed miserably.

    What is your take on the necessity and relevance of these tools?

    Is it possible to make a production grade web app without these tools?

    • says

      These tools help, but they are not mandatory. You can create production ready apps without them. In fact, I think you should first learn how to create the apps without the tools.

  43. WQ says

    I downloaded the sample and tried to run it all i got was My Notes and New… sorry for novice question, but please advice

  44. saikiranmai says

    Hi ya,
    I am very new to this framework ….Currently I am developing a mobile application with JQuery mobile ..is there any way that I can connect to sql server database on the server.(I want to get results from the sql server database tables from the server to the mobile application) ..

    Thanks in Advance…
    Kiran

    • says

      Sure. You only need to create a server-side handler that will either send json-encoded data to the app, or generate the app’s pages on the fly.

  45. tamer says

    Please i use this app in android 2.1 and works well
    but when i use it in android 4.0.3 doesn’t work please why ???

  46. chella says

    Great tutorial. I m working on Sencha touch for sometime now. Your tutorials have been of great help.
    Am in need of generating a pdf of the Form inputs for my application. How do I do it in Sencha touch?
    Please help.
    Thanks in advance. Keep blogging.

    • says

      I’m not aware of any client-side pdf generators. You should find out if there are any out there. If there are, you can probably use them inside your Sencha Touch app.

      What I have used is server-side pdf generators. If you choose this approach, your Sencha Touch app will need to send the values of the form fields to the server. The server will respond by generating the pdf and sending it to the app.

Leave a Comment

Your email address will not be published. Required fields are marked *