1

I'm learning JQM and Backbone.js and I have a few problems. Im making a recipe app following the TODO list example trying to blend them both.

Anyway I can't refresh any page besides the first one, I'm getting undefined variables. I believe it has to do with the DOM and many views I have. Secondly Upon entering the recipe to search for to query the API, it displays the results first as plain links!

If I got forward a page or back a page and return to the results page it display the links as they should be the JQM style. This is because I couldn't figure how or what to append to in the JS, so I did it in the HTML.

I know this is a long shot but can anyone give me guidance as to what the hell I'm doing wrong, general advice, anything?

    var Todo = Backbone.Model.extend({
    defaults: function() {
      return {
    id: 0,
    title: 'defaultname',
    imgUrl: 'defaultimageurl',
        order: searchTemp.nextOrder(),
        rating: 0,
        timeToMake: '',
        salty: 0,
        sour: 0,
        sweet: 0,
        bitter: 0,  
        isPerm: false,
    taggedForList: false
      };
    },      
    initialize: function(){
      if( !this.get('ingrs') ){ 
        this.set({ingrs: new Array()});
      }
    },
    saveModel: function() {
    this.set({isPerm: true});
    this.save();
    }
});

var TodoList = Backbone.Collection.extend({
  model: Todo,
  localStorage: new Backbone.LocalStorage("searchTemp"),
  initialize: function() {
  },
  nextOrder: function() {
    if (!this.length) return 1;
    return this.last().get('order') + 1;
  },
  comparator: 'order',
  taggedForList: function() {
      return this.where({taggedForList: true});
  },
  remaining: function() {
      return this.without.apply(this, this.taggedForList);
  },

  findRecipes: function(theQuery) {
    console.log("findRecipes called");
    searchTemp.each(function (model) {
    if (!model.isPerm) {
        model.destroy();
    }
    });
    $.ajax({
      url: 'http://api.yummly.com/v1/api/recipes?_app_id=d8087d51&_app_key=005af5a16f1a8abf63660c2c784ab65f&maxResult=5&q='+theQuery,
      dataType: 'jsonp',
      success: function(apiStuff){
    var result = new Array();     
    result = apiStuff;          //saves the API's response as a new array
        result = result.matches;    //trims extra information from the json object, now only has information on the various recipes

    $.each(result, function(i, item) {
      var anotherRecipe= new Todo();    // makes a new model for each result

      anotherRecipe.set({
        id: result[i].id,            //then sets the attributes
        title: result[i].recipeName,    
        ingrs: result[i].ingredients,
        imgUrl: result[i].smallImageUrls,
            rating: result[i].rating,
            timeToMake: result[i].totalTimeInSeconds,
          });
          //not all recipes support flavor ratings, so error catching must be used to avoid setting null values
          try { anotherRecipe.set({ salty : result[i].flavors.salty }); } catch(e) {anotherRecipe.set({salty : "?"});}  //maybe replace the error condition to setting the flavor to '?'
          try { anotherRecipe.set({ sour: result[i].flavors.sour }); } catch(e) {anotherRecipe.set({sour : "?"});}
          try { anotherRecipe.set({ sweet: result[i].flavors.sweet }); } catch(e) {anotherRecipe.set({sweet : "?"});}
          try { anotherRecipe.set({ bitter: result[i].flavors.bitter }); } catch(e) {anotherRecipe.set({bitter : "?"});}

      searchTemp.add(anotherRecipe);    //adds the model to the temporary     
    });
      }  //eventually, should add something that checks for an empty search result, appending some warning if that happens
    });
    // console.log("search done");
  }  
});


var ShopItem = Backbone.Model.extend({
    defaults: function() {
        return {
            ingr : 'ingredient',
            done : false
        }
    },
    toggle: function() {
      this.save({taggedForList: !this.get("taggedForList")}); 
    }
});

var ShopList = Backbone.Collection.extend({
    localStorage: new Backbone.LocalStorage("grocery-list"),
    generate: function() {
    console.log("SHOP LIST!  ASSSSSEMMMMBLLLLLLE!");
    searchTemp.fetch();
    var ingrList = searchTemp.pluck('ingrs');  //this returns an array of arrays
    console.log(ingrList);
    ingrList = _.union(ingrList);   //this needs to get a series of arrays ( _.union(array1, array 2); )
    console.log(ingrList);
    },

    getList: function() {
        var list = new Array();
        list = this.toJSON();
        return list;
    }
});

