-1

I've been following along with the tutorial, and i had been having some issues, but i was able to solve all of them on my own - but now I've come to this point. i ran the "meteor remove insecure" and i was pretty sure i updated my tasks.js correctly to reflect my meteor methods. i changed the import on my main.js and TaskForm.jsx and App.jsx

EDIT ** The error i am receiving does not show up in vsCode, the error only shows in the console. but, interestingly, if you look at my methods, you see the warning message is supposed to say "Not Authorized", however the warning that appears in the console says "Update failed: Access denied"

MOST of my variables are named exactly the same as in the tutorial, some are not... and that is probably adding a layer of confusion on top of the learning process... for example i have Task, Tasks, tasksList, and taskList, are all different variables... i am aware i should make those more legible, just trying to make it "work" for now.

tasks.js:

import { Mongo } from 'meteor/mongo';
import { check } from 'meteor/check';

export const Tasks = new Mongo.Collection('taskList');

Meteor.methods({
  'taskList.insert'(text) {
    check(text, String);

    if (!this.userId) {
      throw new Meteor.Error('Not authorized.');
    }

    Tasks.insert({
      text,
      createdAt: new Date,
      owner: this.userId,
      username: Meteor.users.findOne(this.userId).username
    })
  },

  'taskList.remove'(taskId) {
    check(taskId, String);

    if (!this.userId) {
      throw new Meteor.Error('Not authorized.');
    }

    Tasks.remove(taskId);
  },

  'taskList.setChecked'(taskId, isChecked) {
    check(taskId, String);
    check(isChecked, Boolean);

    if (!this.userId) {
      throw new Meteor.Error('Not authorized.');
    }

    Tasks.update(taskId, {
      $set: {
        isChecked
      }
    });
  }
}); 

App.jsx:

import React, { useState } from 'react';
import { useTracker } from 'meteor/react-meteor-data';
import _ from 'lodash';
import { Task } from './Task';
import { Tasks } from '/imports/api/tasks';
import { TaskForm } from './TaskForm';
import { LoginForm } from './LoginForm';

const toggleChecked = ({ _id, isChecked }) => {
  Tasks.update(_id, {
    $set: {
      isChecked: !isChecked
    }
  })
};

const deleteTask = ({ _id }) => Tasks.remove(_id);

const logoutFunction = (e) => {
  Meteor.logout(e)
}

export const App = () => {
  const filter = {};

  const [hideCompleted, setHideCompleted] = useState(false);

   if (hideCompleted) {
    _.set(filter, 'isChecked', false);
  }

  const { tasksList, incompleteTasksCount, user } = useTracker(() => ({
    tasksList: Tasks.find(filter, { sort: { createdAt: -1 } }).fetch(),
    incompleteTasksCount: Tasks.find({ isChecked: { $ne: true }}).count(),
    user: Meteor.user(),
  }));

  if (!user) {
    return (
      <div className="simple-todos-react">
        <LoginForm/>
      </div>
    );
  }

  return (
    <div className="simple-todos-react">
      <button onClick ={logoutFunction}>Log Out</button>
      <h1>Flight List ({ incompleteTasksCount })</h1>

      <div className="filters">
        <label>
          <input
              type="checkbox"
              readOnly
              checked={ Boolean(hideCompleted) }
              onClick={() => setHideCompleted(!hideCompleted)}
          />
          Hide Completed
        </label>
      </div>
 
      <ul className="tasks">
        { tasksList.map(task1 => <Task 
              key={ task1._id }
              task={ task1 }
              onCheckboxClick={toggleChecked}
              onDeleteClick={deleteTask}/>) }
      </ul>

      <TaskForm user={user}/>
    </div>
  );
};

TaskForm.jsx:

import React, { useState } from 'react';
import { Tasks } from '/imports/api/tasks';

