【问题标题】:I am unable to get the chat functionality to work in my Meteor app我无法在 Meteor 应用程序中使用聊天功能
【发布时间】:2015-05-02 12:23:38
【问题描述】:

您好,我是 Meteor 的新手,正在努力学习和熟悉 Meteor 平台。

所以我正在构建一个 Meteor 应用程序 {https://github.com/ahadsheriff/SheriffMessenger} 这是基于我看到的另一个示例 {http://chatt.meteor.com/ (注意:原始应用程序是使用旧版本的流星构建的,并且当前版本(我的)中不需要某些包)我克隆示例应用程序的目的是对流星和应用程序本身进行试验,这个想法是(一旦我让它工作)分解代码并研究每个单独的元素以了解流星是如何工作的。

在我的克隆中,一切看起来都很好,除了两个主要功能在我的版本中不起作用:聊天/消息传递功能以及查看您正在与哪些用户进行通信以及查看他们是否在线的能力。

我已经安装了所有可能的软件包(最新版本的 Meteor 支持的软件包),我认为我的问题可能是缺少一些基本软件包。

无论如何,我想知道是否有人可以帮助我筛选代码并查看我的问题出在哪里,这样我就可以让应用程序正常工作并重新分解这段代码!

感谢任何帮助!

我已经发布了上面的 github 链接,为了您的方便,我将在下面发布 HTML 和 Javascript 代码。

谢谢您的帮助,我们将不胜感激!!!

HTML:

<template name="layout">
  <head>
    <title>Simple chat</title>
    <link href='http://fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'>
  </head>

    <!-- Wrap all page content here -->
    {{> yield}}

</template>

<template name="channel">
    <div class="container">
      <div class="row">
        <div class="col-md-3">
          {{> presentation}}
        </div>
        <div class="col-md-6">
          {{> chat}}        
        </div>
        <div class="col-md-3">
          {{> participants}}
        </div>
      </div>
    </div>
</template>

<template name="homepage">
  <div class="container">
    <div class="row">
      <div class="col-md-6 col-md-offset-3">
        <div class="header">
          <a href="/"><h1>Sheriff Messenger</h1></a>

          <h2>Chat. Like a Sheriff.</h2>
        </div>
        <div class="landing">
          <form class="form-inline" role="form">
          <div class="form-group">
            <input type="text" class="form-control" id="channelInput" placeholder="username" maxlength="20" />
            <button type="button" id="channelButton" class="btn btn-default">enter</button>
          </div>
        </form>
        <div class="channels">
          <ul class="pager">
            {{#each channels}}
              <li><a href="/c/{{ name }}">{{ name }} - {{ participants.length }} <span class="glyphicon glyphicon-user"></span></a></li>
            {{/each}}
          </ul>

        </div>

        </div>

      </div>
    </div>

  </div>
</template>


<template name="presentation">
  <a href="/"><h1>Sheriff Messenger</h1></a>
</template>

<template name="participants">
  <h3>Participants</h3>
  <ul>
    {{#each participants}}
      <li style="list-style-type: none;"><div class="color-swatch" style="background-color:{{labelClass}};"></div> {{ profile.name }}</li>
    {{/each}}
  </ul>
  <p>Edit your name : </p>
  <input type="text" class="form-control" id="nameInput" placeholder="{{name}}" maxlength="40" />

</template>

<template name="chat">
  <div class="chat">
    <div class="conversation commentArea" id="scroll">
      {{#each messages }}
        {{#if message}}
          <div class="{{authorCss author}}">
            {{breaklines message }}<br/>
            <i class="author">{{ name }}</i>
          </div>
        {{else}}
          <div class="bubbledLeft">
            <i class="author">Sheriff Messenger</i>
          </div>
        {{/if}}
        <script> if (typeof(scrolldown) == "function") { scrolldown(); }</script>
      {{/each}}
    </div>
    <div class="conversationInput"> 
      <input type="text" id="messageInput" class="form-control text" placeholder="Message..." maxlength="300">
    </div>
  </div>


  <script type="text/javascript">
    /* Call it everytime ? ....
    around 10 times at the first call...
     */
    var scrolldown = function() {
      console.log("one scrolldown call..."); // reminder to debug...
      var elem = document.getElementById('scroll');
      elem.scrollTop = elem.scrollHeight;
    }
  </script>

</template>

Javascript:

var MeteorUser = function () {
  var userId = Meteor.userId();
  if (!userId) {
    return null;
  }
  var user = Meteor.users.findOne(userId);
  /* if (user !== undefined &&
      user.profile !== undefined &&
      user.profile.guest) {
    return null;
  } */
  return user;  
};



////////// Helpers for in-place editing //////////

// Returns an event map that handles the "escape" and "return" keys and
// "blur" events on a text input (given by selector) and interprets them
// as "ok" or "cancel".
var okCancelEvents = function (selector, callbacks) {
  var ok = callbacks.ok || function () {};
  var cancel = callbacks.cancel || function () {};

  var events = {};
  events['keyup '+selector+', keydown '+selector+', focusout '+selector] =
    function (evt) {
      if (evt.type === "keydown" && evt.which === 27) {
        // escape = cancel
        cancel.call(this, evt);

      } else if (evt.type === "keyup" && evt.which === 13 ||
                 evt.type === "focusout") {
        // blur/return/enter = ok/submit if non-empty
        var value = String(evt.target.value || "");
        if (value)
          ok.call(this, value, evt);
        else
          cancel.call(this, evt);
      }
    };

  return events;
};

var activateInput = function (input) {
  input.focus();
  input.select();
};

UI.registerHelper('breaklines', function(text){ // Should call a fonction to sanitaze the html...
  var html = "";
  if(text) { 
    html = text.replace(/(\r\n|\n|\r)/gm, '<br>');
  }
  return Spacebars.SafeString(html);
});


// Not the right way to do it ?!!!
UI.registerHelper('authorCss', function(author){
  var cssClass = "bubbledLeft";
  if(author === Meteor.userId()) { 
    cssClass = "bubbledRight";
  }
  return cssClass;
});

/////////// End Helper ///////

if (Meteor.isClient) {
  // Create collection on client
  Messages = new Meteor.Collection('messages');
  Channels = new Meteor.Collection('channels');

  Meteor.startup(function() {
      Meteor.loginVisitor(); // Guest Account
      //Meteor.insecureUserLogin('Anonymous'); // Test Account
      // We take car of the name
      Session.setDefault('name', 'Guest');
      Session.setDefault('channel', 'yo');

  });



  //////////// Chat ///////////////
  Template.chat.messages = function () {
    var messagesCursor = Messages.find({}, {sort:{timestamp:-1}, limit:42});
    var messages = messagesCursor.fetch().reverse(); // Should use observechnage to avoid over computation ?

    for (var i = messages.length - 1; i >= 0; i--) {
      var user =  Meteor.users.findOne(messages[i].author);
      if (user) {
        messages[i].name = user.profile.name;
      }
      else {
        messages[i].name = "Unknown";
      }
    };

    var conversations = [];
    var newConversation = messages[0];
    for (var i = 0; i <= messages.length - 2; i++) {
      var timedelta = messages[i+1].timestamp - messages[i].timestamp; 
      var sameauthor = (messages[i+1].author === messages[i].author);
      if (timedelta <= 30000 && sameauthor) {
        newConversation.message = newConversation.message + " \n" + messages[i+1].message;
      }
      else {
        conversations.push(newConversation);
        newConversation = messages[i+1];
      }
    };
    conversations.push(newConversation);
    // title bar alert 
    $.titleAlert("New chat message!", {requireBlur: true});
    return conversations;
  };


  Template.chat.events(okCancelEvents(
      '#messageInput',
      {
        ok: function (value, evt) {
          Messages.insert({
            author: Meteor.userId(),
            message: value,
            timestamp: (new Date()).getTime(),
            channel: Session.get('channel') 
          });
          evt.target.value = "";
        }
      }
  ));
  //////////// End Chat ///////////////


  //////////// Name ///////////////
  Template.participants.name = function () {
    Meteor.users.findOne(userId)
    var user = Meteor.users.findOne(Meteor.userId());
    if (user){
      Session.set('name', user.profile.name);
    }
    return Session.get('name');
  };

  Template.participants.participants = function() {
    var labelClass = function(id) { // Certainly not the right way to do it...
      if (id === Meteor.userId()) {
        return "#428bca";
      }
      var user = Meteor.users.findOne(id);
      if (user) {
        if (user.status.online) {
          return "#5cb85c";
        }
        else {
          return "#f0ad4e";
        }
      }
      else {
        return '#d9534f';
      }

    };

    var participants = Meteor.users.find({}).fetch();
    for (var i = participants.length - 1; i >= 0; i--) {
      participants[i].labelClass = labelClass(participants[i]._id);
    };

    return participants;
  }

  Template.participants.events(okCancelEvents(
    '#nameInput',
    {
      ok: function (value, evt) {
        if (value) {
          var user = Meteor.users.findOne(Meteor.userId());
          if (user){
            Meteor.users.update({_id:Meteor.userId()}, {$set:{"profile.name": value}})
          }
          Session.set('name', value);
        } 
      }
    }));
    //////////// End Name ///////////////


    //////////// Homepage ///////////////
    Template.homepage.events(okCancelEvents(
      '#channelInput',
      {
        ok: function (value, evt) {
          if (value) {
            Session.set('channel', value);
          }
        }
      }));

    Template.homepage.channel = function () {
      return Session.get('channel');
    };

    Template.homepage.channels = function() {
      return Channels.find({}, {limit:42});
    }

    Template.homepage.events({
      'click #channelButton': function (event, template) {
        Router.go('/c/'+Session.get('channel'));
      }
    });



    //////////// END Homepage ///////////


    //////////// Routing ///////////////

  Router.configure({
    layoutTemplate: 'layout'
  });

  Router.map(function () {
    this.route('channel', {
      path: '/c/:channel',
      template: 'channel',
      layoutTemplate: 'layout',
        waitOn: function () {
          Session.set('channel', this.params.channel);
          // Subscribe
          Meteor.subscribe("chatroomMessages", this.params.channel);
          Meteor.subscribe("channels", this.params.channel);
        },
        data: function() {
          var channel = Channels.findOne({name: this.params.channel});
          var participants = [Meteor.userId()]; // default;
          if (channel) {
            var participants  = channel.participants;
          }
          Meteor.subscribe("users", participants);
        }
    });

    this.route('home', {
      path: '/',
      template: 'homepage',
      layoutTemplate: 'layout',
      data: function() {
        Meteor.subscribe("channelslist");
      }
    });
  });

    //////////// END Routing ///////////


}




if (Meteor.isServer) {
  Meteor.startup(function () {
    Messages = new Meteor.Collection('messages');
    Channels = new Meteor.Collection('channels');

    // code to run on server at startup

  });

  Meteor.publish("channelslist", function() {
    return Channels.find({});
  });

  Meteor.publish("channels", function (channel) {
    var id;
    if (Channels.findOne({name:channel}) == null) {
      id = Channels.insert({
        name : channel,
        created_timestamp : (new Date()).getTime(),
        created_author: this.userId,
        participants: [],
        message : 0
      });
    } else {
      id = Channels.findOne({name:channel})._id;
    }

    if (id) {
      Channels.update({_id: id}, {$addToSet: { participants: this.userId}});
    }
    return Channels.find({});
  });



  Meteor.publish("users", function (listUsers) {
    return Meteor.users.find({_id: {$in: listUsers}});
  });


  Meteor.publish("chatroomMessages", function (channel) {
    return Messages.find({channel: channel}, {sort:{timestamp:-1}, limit:42});   
  });


}

以防万一……这里是 CSS:

/* CSS declarations go here */
@media (min-width: 1200px) {
    [class*="span"] {
        margin-left: 20px; /* correction ??? */
    }
}
body {
    background-color: #e7e7e7;
}
.chat {
    height: 100vh; /* What is that ? */
    background-color: #fff;
    -webkit-box-shadow: 0px 3px 5px 0px rgba(0, 0, 0, 0.61);
    -moz-box-shadow:    0px 3px 5px 0px rgba(0, 0, 0, 0.61);
    box-shadow:         0px 3px 5px 0px rgba(0, 0, 0, 0.61);
}
.conversation {
    height: calc(100% - 50px); /* Use preprocessing to get a fixed .conversationInput-height */
    overflow-x:hidden;
    overflow-y: auto;
}
.conversationInput {
    background-color: #ccf;
    height: 50px;
    bottom: 0;
}


/* CSS chat 'stolen' from  http://jsfiddle.net/anuradhabajpai/x8C8S/10/ */

.commentArea {
    font: 14px Arial;
    padding: 0 10px 0px;
}

.bubbledLeft,.bubbledRight {
    margin-top: 10px;
    margin-bottom: 10px;
    padding: 5px 9px;
    max-width: 80%;
    clear: both;
    position: relative;
}

.bubbledLeft{
    float: left;
    margin-right: auto;
    -webkit-border-radius: 8px 8px 8px 0px;
    -moz-border-radius: 8px 8px 8px 0px;
    -o-border-radius: 8px 8px 8px 0px;
    -ms-border-radius: 8px 8px 8px 0px;
    border-radius: 8px 8px 8px 0px;
    background-color: #65B045;
    color: #ffffff;
}

.bubbledLeft:before {
    border-bottom: 10px solid #65B045;
    border-left: 9px solid rgba(0, 0, 0, 0);
    position: absolute;
    bottom: 0;
    left: -8px;
    content: "";
}

.bubbledRight{
    float: right;
    margin-left: auto;
    text-align: right;
    -webkit-border-radius: 8px 8px 0px 8px;
    -moz-border-radius: 8px 8px 0px 8px;
    -o-border-radius: 8px 8px 0px 8px;
    -ms-border-radius: 8px 8px 0px 8px;
    border-radius: 8px 8px 0px 8px;
    background-color: #07D;
    color: white;
}

.bubbledRight:before {
    border-bottom: 9px solid #07D;
    border-right: 9px solid rgba(0, 0, 0, 0);
    position: absolute;
    bottom: 0;
    right: -8px;
    content: "";
}

.author {
    opacity: .5;
    font-size: .9em;
}
/* END CSS Chat */
#messageInput {
    width: 100%;
    padding: 0px;
    padding-left: 16px;
    height: 100%;
    border-bottom: 0;
    border-radius: 0;
}

.color-swatch {
    width: 12px;
    height: 12px;
    display: inline-block;
    border-radius: 50%;
}
body {
    font-family: 'Roboto', sans-serif;
}
.landing {
    padding-top: 150px;
    text-align: center;
}
.channels {
    padding: 10px 10px 10px 10px;
}
h1 {
    text-decoration: none;
    text-decoration-color: none;
    color: #333;
}
.header h1 {
    text-align: center;
} 

这里也是我使用的包的列表:

meteor-platform
autopublish
insecure
mrt:moment
ostrio:user-status
accounts-base
accounts-password
artwells:accounts-guest
mizzao:accounts-testing
fortawesome:fontawesome
iron:router
twbs:bootstrap
meteorhacks:fast-render
peerlibrary:related
standard-app-packages

再次感谢您的帮助!

【问题讨论】:

    标签: javascript html node.js mongodb meteor


    【解决方案1】:

    可能根本不是唯一的原因,但在 var participants = Meteor.users.find({}).fetch(); 中,fetch() 不应该在那里......

    这些行也是如此:

    var messagesCursor = Messages.find({}, {sort:{timestamp:-1}, limit:42});
    var messages = messagesCursor.fetch().reverse();
    

    我也是流星和 mongodb 的新手,但是你的代码中只有两个 fetch() ,而且你都有问题......而且由于我在 mongdb 上没有看到,我发现很有可能这是你的问题。

    编辑:

    另外,您使用Meteor.users.findOne(id),应该是Meteor.users.findOne({_id:id})Meteor.users.findOne(messages[i].author);也一样

    【讨论】:

    • 谢谢!您的解决方案在让参与者出现方面起作用。但是,消息仍然没有发送,您无法看到哪些参与者在线:(
    • 我又指出了 2 个错误,请记住这些错误,请检查您的代码,您现在应该更接近了。还可以尝试使用浏览器的调试工具检查属性,以找出问题所在。
    • 感谢您的帮助!我一直在尝试,但仍然无法让消息正常工作,我会继续寻找,但截至目前,我不知道我的消息发生了什么。
    猜你喜欢
    • 1970-01-01
    • 2016-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-10
    • 1970-01-01
    • 2020-02-10
    • 2016-04-03
    相关资源
    最近更新 更多