var Todos = new TodoList;   //I am afraid to move this, 95% sure its obsolete, though
/*
var savedRecipesView = Backbone.View.extend({
    tagName:  "li",
    initialize: function() {
        this.render();
        this.listenTo(this.model, 'change', this.render);
        this.listenTo(this.model, 'destroy', this.remove);
    },
    render: function() {
        var template = _.template( $("#list_item").html(), {} );
        this.$el.html( template );

        //this.$el.html(this.template(this.model.toJSON()));
        //this.$el.toggleClass('done', this.model.get('done'));
        //this.input = this.$('.edit');
        //return this;

    },
    events: {
        "click input[type=button]": "sendToGroceries"
    },
    sendToGroceries: function() {
        var temp = new Array();
        temp = this.toJSON();
        $.each(temp, function(i, item) {
            var shopItem = new ShopItem();    
            shopItem.set({ name: temp[i].title });
            shoppingList.add(shopItem); //use pluck [ingrs]
            shopItem.save();
        });    
    }
});
*/
window.HomeView = Backbone.View.extend({
    template:_.template($('#home').html()),

    render:function (eventName) {
        $(this.el).html(this.template());
        return this;
    }
});

window.newSearchView = Backbone.View.extend({
    template:_.template($('#newSearch').html()),
    //this VAGUELY works, but causes visual chaos the first run through,
    //still relies on the appending for that
    initialize: function() {
        console.log(searchTemp);
        //searchTemp.bind('searchDone', this.render, this);
        searchTemp.bind('add', this.render, this);
    },
    render:function (eventName) {

        var temp = new Array();  // I think this line isnt doing anyting
        results = searchTemp.toJSON();
        // console.log(results);
        var variables = {
            recipes: results
        };
        $(this.el).html(this.template());
        return this;
    },
    events: {
      "keypress #recipe-search":  "searchOnEnter",
      //add a listener to newSearch to change what's displaye don this list
    },
    searchOnEnter: function(e) {   //the search bar's functionality
      if (e.keyCode != 13) return;
      var searchin = $("input[id='recipe-search']").val();

      console.log("searched for - "+ searchin);
      //this function is in todoList, does an API call and
      //adds a new model for each result (there will almost always be 5 results)
      searchTemp.findRecipes(searchin);

    }
});

window.newListView = Backbone.View.extend({
    template : _.template($('#newList').html()),
    initialize: function() {
    },
    render:function (eventName) {
        recipe = this.model.toJSON(); ///INCOMPLETE, modify newlist to accept straight from JSON
    var variables = {
            recipe_name : this.model.get("title"),
            img_url : this.model.get("imgUrl"),
            timetomake: this.model.get("timeToMake"),
            ingrs : this.model.get("ingrs"),
            rating : this.model.get("rating"),
            salty : this.model.get("salty"),
            sour : this.model.get("sour"),
            sweet : this.model.get("sweet"),
            bitter : this.model.get("bitter")
        };
        $(this.el).html(this.template(variables));
        return this;
    },
    events: {
      "click #save-this":  "saveModel"
    },
    saveModel: function() {
    console.log("saveModel() called");
    //console.log(permStorage.taggedForList());
        //shift the model over to permStorage
        //searchTemp.remove(this.model);
        //console.log(this.model);
        this.model.saveModel();
    //console.log(this.model)
        //now save permStorage to local storage
        searchTemp.each(function (model) {
        if(model.isPerm) {
        model.save();
        }
    });
    }
});

window.savedRecipesView = Backbone.View.extend({
    template:_.template($('#savedRecipes').html()),
    initialize: function() {
    console.log("about to fetch from local storage...");
    searchTemp.fetch();
    console.log("...fetched!");
    },
    render:function (data) {    
        results = searchTemp.toJSON();
    //results = results.models;
    //console.log(results);
        var variables = {
            results: results
        };
    _.each(data, function(task) {
            console.log("meow");
            this.addOne(task);
        }, this);
    $(this.el).html(this.template(variables));
        return this;
    },
    addOne: function(task) {
        var view = new listItemView({ model:task });
        $(this.el).append( view.render().el );
    }
});

window.listItemView = Backbone.View.extend({
    tagName: 'li',
    template:_.template($('#list-item').html()),
    initialize: function() {
        _.bindAll(this, 'render');
        this.model.bind('change', this.render);
        this.model.view = this;
    },
    events: {
        "click input[type=button]" : "onClick"
    },
    render: function() {
        $(this.el).html(this.template(this.model.toJSON()));
        this.setContent();
        return this;
    },
    onClick: function(){
        searchTemp.add(this.model);
        console.log("model added to searchTemp, current state of searchTemp:");
        console.log(searchTemp);
    }
});

window.oldListView = Backbone.View.extend({
    template:_.template($('#oldList').html()), 
    render: function (eventName) {
        $(this.el).html(this.template());
        return this;
    }
});    

window.deleteOldView =  Backbone.View.extend({
    template:_.template($('#deleteOld').html()), 
    render: function (eventName) {
        $(this.el).html(this.template());
        return this;
    }
});