export const TaskForm = ({ user }) => {
  const [text, setText] = useState("");

  const handleSubmit = () => {
    if (!text) return;

    Tasks.insert({
      text: text.trim(),
      createdAt: new Date(),
      isChecked: false,
      owner: user._id,
    });

    setText("");
  };

  return (
    <form className="task-form" onSubmit={handleSubmit}>
      <input
        type="text"
        placeholder="Type to add new tasks"
        value={text}
        onChange={(e) => setText(e.target.value)}
      />

      <button type="submit">Add Task</button>
    </form>
  );
};

main.js:

import { Meteor } from 'meteor/meteor';
import { Tasks } from '/imports/api/tasks';
function insertTask({ text }) {
  Tasks.insert({text});
}

Meteor.startup(() => {
    if (!Accounts.findUserByUsername('meteorite')) {
    Accounts.createUser({
      username: 'meteorite',
      password: 'password'
    });
  }
  
  if (Tasks.find().count() === 0) { //this is for basic data that will never render once app is live. 
    [
      {text:'updated THE Firstttt Task again this wont show'},
      {text:'the Second Task'},
      {text:'update 1 Third Task'},
      {text:'Fourth Task'},
      {text:'Fifth Task'},
      {text:'Sixth Task'},
      {text:'Seventh Task'}
    ].forEach(eachTask=>{insertTask(eachTask)})
  }

});

Task.jsx:

import React from 'react';
import classnames from 'classnames';

export const Task = ({ task, onCheckboxClick, onDeleteClick  }) => {
  const classes = classnames('task', {
    'checked': Boolean(task.isChecked)
  });
 
  return (
    <li className={classes}>
      <button onClick={ () => onDeleteClick(task) }>&times;</button>
      <span>{ task.text }</span>
      <input
        type="checkbox"
        checked={ Boolean(task.isChecked) }
        onClick={ () => onCheckboxClick(task) }
        readOnly
      />
    </li>
  );
};
  • Could it be because i do have not called allow anywhere? But where do i call it? – AlaskanProgrammer Jul 26 '20 at 17:10
  • OMG WOOOHOOO I FIGURED IT OUT! it was because i wasn't making the call correctly. The guide tells you to update the import frmo tasks.js, but it does not say to update Tasks.Remove to Meteor.Call('method',parameter) i got it on my own!!!!!!! – AlaskanProgrammer Jul 26 '20 at 17:18
  • Hi, could you please explain more, or can you share the code? I have the same issue – Duy Hoang Jul 30 '20 at 15:03

2 Answers2

0

I think i figured out SOME of it, but still having issues. These are my methods.

tasks.js:

    import { Mongo } from 'meteor/mongo';
import { check } from 'meteor/check';

// export default new Mongo.Collection('taskList');
export const Tasks = new Mongo.Collection('tasks');
 
Meteor.methods({
  'tasks.insert'(text) {
    check(text, String);
 
    if (!this.userId) {
      throw new Meteor.Error('Not authorized.');
    }
 
    Tasks.insert({
      text,
      createdAt: new Date,
      owner: this.userId,
      username: Meteor.users.findOne(this.userId).username
    })
  },
 
  'tasks.remove'(taskId) {
    check(taskId, String);
 
    if (!this.userId) {
      throw new Meteor.Error('Not authorized.');
    }
 
    Tasks.remove(taskId);
  },
 
  'tasks.setChecked'(taskId, isChecked) {
    check(taskId, String);
    check(isChecked, Boolean);
 
    if (!this.userId) {
      throw new Meteor.Error('Not authorized.');
    }
 
    Tasks.update(taskId, {
      $set: {
        isChecked
      }
    });
  }
});

those above are my methods. below are my calls to those methods. the ONLY ONE THAT WORKS is delete. any ideas why the others are wrong?

App.jsx:

import React, { useState } from 'react';
import { useTracker } from 'meteor/react-meteor-data';
import _ from 'lodash';
import { Task } from './Task';
import { Tasks } from '/imports/api/tasks';
import { TaskForm } from './TaskForm';
import { LoginForm } from './LoginForm';

const toggleChecked = ({ _id }) => Meteor.call('tasks.setChecked', _id)
const deleteTask = ({ _id }) => Meteor.call('tasks.remove',_id);

const logoutFunction = (e) => {
  Meteor.logout(e)
}

