GithubHelp home page GithubHelp logo

hyf-javascript3's People

Contributors

waelalnadeem avatar

Watchers

 avatar

hyf-javascript3's Issues

Feedback on week 1 revised homework.

Hi Wael,

Here is my feedback on your revised week 1 homework.

1. It initially looked like you solved the layout problem of the contributions section. But on my 19:6 monitor the pictures are a bit stretched out horizontally. If I try to fix it by setting the width in CSS to 60 px the layout problem comes back again.

2. If I click on the repo name link to go to the repo GitHub web page, I get the message "Your file was not found". Can you fix that one?

3. In your render() function you are still creating elements using document.createElement(), etc. But in main() you are using createAndAppend(). I think you need to be consistent. I would go for the shorter code by using createAndAppend() throughout all of your code.

4. You are not handling errors for the fetchJSON() call in your render() function.

I think you covered all other issues. Good work!

I will do week2 later, when there are some more student submissions.

Feedback homework week 3

Hi Wael, here is my feedback on your week 3 homework (OOP and ES6 classes folder).

When I load up your index.html file everything seems to be in working order. You app is responsive, although the aspect ratio of the avatars is either stretched out horizontally (on a wide-screen) or squeezed (on a mobile phone).

1. Your sortList() function looks like a straight copy from Isaac's earlier work. We expect you to deliver your own, original work. If you and Isaac worked together on this function, please mention this in your code. Otherwise people can mistake you for a cheater.

This is the feedback I gave to Isaac on this sortList function. With some modifications you can apply it to your own code:

In your manipulateSelect() function you create a <select> element and add <option> elements in the order of the repository data returned by GitHub. Then, you use your sortList() function to correct the text and value properties of each <option> element to make them appear in alphabetically sorted order. This seems rather convoluted. Why not sort the repo array before creating <option> statements in the first place, like shown here.

function manipulateSelect(repos) {

  repos.sort((a, b) => a.name.localeCompare(b.name)); // <== added

  const select = createAndAppend('select', document.getElementById('header'));

  // ...

    repos.forEach((repo, i) => {
    createAndAppend('option', select, {
      html: repos[i].name,
      value: i
    });
  });

  // sortList(select); // <== removed

  // ...
}

So instead of sorting afterwards (and correcting what was the wrong order initially), do it directly on the data as you receive it (without creating something in the wrong order first and then correcting it). You only need a single line for the sort (but of course you need to know how you can use .sort() in combination with the localeCompare() string method).

You can now completely get rid of your sortList function.

2. The initialize() method of your View class does not handle errors from fetchJSON(). If I change in the APIUrl orgs to orgsx I get this error message in the console:

app.js:135 Uncaught (in promise) Error: Not Found
    at XMLHttpRequest.req.onreadystatechange (app.js:135)

You need to add a try/catch block in initialize() and render the error to the web page.

3. The following code snippet from the initialize() method contains an unnecessary intermediate variable:

let repos;
const data = await this.fetchJSON(APIUrl);
repos = data;
repos.forEach((repo, index) => {
  createAndAppend('option', select, { html: repo.name, value: index });
});

You can define and assign repos in one line:

const repos = await this.fetchJSON(APIUrl);
repos.forEach((repo, index) => {
  createAndAppend('option', select, { html: repo.name, value: index });
});

4. In the assignment, it was suggested that you create a method fetchContributors() in the Repository class. Because the Repository class has all the information needed (in your case in this._infoData) to manage itself completely, it can fetch the contributors without an external caller needing to tell it what URL to use. So this fetchContributors() method could look like this:

fetchContributors() {
  return fetchJSON(this._infoData.contributors_url);
}

However, to make this work you would need to move the fetchJSON() function out of the View class and make it a stand-alone function again, similar to createAndAppend().

With this fetchContributors() method in the Repository class your render() method of the View class could look like this:

async render(repoData) {
  try {
    // ...
    const repository = new Repository(repoData);
    const data = await repository.fetchContributors();
    const contributors = new Contributor(data);
    repository.render(details);
    contributors.render(contributor);
  }
  catch (err) {
    root.innerHTML = err.message;
  }
}