window.shoppingListView = Backbone.View.extend({
    template:_.template($('#shoppingList').html()),
    initialize: function() {
    shopList.generate();
    },

    render: function (eventName) {
    var variables = {


    };
    $(this.el).html(this.template(variables));
    return this;
    }

});

var AppRouter = Backbone.Router.extend({
    routes:{
        "":"home",
        "newSearch":"newSearch",
        "newList/:id":"newList",
        "savedRecipes":"savedRecipes",
        "oldList":"oldList",
    "deleteOld":"deleteOld",
    "shoppingList":"shoppingList"
    },
    initialize:function () {
        // Handle back button throughout the application
        $('.back').live('click', function(event) {
            window.history.back();
            return false;
        });
        this.firstPage = true;
    },
    home:function () {
        this.changePage(new HomeView());
    },
    newSearch:function () {
        this.changePage(new newSearchView());
    },
    newList:function (theID) {
        var tempModel = searchTemp.get(theID);
        this.changePage(new newListView({
            model: tempModel,
            id: theID
        }));
        //console.log(permStorage.taggedForList());
    },
    savedRecipes:function () {  
        this.changePage(new savedRecipesView());      
    },
    oldList:function () {
        this.changePage(new oldListView());
    },
    deleteOld:function () {
        this.changePage(new deleteOldView());
    },
    shoppingList:function () {
    this.changePage(new shoppingListView());
    },
    changePage:function (page) {
        $(page.el).attr('data-role', 'page');
        page.render();
        $('body').append($(page.el));
        var transition = $.mobile.defaultPageTransition;
        // We don't want to slide the first page
        if (this.firstPage) {
            transition = 'none';
            this.firstPage = false;
        }
        $.mobile.changePage($(page.el), {changeHash:false, transition: transition});
    }

});

$(document).ready(function () {
    console.log('document ready');
    app = new AppRouter();
    Backbone.history.start();
    searchTemp = new TodoList(); //this stores searched recipes, rename to myRecipes
    shopList = new ShopList();
});