export const App = () => {
  const filter = {};

  const [hideCompleted, setHideCompleted] = useState(false);

   if (hideCompleted) {
    _.set(filter, 'isChecked', false);
  }

  const { tasksList, incompleteTasksCount, user } = useTracker(() => ({
    tasksList: Tasks.find(filter, { sort: { createdAt: -1 } }).fetch(),
    incompleteTasksCount: Tasks.find({ isChecked: { $ne: true }}).count(),
    user: Meteor.user(),
  }));

  if (!user) {
    return (
      <div className="simple-todos-react">
        <LoginForm/>
      </div>
    );
  }

  return (
    <div className="simple-todos-react">
      <button onClick ={logoutFunction}>Log Out</button>
      <h1>Flight List ({ incompleteTasksCount })</h1>

      <div className="filters">
        <label>
          <input
              type="checkbox"
              readOnly
              checked={ Boolean(hideCompleted) }
              onClick={() => setHideCompleted(!hideCompleted)}
          />
          Hide Completed
        </label>
      </div>
 
      <ul className="tasks">
        { tasksList.map(task1 => <Task 
              key={ task1._id }
              task={ task1 }
              onCheckboxClick={toggleChecked}
              onDeleteClick={deleteTask}/>) }
      </ul>

      <TaskForm user={user}/>
    </div>
  );
};

so again, function deleteTask works as expected. however, function toggleChecked gives me the following error:

errorClass {message: "Match error: Expected boolean, got undefined", path: "", sanitizedError: errorClass, errorType: "Match.Error", stack: "Error: Match error: Expected boolean, got undefine…ea528700c66dd42ddcc29ef7434e9e62b909dc14:3833:16)"}errorType: "Match.Error"message: "Match error: Expected boolean, got undefined"path: ""sanitizedError: errorClass {isClientSafe: true, error: 400, reason: "Match failed", details: undefined, message: "Match failed [400]", …}stack: "Error: Match error: Expected boolean, got undefined↵ at check (http://localhost:3000/packages/check.js?hash=75acf7c24e10e7b3e7b30bb8ecc775fd34319ce5:76:17)↵ at MethodInvocation.tasks.setChecked (http://localhost:3000/app/app.js?hash=7e0d6e119e929408da1c048d1448a91b43b1a759:55:5)↵ at http://localhost:3000/packages/ddp-client.js?hash=5333e09ab08c9651b0cc016f95813ab4ce075f37:976:25↵ at Meteor.EnvironmentVariable.EVp.withValue (http://localhost:3000/packages/meteor.js?hash=857dafb4b9dff17e29ed8498a22ea5b1a3d6b41d:1207:15)↵ at Connection.apply (http://localhost:3000/packages/ddp-client.js?hash=5333e09ab08c9651b0cc016f95813ab4ce075f37:967:60)↵ at Connection.call (http://localhost:3000/packages/ddp-client.js?hash=5333e09ab08c9651b0cc016f95813ab4ce075f37:869:17)↵ at toggleChecked (http://localhost:3000/app/app.js?hash=7e0d6e119e929408da1c048d1448a91b43b1a759:149:17)↵ at onClick (http://localhost:3000/app/app.js?hash=7e0d6e119e929408da1c048d1448a91b43b1a759:318:20)↵ at HTMLUnknownElement.callCallback (http://localhost:3000/packages/modules.js?hash=ea528700c66dd42ddcc29ef7434e9e62b909dc14:3784:14)↵ at Object.invokeGuardedCallbackDev (http://localhost:3000/packages/modules.js?hash=ea528700c66dd42ddcc29ef7434e9e62b909dc14:3833:16)"proto: Error

0

Completely answered.

Updated my TaskForm.jsx submit function to:

  const handleSubmit = () => {
    if (!text) return;
Meteor.call('tasks.insert',text)
  };

and updated my App.jsx to:

const toggleChecked = ({ _id, isChecked }) => Meteor.call('tasks.setChecked', _id, isChecked)
const deleteTask = ({ _id }) => Meteor.call('tasks.remove',_id);