Now, your View class can remain completely unaware about the internal details of the repository information (that's a good thing).

BTW: avoid the use of alert(). It is too much "in your face" for most users. Rendering the error in the web page is good enough, although you want to style it as an error (e.g. red text or something like that.)

5. There is a problem with your use of window.onload. The way you coded it does not actually cause your code to be executed when the page is fully loaded. Instead it is executed immediately when the JavaScript engine loads the script file, which may be too early. If I move the <script src="app.js"> tag from <body> to <header> (which is a valid thing to do), you app no longer works. I get this error in the console:

Uncaught (in promise) TypeError: Cannot read property 'appendChild' of null
    at createAndAppend (app.js:112)
    at View.mainPage (app.js:54)
    at new View (app.js:47)
    at app.js:149

The window.onload property must be assigned a function. That function will be called when the page is completely loaded. For instance, you could do this (using an arrow function):

window.onload = () => new View();

6. Please pay attention on where you use blank lines. Every blank line that you use should be there for a reason. Use a single blank line between function and class definitions. Use a single blank line between blocks of related code where needed to improve readability. Remove all blank lines that do not fall into one of these categories.

Bottom line: I think you have a reasonable grasp of the concepts we covered in JS3. But please don't make teachers doubt about your abilities by copying code from others without an explanation.

Feedback homework week 1

Hi Wael, here is my feedback on your week 1 homework. This is only about week 1. I will review week 2 homework probably the Tuesday after the third lecture.

Despite the hard time that you said you had to get to this result, overall, from looking at the web page, your application seems to work fine. However, the code structure can be improved.

1. There is a slight issue with the contributors in the right-hand column when using a high-resolution monitor. On my 23 inch 1600x900 monitor the positioning of elements goes bananas.

contributors

If I look at the HTML elements you generate for the contributors, it looks like this:

<div class="contributor">
  <h2 class="contributionsHead">contributions</h2>
  <img src="https://avatars3.githubusercontent.com/u/3985124?v=4" class="Img">
  <p class="contribution">24</p>
  <h3 class="login">isalga</h3>
  <img src="https://avatars0.githubusercontent.com/u/2788771?v=4" class="Img">
  <p class="contribution">2</p>
  <h3 class="login">remarcmij</h3>
</div>

It would be better is you created a separate <div> for each contributor so that all related elements for a contributor stays together.

2. Your repo is missing an .eslintrc file. Therefore you are missing out on the useful help and warnings that ESLint can give you. Please create an .eslintrc file in the root of your hyf-javascript3 folder and paste in the contents that I posted in slack during class.

Having said that, when I add the mentioned .eslintrc file to your repo, I don't get any warnings from ESLint. It looks like you wrote your code completely according to the rules: preferring const over let, no unused variables, no missing semicolons, etc. Or did you perhaps have a .eslintrc file that you did not add to you repo? Anyway, well done on this point.

3. You have a lot of code and variables at the "global" level, i.e. outside of any function. This is probably why you had difficulties overseeing your own code. As you may remember, during class I tried to refactor your code and gave up because it was too difficult to do in a short time.

It will become much easier if you let each function be complete responsible for its own task. First, you could place all code that has to do with initialization and starting up in a single function. I usually name such a function main(). Looking at your code I would create that function as follows:

function main() {
  document.getElementById("root");

  const header = document.createElement('div');
  header.className = "header";
  root.appendChild(header);

  const head = document.createElement('h2');
  head.className = "head";
  head.innerHTML = "HYF Repositories";
  header.appendChild(head);

  const select = document.createElement('select');
  header.appendChild(select);
  select.className = "select";

  const main = document.createElement('div');
  main.className = "main";
  root.appendChild(main);

  const details = document.createElement('div');
  details.className = "details";
  main.appendChild(details);

  const contributor = document.createElement('div');
  contributor.className = "contributor";
  main.appendChild(contributor);

  let repos;
  fetchJSON(APIUrl, function (error, data) {
    repos = data;
    repos.forEach((repo, index) => {
      const option = document.createElement('option');
      option.innerHTML = repo.name;
      option.value = index;
      select.appendChild(option);
    });
    result(repos[0]);
  });

  select.addEventListener('change', () => {
    const index = select.value;
    details.innerHTML = '';
    contributor.innerHTML = '';
    result(repos[index]);
  });
}

Then, at the end of your file, outside of any function,add:

window.onload = main;

This causes your main() function to execute when the browser has completed loading the web page.

Now, let's look at main() in detail:

  1. I have placed all code that deals with creating and configuring an HTML element together, for clarity.
  2. You create an object called newData in which you store some information that you got back from the fetchJSON() call you used to get the initial repository information. But in fact, you can use that array directly to populate your <select> statement. The trick is to use the array index as the value attribute of each <option> element. Then, in your 'change' event handler, use that index to get the array element corresponding to your selection.
  3. Note that I changed event.target.value in simply select.value. In this case the event target causing the 'change' event is in fact the select element (we know that). So this is a bit more readable.
  4. Note further that the repository information that we get from the first call to fetchJSON() already contains all the information we need to display the repository detail (I overlooked that when I wrote the assignment). So there is no need to fetch that information again in your result() function.

Your result() function could now look like this:

function result(repoData) {
  const details = document.querySelector('.details');
  const contributor = document.querySelector('.contributor');

  const detailsText = document.createElement('p');
  // ... more code that works on the details

  // code moved from top of file. 
  const contributions = document.createElement('h2');
  contributions.className = 'contributionsHead';
  contributor.appendChild(contributions);
  contributions.innerHTML = "contributions";

  fetchJSON(repoData.contributors_url, function (error, data) {
    // ...
  }
}

There is no need any more to do two fetchJSON() calls in your result() function. The repoData argument already has all the information required. So you can remove the "outer" fetchJSON().

By the way, I would rename your function result() to render(). The name result is a bit meaningless.

4. You can further simplify your code by using the (enhanced) createAndAppend() utility function that I posted in slack:

function createAndAppend(name, parent, options = {}) {
  const elem = document.createElement(name);
  parent.appendChild(elem);
  Object.keys(options).forEach(key => {
    const value = options[key];
    if (key === 'html') {
      elem.innerHTML = value;
    } else {
      elem.setAttribute(key, value);
    }
  });
  return elem;
}

Using this createAndAppend() your main() function could now look like this:

function main() {
  document.getElementById("root");

  const header = createAndAppend('div', root, { class: 'header' });
  createAndAppend('h2', header, { class: 'head', html: 'HYF Repositories' });
  const select = createAndAppend('select', header, { class: 'select' });
  const main = createAndAppend('div', root, { class: 'main' });
  const details = createAndAppend('div', main, { class: 'details' });
  const contributor = createAndAppend('div', main, { class: 'contributor' });

  let repos;
  fetchJSON(APIUrl, function (error, data) {
    repos = data;
    repos.forEach((repo, index) => {
      createAndAppend('option', select, { html: repo.name, value: index });
    });
    result(repos[0]);
  });

  select.addEventListener('change', () => {
    const index = select.value;
    details.innerHTML = '';
    contributor.innerHTML = '';
    result(repos[index]);
  });
}

In my view, this makes your function looks nice and concise (less is more), and removes a lot of repetition (DRY principle: Don't Repeat Yourself).

Of course, you can also use createAndAppend() in your result() function.

5. You are not handling any errors returned from fetchJSON(). What if a network error occurs?

Feedback homework week 2

Hi Wael,

Here is my feedback on your week 2 homework.

Overall, your app works fine and the code looks good. You correctly converted the callbacks to promises.

There is still the issue with the avatar picture being stretched out on a wide-screen monitor, perhaps you can look into that again.

My comments below are about how you can further improve your already working code by making it more concise and better maintainable (the shorter the code, the less chance for errors).

1. You can improve your main() function by moving the addEventListener() call to inside the .then() method, like this:

function main() {
  document.getElementById('root');
  const header = createAndAppend('div', root, { class: 'header' });
  createAndAppend('h2', header, { class: 'head', html: 'HYF Repositories' });
  const select = createAndAppend('select', header, { class: 'select' });
  const main = createAndAppend('div', root, { class: 'main' });
  createAndAppend('div', main, { class: 'details' });
  createAndAppend('div', main, { class: 'contributor' });

  fetchJSON(APIUrl)
    .then(repos => {
      repos.forEach((repo, index) => {
        createAndAppend('option', select, { html: repo.name, value: index });
      });
      select.addEventListener('change', () => {
        const index = select.value;
        result(repos[index]);
      });
      result(repos[0]);
    })
    .catch(err => {
      root.innerHTML = err.message;
    });
}

There is no need for a second .then(), you can just call result(repos[0]) directly at the end of the first .then(). This also makes it unnecessary to introduce a variable repos outside of the .then() method as you had in your code.

2. In your result() function, you have a reference to repoData.gitHubAdress. However, there is no gitHubAddress property on the repoData object. If I click the link on your web page I get an error message (did you try that yourself?). The correct property to use is html_url.

3. You have used the createAndAppend() function in main() but your result() function still uses the tedious, manual method with createElement(), appendChild(), etc. I think you need to be consistent and use createAndAppend() throughout all of your code.

4. If you are using a for loop to go through array elements, try and use a for...of loop instead of a traditional (old-fashioned) loop with an numerical index value. Together with the previous point 3 above, your code can be cleaned up quite a bit, making it easier to understand and maintain:

function result(repoData) {
  const detailsContainer = document.querySelector('.details');
  const contributorsContainer = document.querySelector('.contributor');

  createAndAppend('h2', contributorsContainer, { html: 'contributions' });

  createAndAppend('p', detailsContainer, {
    html: 'name : <a href = "' + repoData.html_url + '">  ' + repoData.name + ' </a><br>' + 'description : ' + repoData.description + '<br>' + 'forks : ' + repoData.forks + '<br>' + 'updated :  ' + repoData.updated_at,
    class: 'detailsText'
  });

  fetchJSON(repoData.contributors_url)
    .then(contributors => {
      for (const contributor of contributors) {
        createAndAppend('img', contributorsContainer, {
          src: contributor.avatar_url,
          class: 'Img'
        });
        createAndAppend('p', contributorsContainer, {
          class: 'contribution',
          html: contributor.contributions,
        });
        createAndAppend('h3', contributorsContainer, {
          class: 'login',
          html: contributor.login
        });
      }
    })
    .catch(err => {
      root.innerHTML = err.message;
    });
}

5. When there is a network error, the only thing your web page displays is 'Not found', on an otherwise empty page. I would suggest that you at least display the header and the (potentially empty) select box. Perhaps the status code (400 something) and add some text, like 'Network error'.

6. Be consistent in your use of quotes as string delimiters. Either use single quotes (my preference) or double quotes throughout all of your code.

Finally, most JavaScript developers now prefer two spaces instead of four to indent their code. If you would like to try that, in VSCode you can do that by adding these User Settings:

"editor.detectIndentation": false,
"editor.tabSize": 2,

If you now reformat your code in VSCode it will indent with two space.

In conclusion, you're doing well. Good code, minor improvements possible.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.