<!DOCTYPE html>
<html class="ui-mobile-rendering">
<head>
    <title>RECILIST</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="css/jquery.mobile-1.0.1.min.css"/>

    <!-- The Templates -->
    <script type="text/template" id="home">  
         <div data-role="header"  >
            <h1 style="color:black">Recilist Home</h1>
                </div>
                 <img src="store.jpg" id="vege">

        <div data-role="content" style="color:red">
            <!-- <h3>recilist home page</h3>
                 <p>Welcome to Recilist!</p>
                <p>This is the Home page. </p>
                -->
            <h1>Save Recipes & <br/>
            Create Shopping <br/>
             Lists Anywhere</h1>
            <p class="blurb">Create and manage your grocery shopping list, FIND and <br/>SAVE your favorite recipes from across the web,<br/> get great SAVINGS and share with your entire family - for FREEEE!!!!</p>


            <ul data-role="listview" id="choices" data-inset="true">
                <li><a style="color:red" href="#newSearch">Search for new Recipes</a></li>
                <li><a style="color:red" href="#savedRecipes">View saved Recipes</a></li>
            </ul>

        </div>

        <div data-role="footer" class="ui-bar" id ="footer">
            <h5 style="color:black"> powered by <img src="http://static.yummly.com/api-logo.png"> </h5>
        </div>
    </script>

    <script type="text/template" id="newSearch">  
        <div data-role="header">
            <h1 style="color:black">Search for a new Recipe</h1> 
        </div>

        <div data-role="content">
            <input name= "recipe-search" id="recipe-search" data-icon="search" type="text" placeholder="What do you want to cook?">
            <ul id="search-list" data-role="listview" data-inset="true">
              <% for(var i in results) { %>
                <li> <img src="<%= results[i].imgUrl %>"> <a href="#newList/<%= results[i].id%>"><%= results[i].title %> </a> </li>
              <% } %> 
            </ul>
        </div>
        <img src="list.png" id="list">
        <div data-role="footer" class="ui-bar">
            <a href="#" data-icon="back" class="back ui-btn-left">Back</a>
            <a href="#" data-icon="home">Home</a>
        </div>
    </script>

    <script type="text/template" id="newList">
        <div data-role="header">
            <h1 style="color:black"> <%=recipe_name%> </h1>
        </div>

        <div>
        <div data-role="content">
          <img src= <%=img_url%> >
          <h4>Recipe Rating: <%= rating %> </h4>
          <h4>Total time to Prepare: <%= timetomake %> </h4>
          <h4>Flavor Ratings</h4>
          <div class="ui-block-a"> <div class="ui-bar ui-bar-e"> <h4>saltiness</h4> <%= salty %> </div> </div>
          <div class="ui-block-b"> <div class="ui-bar ui-bar-e"> <h4>sourness</h4> <%= sour %> </div> </div>
          <div class="ui-block-c"> <div class="ui-bar ui-bar-e"> <h4>sweetness</h4> <%= sweet %> </div> </div>
          <div class="ui-block-d"> <div class="ui-bar ui-bar-e"> <h4>bitterness</h4> <%= bitter %> </div> </div>            
        </div>

        <div data-role="collapsible" data-collapsed="true">
          <h3>Ingredients</h3>
            <ul id="ingr-list" data-role="listview" data-inset="true">
              <% for(var i in ingrs) { %>
                <li><%= ingrs[i] %></li>
              <% } %>
            </ul>
        </div>


        <div data-role="content">
          <input type="button" id="save-this" data-icon="check" value="save to My Recipies">
          <div>
           <img src="store3.jpg" id="aisle">
        </div>

        <div data-role="footer" class="ui-bar">
            <a href="#" data-icon="back" class="back ui-btn-left">Back</a>
            <a href="#" data-icon="home">Home</a>
        </div>
    </script>

    <script type="text/template" id="savedRecipes">
        <div data-role="header">
            <h1>My Recipes</h1>
        </div>

        <div data-role="content">
            <p>list of saved recipes, retrieved from local storage</p>
            <p>Saved Recipes:</p>
            <ul id="search-list" data-role="listview" data-inset="true">
              <% for(var i in results) { %>
                <li> <img src="<%= results[i].imgUrl %>"> <a href="#newList/<%= results[i].id%>"><%= results[i].title %> </a> </li>
              <% } %> 
            </ul>
           <a href="#deleteOld" data-role="button">manage saved recipes</a>
           <a href="#shoppingList" data-role="button">generate shopping list</a>
        </div>

        <div data-role="footer" class="ui-bar">
            <a href="#" data-icon="back" class="back ui-btn-left">Back</a>
            <a href="#" data-icon="home">Home</a>
        </div>
    </script>

    <script type="text/template" id="deleteOld">
        <div data-role="header">
            <h1>Delete Recipes</h1>
        </div>

        <div data-role="content">
            <p>select which recipes you wish to delete from local storage</p>
            <p>recipes:</p>
            <p>(currently lacks functionality to populate this list)</p>
            <ul data-role="listview" data-inset="true" id="recipe-list">

            </ul>
            <input type="button" data-icon="delete" value="delete selected" />
        </div>

        <div data-role="footer" class="ui-bar">
            <a href="#" data-icon="back" class="back ui-btn-left">Back</a>
            <a href="#" data-icon="home">Home</a>
        </div>
    </script>


    <script type="text/template" id="oldList">
      <div data-role="header">
          <h1>---NAME OF THE RECIPE----</h1> 
      </div>

      <div data-role="content">
          <p>This is a list of all the ingredients in this recipe</p>
          <p>Ingredients:</p>
          <ul data-role="listview" data-inset="true">
              <li>ingredient 1</li>
              <li>ingredient 2</li>
              <li>ingredient 3</li>
              <li>ingredient 4</li>
          </ul>
          <p> (button to view the recipe) </p>
      </div>

      <div data-role="footer" class="ui-bar">
          <a href="#" data-icon="back" class="back ui-btn-left">Back</a>
          <a href="#" data-icon="home">Home</a>
      </div>
    </script>

    <script type="text/template" id="shoppingList">
      <div data-role="header">
          <h1>shopping list</h1> 
      </div>

      <div data-role="content">
        <ul id="search-list" data-role="listview" data-inset="true">
          <% for(var i in results) { %>
            <li> <input type="checkbox" name=i id=i class="custom" /></li>  <!-- these checkboxes are HIDEOUSLY DEFORMED-->
            <label for=i> <%= results[i].title %> </label>
          <% } %> 
        </ul>
      </div>

      <div data-role="footer" class="ui-bar">
          <a href="#" data-icon="back" class="back ui-btn-left">Back</a>
          <a href="#" data-icon="home">Home</a>
      </div>      
    </script>


    <script type="text/template" id="list-item">
      <li>cheese</li>
    </script>
  <!--      <li> <img src= <%=img_url%> > <a href="#newList/"+ <%=model_id%> +"' class='ui-link-inherit'>" + <%=model_title%> + "</a> </li> -->


    <!-- The Scripts -->
    <script src="lib/jquery-1.7.1.min.js"></script>
    <script src="js/jqm-config.js"></script>
    <script src="lib/jquery.mobile-1.0.1.min.js"></script>
    <script src="js/underscore.js"></script>
    <script src="lib/backbone-min.js"></script>    
    <script src="js/backbone.localStorage.js"</script>
    <script src="js/json2.js"></script>
    <script src="js/main.js"></script>
</head>

<body></body>

</html>
4

1 回答 1

1

要增强动态创建的列表视图,您需要使用以下内容刷新标记。

$('[data-role=listview]').listview('refresh');
于 2013-04-19T22:58:29.220 回答